Compare commits

..

7 Commits

Author SHA1 Message Date
icex2
87b7e53973 feat: Add core module
This module contains the "core" (API) of
bemanitools which includes an abstraction
layer for threads and logging at this time.

The threads API is very close to what
util/thread already was with some structural
enhancements which make it easier to understand
and work with the API, I hope. Some additional
helpers (*-ext module) support in doing common
tasks, e.g. setting up the thread API with other
modules.

The log(ging) part receives a major overhaul to
address known limitations and issues with the
util/log module:
- Cleaner API layer
- Separate sinks from actual logging engine
- Sinks are composable
- Improved and cleaner compatibility layer
  with AVS logging API

Additional "extensions" (*-ext modules) add
various helper functions for common tasks like
setting up the logging engine with a file and stdout
sink.

The sinks also improved significantly with the file
sink now supporting proper appending and log rotation.
Logging to stdout/stderr supports coloring of log
messages which works across logging engines.

Overall, this refactored foundation is expected to
support future developments and removes known
limitations at the current scale of bemanitools such as:
- Reducing boiler plate code across hooks
- Interop of bemanitools and AVS (and setting the foundation
  for addressing currently missing interop, e.g. for
  dealing with property structures without AVS)
- Addressing performance issues in the logging engine
  due to incorrect interop with AVS
2024-02-25 09:30:53 +01:00
icex2
2e45f095ba feat(avs-util): Add helper to translate property errors 2024-02-25 09:30:53 +01:00
icex2
e12cd63ba2 Fix(avs): Incorrect function signature
After getting doubts, I looked this one up again on the
assembly. The decompiled output confused me
and no actual value is being returned there.
2024-02-25 09:30:47 +01:00
icex2
8804e667b3 feat(avs): Add property get and clear error functions
Use these to improve error handling by allowing
one to provide additional error information on
property related operations.
2024-02-25 09:14:42 +01:00
icex2
f5b8af3f2a feat(dev): Add a separate docker dev container
Improve the development experience by providing
an additional docker container that can be started
and used as an interactive development environment.
It provides all the tools and a stable environment
for building (identical to the build container).
2024-02-25 09:14:42 +01:00
icex2
ab6c3fc8bc fix(hook): Add missing hook_table_revert impl
Allow hooks to cleanup when they are shut down.
2024-02-25 09:07:54 +01:00
icex2
7a56fab96e fix(dist): Incorrect versioning for ddr distribution packages
Apparently forgotten to get updated to reflect the
currently supported versions correctly.
2024-02-25 08:51:25 +01:00
92 changed files with 1776 additions and 6432 deletions

21
Dockerfile.dev Normal file
View File

@@ -0,0 +1,21 @@
FROM --platform=amd64 debian:11.6-slim@sha256:f7d141c1ec6af549958a7a2543365a7829c2cdc4476308ec2e182f8a7c59b519
LABEL description="Development environment for bemanitools"
# mingw-w64-gcc has 32-bit and 64-bit toolchains
RUN apt-get update && apt-get install -y --no-install-recommends \
mingw-w64 \
mingw-w64-common \
make \
zip \
git \
clang-format \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
RUN pip3 install mdformat
RUN mkdir /bemanitools
WORKDIR /bemanitools
ENV SHELL /bin/bash

View File

@@ -13,8 +13,10 @@ BUILDDIR ?= build
builddir_docker := $(BUILDDIR)/docker
docker_container_name := "bemanitools-build"
docker_image_name := "bemanitools-build:latest"
docker_build_container_name := "bemanitools-build"
docker_build_image_name := "bemanitools-build:latest"
docker_dev_container_name := "bemanitools-dev"
docker_dev_image_name := "bemanitools-dev:latest"
depdir := $(BUILDDIR)/dep
objdir := $(BUILDDIR)/obj
@@ -41,6 +43,7 @@ FORCE:
.PHONY: \
build-docker \
dev-docker \
clean \
code-format \
doc-format \
@@ -89,21 +92,38 @@ version:
$(V)echo "$(gitrev)" > version
build-docker:
$(V)docker rm -f $(docker_container_name) 2> /dev/null || true
$(V)docker rm -f $(docker_build_container_name) 2> /dev/null || true
$(V)docker \
build \
-t $(docker_image_name) \
-f Dockerfile \
-t $(docker_build_image_name) \
-f Dockerfile.build \
.
$(V)docker \
run \
--volume $(shell pwd):/bemanitools \
--name $(docker_container_name) \
$(docker_image_name)
--name $(docker_build_container_name) \
$(docker_build_image_name)
dev-docker:
$(V)docker rm -f $(docker_dev_container_name) 2> /dev/null || true
$(V)docker \
build \
-t $(docker_dev_image_name) \
-f Dockerfile.dev \
.
$(V)docker \
run \
--interactive \
--tty \
--volume $(shell pwd):/bemanitools \
--name $(docker_dev_container_name) \
$(docker_dev_image_name)
clean-docker:
$(V)docker rm -f $(docker_container_name) || true
$(V)docker image rm -f $(docker_image_name) || true
$(V)docker rm -f $(docker_dev_container_name) || true
$(V)docker image rm -f $(docker_dev_image_name) || true
$(V)docker rm -f $(docker_build_container_name) || true
$(V)docker image rm -f $(docker_build_image_name) || true
$(V)rm -rf $(BUILDDIR)
#

View File

@@ -100,6 +100,7 @@ include src/main/bstio/Module.mk
include src/main/camhook/Module.mk
include src/main/cconfig/Module.mk
include src/main/config/Module.mk
include src/main/core/Module.mk
include src/main/d3d9-util/Module.mk
include src/main/d3d9exhook/Module.mk
include src/main/ddrhook-util/Module.mk
@@ -135,7 +136,6 @@ include src/main/ezusb2-popn-shim/Module.mk
include src/main/ezusb2-tool/Module.mk
include src/main/ezusb2/Module.mk
include src/main/geninput/Module.mk
include src/main/hdr-histogram/Module.mk
include src/main/hook/Module.mk
include src/main/hooklib/Module.mk
include src/main/iidx-bio2-exit-hook/Module.mk
@@ -155,11 +155,9 @@ include src/main/iidxhook6/Module.mk
include src/main/iidxhook7/Module.mk
include src/main/iidxhook8/Module.mk
include src/main/iidxhook9/Module.mk
include src/main/iidxio-async/Module.mk
include src/main/iidxio-bio2/Module.mk
include src/main/iidxio-ezusb/Module.mk
include src/main/iidxio-ezusb2/Module.mk
include src/main/iidxio-perf/Module.mk
include src/main/iidxio/Module.mk
include src/main/iidxiotest/Module.mk
include src/main/inject/Module.mk
@@ -459,11 +457,9 @@ $(zipdir)/iidx-27-to-30.zip: \
$(zipdir)/iidx-hwio-x86.zip: \
build/bin/indep-32/aciomgr.dll \
build/bin/indep-32/eamio-icca.dll \
build/bin/indep-32/iidxio-async.dll \
build/bin/indep-32/iidxio-bio2.dll \
build/bin/indep-32/iidxio-ezusb.dll \
build/bin/indep-32/iidxio-ezusb2.dll \
build/bin/indep-32/iidxio-perf.dll \
dist/iidx/iidxio-bio2.conf \
build/bin/indep-32/vigem-iidxio.exe \
dist/iidx/vigem-iidxio.conf \
@@ -474,11 +470,9 @@ $(zipdir)/iidx-hwio-x86.zip: \
$(zipdir)/iidx-hwio-x64.zip: \
build/bin/indep-64/aciomgr.dll \
build/bin/indep-64/eamio-icca.dll \
build/bin/indep-64/iidxio-async.dll \
build/bin/indep-64/iidxio-bio2.dll \
build/bin/indep-64/iidxio-ezusb.dll \
build/bin/indep-64/iidxio-ezusb2.dll \
build/bin/indep-64/iidxio-perf.dll \
dist/iidx/iidxio-bio2.conf \
build/bin/indep-64/vigem-iidxio.exe \
dist/iidx/vigem-iidxio.conf \
@@ -717,6 +711,8 @@ $(zipdir)/ddr-14-to-18.zip: \
build/bin/indep-32/eamio.dll \
build/bin/indep-32/geninput.dll \
dist/ddr/config.bat \
dist/ddr/gamestart-17.bat \
dist/ddr/gamestart-18.bat \
dist/ddr/gamestart-14.bat \
dist/ddr/gamestart-15.bat \
dist/ddr/gamestart-16.bat \
@@ -735,6 +731,8 @@ $(zipdir)/ddr-16-to-18-x64.zip: \
build/bin/indep-64/eamio.dll \
build/bin/indep-64/geninput.dll \
dist/ddr/config.bat \
dist/ddr/gamestart-17.bat \
dist/ddr/gamestart-18.bat \
dist/ddr/gamestart-16.bat \
dist/ddr/gamestart-17.bat \
dist/ddr/gamestart-18.bat \

View File

@@ -220,6 +220,9 @@ void property_file_write(struct property *prop, const char *path);
int property_set_flag(struct property *prop, int flags, int mask);
void property_destroy(struct property *prop);
avs_error property_get_error(struct property *prop);
void property_clear_error(struct property *prop);
int property_psmap_import(
struct property *prop,
struct property_node *root,

View File

@@ -25,6 +25,8 @@ EXPORTS
property_destroy
property_file_write
property_insert_read
property_clear_error
property_get_error
property_mem_write
property_read_query_memsize
property_search

View File

@@ -28,6 +28,8 @@ EXPORTS
property_destroy
property_file_write
property_insert_read
property_clear_error
property_get_error
property_mem_write
property_read_query_memsize
property_search

View File

@@ -26,6 +26,8 @@ EXPORTS
property_desc_to_buffer @246 NONAME
property_destroy @247 NONAME
property_insert_read @255 NONAME
property_clear_error @573 NONAME
property_get_error @573 NONAME
property_node_create @266 NONAME
property_node_datasize @267 NONAME
property_node_name @274 NONAME

View File

@@ -25,6 +25,8 @@ EXPORTS
property_desc_to_buffer @201 NONAME
property_destroy @264 NONAME
property_insert_read @23 NONAME
property_clear_error @573 NONAME
property_get_error @573 NONAME
property_node_create @316 NONAME
property_node_datasize @249 NONAME
property_node_name @255 NONAME

View File

@@ -25,6 +25,8 @@ EXPORTS
property_desc_to_buffer @201 NONAME == XC058ba50000cd
property_destroy @264 NONAME == XC058ba500010f
property_insert_read @23 NONAME == XC058ba5000016
property_clear_error @573 NONAME
property_get_error @573 NONAME
property_node_create @316 NONAME == XC058ba5000143
property_node_datasize @249 NONAME == XC058ba5000100
property_node_name @255 NONAME == XC058ba5000106

View File

@@ -24,6 +24,8 @@ EXPORTS
property_desc_to_buffer @131 NONAME
property_destroy @130 NONAME
property_insert_read @133 NONAME
property_clear_error @573 NONAME
property_get_error @573 NONAME
property_node_name @573 NONAME ==
property_node_read @573 NONAME ==
property_node_remove @148 NONAME

View File

@@ -26,6 +26,8 @@ EXPORTS
property_desc_to_buffer @129 NONAME
property_destroy @128 NONAME
property_insert_read @131 NONAME
property_clear_error @573 NONAME
property_get_error @573 NONAME
property_node_create @145 NONAME
property_node_name @150 NONAME
property_node_read @154 NONAME == XCd229cc0000f3

View File

@@ -19,6 +19,8 @@ EXPORTS
property_destroy @125 NONAME
property_desc_to_buffer @126 NONAME
property_insert_read @128 NONAME
property_clear_error @573 NONAME
property_get_error @573 NONAME
property_search @141 NONAME
property_node_create @142 NONAME
property_node_name @147 NONAME == XCnbrep7000092

View File

@@ -19,6 +19,8 @@ EXPORTS
property_destroy @146 NONAME
property_desc_to_buffer @147 NONAME
property_insert_read @149 NONAME
property_clear_error @158 NONAME == XCnbrep700009d
property_get_error @159 NONAME == XCnbrep700009e
property_search @162 NONAME
property_node_create @163 NONAME
property_node_name @168 NONAME == XCnbrep70000a7

View File

@@ -21,6 +21,8 @@ EXPORTS
property_destroy @146 NONAME
property_desc_to_buffer @147 NONAME
property_insert_read @149 NONAME
property_clear_error @158 NONAME == XCgsqzn000009d
property_get_error @159 NONAME == XCgsqzn000009e
property_search @162 NONAME
property_node_create @163 NONAME
property_node_name @168 NONAME == XCgsqzn00000a7

View File

@@ -25,6 +25,8 @@ EXPORTS
property_destroy
property_file_write
property_insert_read
property_clear_error
property_get_error
property_mem_write
property_read_query_memsize
property_search

View File

@@ -26,6 +26,8 @@ EXPORTS
property_desc_to_buffer @129 NONAME
property_destroy @128 NONAME
property_insert_read @131 NONAME
property_clear_error @573 NONAME
property_get_error @573 NONAME
property_node_create @145 NONAME
property_node_name @150 NONAME
property_node_read @154 NONAME == XCd229cc0000f3

View File

@@ -26,6 +26,8 @@ EXPORTS
property_desc_to_buffer @129 NONAME
property_destroy @128 NONAME
property_insert_read @131 NONAME
property_clear_error @573 NONAME
property_get_error @573 NONAME
property_node_create @145 NONAME
property_node_name @573 NONAME ==
property_node_read @573 NONAME ==

View File

@@ -19,6 +19,8 @@ EXPORTS
property_destroy @125 NONAME
property_desc_to_buffer @126 NONAME
property_insert_read @128 NONAME
property_clear_error @573 NONAME
property_get_error @573 NONAME
property_search @141 NONAME
property_node_create @142 NONAME
property_node_name @147 NONAME == XCnbrep7000092

View File

@@ -19,12 +19,14 @@ EXPORTS
property_destroy @146 NONAME
property_desc_to_buffer @147 NONAME
property_insert_read @149 NONAME
property_clear_error @158 NONAME == XCnbrep700009d
property_get_error @159 NONAME == XCnbrep700009e
property_search @162 NONAME
property_node_create @163 NONAME
property_node_name @168 NONAME == XCnbrep70000a7
property_node_remove @164 NONAME
property_node_type @169 NONAME == XCnbrep70000a8
property_node_clone @165 NONAME
property_node_clone @165 NONAME == XCnbrep70000a4
property_node_traversal @167 NONAME
property_node_refdata @166 NONAME == XCnbrep70000a5
property_node_datasize @171 NONAME == XCnbrep70000aa

View File

@@ -21,6 +21,8 @@ EXPORTS
property_destroy @146 NONAME
property_desc_to_buffer @147 NONAME
property_insert_read @149 NONAME
property_clear_error @158 NONAME == XCgsqzn000009d
property_get_error @159 NONAME == XCgsqzn000009e
property_search @162 NONAME
property_node_create @163 NONAME
property_node_name @168 NONAME == XCgsqzn00000a7

View File

@@ -96,4 +96,14 @@ const char *avs_util_error_str(avs_error error)
}
return avs_util_error_unknown;
}
const char *avs_util_property_error_get_and_clear(struct property *prop)
{
avs_error error;
error = property_get_error(prop);
property_clear_error(prop);
return avs_util_error_str(error);
}

View File

@@ -5,4 +5,6 @@
const char *avs_util_error_str(avs_error error);
#endif
const char *avs_util_property_error_get_and_clear(struct property *prop);
#endif

20
src/main/core/Module.mk Normal file
View File

@@ -0,0 +1,20 @@
libs += core
libs_core := \
util \
src_core := \
log-bt-ext.c \
log-bt.c \
log-sink-async.c \
log-sink-debug.c \
log-sink-file.c \
log-sink-list.c \
log-sink-mutex.c \
log-sink-null.c \
log-sink-std.c \
log.c \
thread-crt-ext.c \
thread-crt.c \
thread.c \

View File

@@ -0,0 +1,67 @@
#include <stdbool.h>
#include "core/log-bt.h"
#include "core/log-sink-debug.h"
#include "core/log-sink-file.h"
#include "core/log-sink-list.h"
#include "core/log-sink-mutex.h"
#include "core/log-sink-std.h"
#include "core/log.h"
void core_log_bt_ext_impl_set()
{
core_log_impl_set(
core_log_bt_log_misc,
core_log_bt_log_info,
core_log_bt_log_warning,
core_log_bt_log_fatal);
}
void core_log_bt_ext_init_with_stdout()
{
struct core_log_sink sink;
core_log_sink_std_out_open(true, &sink);
core_log_bt_init(&sink);
}
void core_log_bt_ext_init_with_stderr()
{
struct core_log_sink sink;
core_log_sink_std_err_open(true, &sink);
core_log_bt_init(&sink);
}
void core_log_bt_ext_init_with_debug()
{
struct core_log_sink sink;
core_log_sink_debug_open(&sink);
core_log_bt_init(&sink);
}
void core_log_bt_ext_init_with_file(
const char *path, bool append, bool rotate, uint8_t max_rotations)
{
struct core_log_sink sink;
core_log_sink_file_open(path, append, rotate, max_rotations, &sink);
core_log_bt_init(&sink);
}
void core_log_bt_ext_init_with_stdout_and_file(
const char *path, bool append, bool rotate, uint8_t max_rotations)
{
struct core_log_sink sinks[2];
struct core_log_sink sink_composed;
struct core_log_sink sink_mutex;
core_log_sink_std_out_open(true, &sinks[0]);
core_log_sink_file_open(path, append, rotate, max_rotations, &sinks[1]);
core_log_sink_list_open(sinks, 2, &sink_composed);
core_log_sink_mutex_open(&sink_composed, &sink_mutex);
core_log_bt_init(&sink_mutex);
}

View File

@@ -0,0 +1,59 @@
#ifndef CORE_LOG_BT_EXT_H
#define CORE_LOG_BT_EXT_H
#include <stdbool.h>
#include <stdint.h>
/**
* Set the current thread API implementation to use the bemanitools log
* implementation
*/
void core_log_bt_ext_impl_set();
/**
* Helper to setup the bemanitools log implementation with a stdout sink.
*/
void core_log_bt_ext_init_with_stdout();
/**
* Helper to setup the bemanitools log implementation with a stderr sink.
*/
void core_log_bt_ext_init_with_stderr();
/**
* Helper to setup the bemanitools log implementation with a OutputDebugStr
* sink.
*/
void core_log_bt_ext_init_with_debug();
/**
* Helper to setup the bemanitools log implementation with a file sink
*
* @param path Path to the log file to write the log output to
* @param append If true, then append to an existing file, false to overwrite
* any existing file
* @param rotate If true, rotates an existing log file and creates a new one
* for this session
* @param max_rotations Max number of rotations for the log files
*/
void core_log_bt_ext_init_with_file(
const char *path, bool append, bool rotate, uint8_t max_rotations);
/**
* Helper to setup the bemanitools log implementation with a stdout and file
* sink
*
* Important: This combined sink is guarded by a mutex to avoid data races on
* logging to two different sinks.
*
* @param path Path to the log file to write the log output to
* @param append If true, then append to an existing file, false to overwrite
* any existing file
* @param rotate If true, rotates an existing log file and creates a new one
* for this session
* @param max_rotations Max number of rotations for the log files
*/
void core_log_bt_ext_init_with_stdout_and_file(
const char *path, bool append, bool rotate, uint8_t max_rotations);
#endif

129
src/main/core/log-bt.c Normal file
View File

@@ -0,0 +1,129 @@
#include <stdarg.h>
#include <stdlib.h>
#include <time.h>
#include "core/log-bt.h"
#include "core/log-sink.h"
#include "core/log.h"
#include "util/mem.h"
#include "util/str.h"
static enum core_log_bt_log_level _core_log_bt_log_level;
static struct core_log_sink *_core_log_bt_sink;
static void _core_log_bt_vformat_write(
enum core_log_bt_log_level level,
const char *module,
const char *fmt,
va_list ap)
{
static const char chars[] = "FFWIM";
char timestamp[64];
/* 64k so we can log data dumps of rs232 without crashing */
char msg[65536];
char line[65536];
int result;
time_t curtime;
struct tm *tm;
curtime = 0;
tm = NULL;
curtime = time(NULL);
tm = localtime(&curtime);
strftime(timestamp, sizeof(timestamp), "[%Y/%m/%d %H:%M:%S]", tm);
str_vformat(msg, sizeof(msg), fmt, ap);
result = str_format(
line,
sizeof(line),
"%s %c:%s: %s\n",
timestamp,
chars[level],
module,
msg);
_core_log_bt_sink->write(_core_log_bt_sink->ctx, line, result);
}
void core_log_bt_init(const struct core_log_sink *sink)
{
if (sink == NULL) {
abort();
}
_core_log_bt_sink = xmalloc(sizeof(struct core_log_sink));
memcpy(_core_log_bt_sink, sink, sizeof(struct core_log_sink));
_core_log_bt_log_level = CORE_LOG_BT_LOG_LEVEL_OFF;
}
void core_log_bt_level_set(enum core_log_bt_log_level level)
{
_core_log_bt_log_level = level;
}
void core_log_bt_fini()
{
log_assert(_core_log_bt_sink);
_core_log_bt_sink->close(_core_log_bt_sink->ctx);
free(_core_log_bt_sink);
}
void core_log_bt_log_fatal(const char *module, const char *fmt, ...)
{
va_list ap;
if (_core_log_bt_log_level >= CORE_LOG_BT_LOG_LEVEL_FATAL) {
va_start(ap, fmt);
_core_log_bt_vformat_write(
CORE_LOG_BT_LOG_LEVEL_FATAL, module, fmt, ap);
va_end(ap);
}
}
void core_log_bt_log_warning(const char *module, const char *fmt, ...)
{
va_list ap;
if (_core_log_bt_log_level >= CORE_LOG_BT_LOG_LEVEL_WARNING) {
va_start(ap, fmt);
_core_log_bt_vformat_write(
CORE_LOG_BT_LOG_LEVEL_WARNING, module, fmt, ap);
va_end(ap);
}
}
void core_log_bt_log_info(const char *module, const char *fmt, ...)
{
va_list ap;
if (_core_log_bt_log_level >= CORE_LOG_BT_LOG_LEVEL_INFO) {
va_start(ap, fmt);
_core_log_bt_vformat_write(CORE_LOG_BT_LOG_LEVEL_INFO, module, fmt, ap);
va_end(ap);
}
}
void core_log_bt_log_misc(const char *module, const char *fmt, ...)
{
va_list ap;
if (_core_log_bt_log_level >= CORE_LOG_BT_LOG_LEVEL_MISC) {
va_start(ap, fmt);
_core_log_bt_vformat_write(CORE_LOG_BT_LOG_LEVEL_MISC, module, fmt, ap);
va_end(ap);
}
}
void core_log_bt_direct_sink_write(const char *chars, size_t nchars)
{
_core_log_bt_sink->write(_core_log_bt_sink->ctx, chars, nchars);
}

87
src/main/core/log-bt.h Normal file
View File

@@ -0,0 +1,87 @@
#ifndef CORE_LOG_BT_H
#define CORE_LOG_BT_H
#include "core/log-sink.h"
/**
* Log API implementation for games/applications without AVS
*/
enum core_log_bt_log_level {
CORE_LOG_BT_LOG_LEVEL_OFF = 0,
CORE_LOG_BT_LOG_LEVEL_FATAL = 1,
CORE_LOG_BT_LOG_LEVEL_WARNING = 2,
CORE_LOG_BT_LOG_LEVEL_INFO = 3,
CORE_LOG_BT_LOG_LEVEL_MISC = 4,
};
/**
* Initialize the logging backend
*
* This must be called as early as possible in your application to setup
* a logging sink according to your needs. Until this is finished, no
* log output is available.
*
* By default, logging is turned off entirely and must be enabled by setting
* a desired logging level explicitly.
*
* @param sink Pointer to a log sink implementation. The caller owns the memory
* of this.
*/
void core_log_bt_init(const struct core_log_sink *sink);
/**
* Set the current logging level. This can be changed at any given time, e.g.
* to increase/decrease verbosity.
*
* @param level The logging level to set.
*/
void core_log_bt_level_set(enum core_log_bt_log_level level);
/**
* Cleanup the logging backend.
*
* Ensure to call this on application exit and cleanup.
*/
void core_log_bt_fini();
/**
* Implementation of the log API.
*/
void core_log_bt_log_fatal(const char *module, const char *fmt, ...);
/**
* Implementation of the log API.
*/
void core_log_bt_log_warning(const char *module, const char *fmt, ...);
/**
* Implementation of the log API.
*/
void core_log_bt_log_info(const char *module, const char *fmt, ...);
/**
* Implementation of the log API.
*/
void core_log_bt_log_misc(const char *module, const char *fmt, ...);
/**
* Allow AVS to by-pass the core log API/engine.
*
* This function must only be called by AVS in an appropriate log callback
* function that is passed to avs_boot.
*
* AVS has it's own logging engine and manages aspects such as async logging,
* log levels and decorating log messages.
*
* Thus, proper interoperability only requires the writer/sink part to be shared
* with AVS.
*
* @param chars Buffer with text data to write to the configured sinks. The
* buffer might contain several log messages separated by newline
* characters.
* @param nchars Number of chars to write to the sink.
*/
void core_log_bt_direct_sink_write(const char *chars, size_t nchars);
#endif

View File

@@ -0,0 +1,23 @@
#include <stdlib.h>
#include "core/log-sink.h"
static void
_core_log_sink_file_write(void *ctx, const char *chars, size_t nchars)
{
// TODO
}
static void _core_log_sink_file_close(void *ctx)
{
// TODO
}
void core_log_sink_async_open(struct core_log_sink *sink)
{
// TODO
sink->ctx = NULL;
sink->write = _core_log_sink_file_write;
sink->close = _core_log_sink_file_close;
}

View File

@@ -0,0 +1,19 @@
#ifndef CORE_LOG_SINK_ASYNC_H
#define CORE_LOG_SINK_ASYNC_H
#include <stdint.h>
#include <stdlib.h>
#include "core/log-sink.h"
/**
* Open a async log sink
*
* The sink passes data to log to a separate thread which executes the actual
* logging of the data.
*
* @param sink Pointer to allocated memory that receives the opened sink
*/
void core_log_sink_async_open(struct core_log_sink *sink);
#endif

View File

@@ -0,0 +1,23 @@
#include <debugapi.h>
#include <stdlib.h>
#include "core/log-sink.h"
static void
_core_log_sink_debug_write(void *ctx, const char *chars, size_t nchars)
{
OutputDebugStringA(chars);
}
static void _core_log_sink_debug_close(void *ctx)
{
// noop
}
void core_log_sink_debug_open(struct core_log_sink *sink)
{
sink->ctx = NULL;
sink->write = _core_log_sink_debug_write;
sink->close = _core_log_sink_debug_close;
}

View File

@@ -0,0 +1,15 @@
#ifndef CORE_LOG_SINK_DEBUG_H
#define CORE_LOG_SINK_DEBUG_H
#include <stdlib.h>
#include "core/log-sink.h"
/**
* Open a log sink that uses OutputDebugStr
*
* @param sink Pointer to allocated memory that receives the opened sink
*/
void core_log_sink_debug_open(struct core_log_sink *sink);
#endif

View File

@@ -0,0 +1,92 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "core/log-sink.h"
#include "util/fs.h"
#include "util/str.h"
static void _core_log_sink_file_rotate(const char *path, uint8_t max_rotations)
{
uint8_t i;
char rotate_file[MAX_PATH];
char rotate_file_next[MAX_PATH];
char version[8];
char version_next[8];
for (i = max_rotations; i > 0; i++) {
str_cpy(rotate_file, sizeof(rotate_file), path);
str_cpy(rotate_file_next, sizeof(rotate_file_next), path);
if (i - 1 != 0) {
sprintf(version, ".%d", i);
} else {
memset(version, 0, sizeof(version));
}
sprintf(version_next, ".%d", i);
str_cat(rotate_file, sizeof(rotate_file), version);
str_cat(rotate_file_next, sizeof(rotate_file_next), version_next);
if (path_exists(rotate_file)) {
CopyFile(rotate_file, rotate_file_next, FALSE);
}
}
}
static void
_core_log_sink_file_write(void *ctx, const char *chars, size_t nchars)
{
FILE *file;
file = (FILE *) ctx;
fwrite(chars, 1, nchars, file);
}
static void _core_log_sink_file_close(void *ctx)
{
FILE *file;
file = (FILE *) ctx;
fflush(file);
fclose(file);
}
void core_log_sink_file_open(
const char *path,
bool append,
bool rotate,
uint8_t max_rotations,
struct core_log_sink *sink)
{
FILE *file;
if (rotate) {
_core_log_sink_file_rotate(path, max_rotations);
// Appending doesn't matter when file is rotated anyway
file = fopen(path, "w+");
} else {
if (append) {
file = fopen(path, "a+");
} else {
file = fopen(path, "w+");
}
}
if (!file) {
printf("Cannot open log file: %s", path);
abort();
}
sink->ctx = (void *) file;
sink->write = _core_log_sink_file_write;
sink->close = _core_log_sink_file_close;
}

View File

@@ -0,0 +1,28 @@
#ifndef CORE_LOG_SINK_FILE_H
#define CORE_LOG_SINK_FILE_H
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "core/log-sink.h"
/**
* Open a log sink writing data to a file
*
* @param path Path to the log file to write the log output to
* @param append If true, then append to an existing file, false to overwrite
* any existing file
* @param rotate If true, rotates an existing log file and creates a new one
* for this session
* @param max_rotations Max number of rotations for the log files
* @param sink Pointer to allocated memory that receives the opened sink
*/
void core_log_sink_file_open(
const char *path,
bool append,
bool rotate,
uint8_t max_rotations,
struct core_log_sink *sink);
#endif

View File

@@ -0,0 +1,66 @@
#include <stdint.h>
#include <stdlib.h>
#include "core/log-sink-list.h"
#include "core/log-sink.h"
#include "util/mem.h"
#define MAX_SINKS 8
struct core_log_sink_list {
struct core_log_sink entries[MAX_SINKS];
uint8_t num;
};
static void
_core_log_sink_list_write(void *ctx, const char *chars, size_t nchars)
{
struct core_log_sink_list *sink_list;
int i;
sink_list = (struct core_log_sink_list *) ctx;
for (i = 0; i < sink_list->num; i++) {
sink_list->entries[i].write(sink_list->entries[i].ctx, chars, nchars);
}
}
static void _core_log_sink_list_close(void *ctx)
{
struct core_log_sink_list *sink_list;
int i;
sink_list = (struct core_log_sink_list *) ctx;
for (i = 0; i < sink_list->num; i++) {
sink_list->entries[i].close(sink_list->entries[i].ctx);
}
free(sink_list);
}
void core_log_sink_list_open(
const struct core_log_sink *entry, uint8_t num, struct core_log_sink *sink)
{
struct core_log_sink_list *sink_list;
int i;
if (num > MAX_SINKS) {
abort();
}
sink_list = xmalloc(sizeof(struct core_log_sink_list));
for (i = 0; i < num; i++) {
sink_list->entries[i].ctx = entry[i].ctx;
sink_list->entries[i].write = entry[i].write;
sink_list->entries[i].close = entry[i].close;
}
sink_list->num = num;
sink->ctx = (void *) sink_list;
sink->write = _core_log_sink_list_write;
sink->close = _core_log_sink_list_close;
}

View File

@@ -0,0 +1,24 @@
#ifndef CORE_LOG_SINK_LIST_H
#define CORE_LOG_SINK_LIST_H
#include <stdint.h>
#include <stdlib.h>
#include "core/log-sink.h"
/**
* Combine multiple log sinks into a list of sinks.
*
* Upon invoking a list sink, all sinks contained within the list are
* being invoked in the configured order.
*
* @param entry A pointer to allocated memory with a sequence of opened sinks
* that you want to add to the list. Ownership of these sinks
* is transferred, i.e. closing the list sink closes its children.
* @param num The number of elements in the sequence of opened sinks pointed to.
* @param sink Pointer to allocated memory that receives the opened sink
*/
void core_log_sink_list_open(
const struct core_log_sink *entry, uint8_t num, struct core_log_sink *sink);
#endif

View File

@@ -0,0 +1,53 @@
#include <windows.h>
#include <stdlib.h>
#include "core/log-sink.h"
#include "util/mem.h"
struct core_log_sink_mutex_ctx {
struct core_log_sink *child;
HANDLE mutex;
};
static void
_core_log_sink_mutex_write(void *ctx_, const char *chars, size_t nchars)
{
struct core_log_sink_mutex_ctx *ctx;
ctx = (struct core_log_sink_mutex_ctx *) ctx_;
WaitForSingleObject(ctx->mutex, INFINITE);
ctx->child->write(ctx->child->ctx, chars, nchars);
ReleaseMutex(ctx->mutex);
}
static void _core_log_sink_mutex_close(void *ctx_)
{
struct core_log_sink_mutex_ctx *ctx;
ctx = (struct core_log_sink_mutex_ctx *) ctx_;
CloseHandle(ctx->mutex);
ctx->child->close(ctx->child->ctx);
free(ctx);
}
void core_log_sink_mutex_open(
const struct core_log_sink *child_sink, struct core_log_sink *sink)
{
struct core_log_sink_mutex_ctx *ctx;
ctx = xmalloc(sizeof(struct core_log_sink_mutex_ctx));
memcpy(ctx->child, child_sink, sizeof(struct core_log_sink));
ctx->mutex = CreateMutex(NULL, FALSE, NULL);
sink->ctx = ctx;
sink->write = _core_log_sink_mutex_write;
sink->close = _core_log_sink_mutex_close;
}

View File

@@ -0,0 +1,21 @@
#ifndef CORE_LOG_SINK_MUTEX_H
#define CORE_LOG_SINK_MUTEX_H
#include <stdlib.h>
#include "core/log-sink.h"
/**
* Create a sink that surrounds another sink with a mutex.
*
* Use this to make other sink implementations thread-safe.
*
* @param child_sink Another opened sink to surround with the mutex. Ownership
* of the sink is transferred, i.e. closing the mutex sink
* also closes the wrapped child sink.
* @param sink Pointer to allocated memory that receives the opened sink
*/
void core_log_sink_mutex_open(
const struct core_log_sink *child_sink, struct core_log_sink *sink);
#endif

View File

@@ -0,0 +1,21 @@
#include <stdlib.h>
#include "core/log-sink.h"
static void
_core_log_sink_null_write(void *ctx, const char *chars, size_t nchars)
{
// noop
}
static void _core_log_sink_null_close(void *ctx)
{
// noop
}
void core_log_sink_null_open(struct core_log_sink *sink)
{
sink->ctx = NULL;
sink->write = _core_log_sink_null_write;
sink->close = _core_log_sink_null_close;
}

View File

@@ -0,0 +1,17 @@
#ifndef CORE_LOG_SINK_NULL_H
#define CORE_LOG_SINK_NULL_H
#include <stdlib.h>
#include "core/log-sink.h"
/**
* Create a null/dummy sink.
*
* Use this to disable any logging entirely.
*
* @param sink Pointer to allocated memory that receives the opened sink
*/
void core_log_sink_null_open(struct core_log_sink *sink);
#endif

View File

@@ -0,0 +1,193 @@
#include <windows.h>
#include <stdlib.h>
#include "core/log-sink.h"
#include "util/mem.h"
struct core_log_sink_std_ctx {
HANDLE handle;
bool color;
};
static char _core_log_sink_std_determine_color(const char *str)
{
/* Add some color to make spotting warnings/errors easier.
Based on debug output level identifier. */
/* Avoids colored output on strings like "Windows" */
if (str[1] != ':') {
return 15;
}
switch (str[0]) {
/* green */
case 'M':
return 10;
/* blue */
case 'I':
return 9;
/* yellow */
case 'W':
return 14;
/* red */
case 'F':
return 12;
/* default console color */
default:
return 15;
}
}
static size_t _core_log_sink_std_msg_coloring_len(const char *str)
{
// Expected format example: "I:boot: my log message"
const char *ptr;
size_t len;
int colon_count;
ptr = str;
len = 0;
colon_count = 0;
while (true) {
// End of string = invalid log format
if (*ptr == '\0') {
return 0;
}
if (*ptr == ':') {
colon_count++;
}
if (colon_count == 2) {
// Skip current colon, next char is a space
return len + 1;
}
len++;
ptr++;
}
return 0;
}
static void
_core_log_sink_std_write(void *ctx_, const char *chars, size_t nchars)
{
static const size_t timestamp_len = strlen("[----/--/-- --:--:--]");
struct core_log_sink_std_ctx *ctx;
char color;
size_t color_len;
size_t msg_len;
const char *msg_start;
const char *msg_end;
DWORD written;
DWORD write_pos;
ctx = (struct core_log_sink_std_ctx *) ctx_;
if (ctx->color) {
write_pos = 0;
// Support multiple buffered log messages, e.g. from the AVS logging
// engine
while (write_pos < nchars) {
// Expects the AVS timestamp format
msg_start = chars + timestamp_len + 1; // +1 is the space
color_len = _core_log_sink_std_msg_coloring_len(msg_start);
// Check if we could detect which part to color, otherwise just
// write the whole log message without any coloring logic
if (color_len > 0) {
color = _core_log_sink_std_determine_color(msg_start);
// Timestamp
WriteConsole(
ctx->handle, chars, timestamp_len + 1, &written, NULL);
write_pos += written;
chars += written;
// Log level + module colored
SetConsoleTextAttribute(ctx->handle, color);
WriteConsole(ctx->handle, chars, color_len, &written, NULL);
write_pos += written;
chars += written;
SetConsoleTextAttribute(ctx->handle, 15);
msg_end = strchr(chars, '\n');
if (msg_end != NULL) {
msg_len = msg_end - chars;
// Write \n as well
msg_len++;
// Write actual message non colored
WriteConsole(ctx->handle, chars, msg_len, &written, NULL);
write_pos += written;
chars += written;
} else {
WriteConsole(
ctx->handle, chars, nchars - write_pos, &written, NULL);
write_pos += written;
chars += written;
}
} else {
WriteConsole(
ctx->handle,
chars + write_pos,
nchars - write_pos,
&written,
NULL);
write_pos += written;
}
}
} else {
WriteConsole(ctx->handle, chars, nchars, &written, NULL);
}
}
static void _core_log_sink_std_close(void *ctx_)
{
struct core_log_sink_std_ctx *ctx;
ctx = (struct core_log_sink_std_ctx *) ctx_;
// Remark: Don't close the ctx->handle, see win API docs
free(ctx);
}
void core_log_sink_std_out_open(bool color, struct core_log_sink *sink)
{
struct core_log_sink_std_ctx *ctx;
ctx = xmalloc(sizeof(struct core_log_sink_std_ctx));
ctx->handle = GetStdHandle(STD_OUTPUT_HANDLE);
ctx->color = color;
sink->ctx = (void *) ctx;
sink->write = _core_log_sink_std_write;
sink->close = _core_log_sink_std_close;
}
void core_log_sink_std_err_open(bool color, struct core_log_sink *sink)
{
struct core_log_sink_std_ctx *ctx;
ctx = xmalloc(sizeof(struct core_log_sink_std_ctx));
ctx->handle = GetStdHandle(STD_ERROR_HANDLE);
ctx->color = color;
sink->ctx = (void *) ctx;
sink->write = _core_log_sink_std_write;
sink->close = _core_log_sink_std_close;
}

View File

@@ -0,0 +1,24 @@
#ifndef CORE_LOG_SINK_STD_H
#define CORE_LOG_SINK_STD_H
#include <stdlib.h>
#include "core/log-sink.h"
/**
* Create a sink that writes to stdout.
*
* @param color If true, messages are colored by log level.
* @param sink Pointer to allocated memory that receives the opened sink
*/
void core_log_sink_std_out_open(bool color, struct core_log_sink *sink);
/**
* Create a sink that writes to stderr.
*
* @param color If true, messages are colored by log level.
* @param sink Pointer to allocated memory that receives the opened sink
*/
void core_log_sink_std_err_open(bool color, struct core_log_sink *sink);
#endif

45
src/main/core/log-sink.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef CORE_LOG_SINK_H
#define CORE_LOG_SINK_H
#include <stdint.h>
/**
* Write function for a log sink implementation.
*
* Write the given data to your target output destination.
*
* @param ctx Context defined by the implementation when opening the sink.
* @param chars Buffer with text data to log. This can contain partial data of
* a single log line, a full log line terminated by a newline
* character or multiple log lines (each terminated by a newline
* character).
* @param nchars Number of characters to write.
*/
typedef void (*core_log_sink_write_t)(
void *ctx, const char *chars, size_t nchars);
/**
* Close your log sink and cleanup resources
*
* Depending on your implementation, you might want to flush any
* outstanding/buffered data.
*
* @param ctx Context defined by the implementation when opening the sink.
*/
typedef void (*core_log_sink_close_t)(void *ctx);
/**
* Log sink structure.
*
* This must be set-up and populated when opening your log sink implementation.
* The ctx field contains any arbitrary data that you need for your log sink
* to operate, e.g. a file handle, additional buffers etc. Make sure these
* resources are cleaned up upon closing the sink.
*/
struct core_log_sink {
void *ctx;
core_log_sink_write_t write;
core_log_sink_close_t close;
};
#endif

74
src/main/core/log.c Normal file
View File

@@ -0,0 +1,74 @@
#include <stdlib.h>
#include "core/log.h"
core_log_message_t _core_log_misc_impl;
core_log_message_t _core_log_info_impl;
core_log_message_t _core_log_warning_impl;
core_log_message_t _core_log_fatal_impl;
void core_log_impl_set(
core_log_message_t misc,
core_log_message_t info,
core_log_message_t warning,
core_log_message_t fatal)
{
if (misc == NULL || info == NULL || warning == NULL || fatal == NULL) {
abort();
}
_core_log_misc_impl = misc;
_core_log_info_impl = info;
_core_log_warning_impl = warning;
_core_log_fatal_impl = fatal;
}
void core_log_impl_assign(core_log_impl_set_t impl_set)
{
if (_core_log_misc_impl == NULL || _core_log_info_impl == NULL ||
_core_log_warning_impl == NULL || _core_log_fatal_impl == NULL) {
abort();
}
impl_set(
_core_log_misc_impl,
_core_log_info_impl,
_core_log_warning_impl,
_core_log_fatal_impl);
}
core_log_message_t core_log_misc_impl_get()
{
if (_core_log_misc_impl == NULL) {
abort();
}
return _core_log_misc_impl;
}
core_log_message_t core_log_info_impl_get()
{
if (_core_log_info_impl == NULL) {
abort();
}
return _core_log_info_impl;
}
core_log_message_t core_log_warning_impl_get()
{
if (_core_log_warning_impl == NULL) {
abort();
}
return _core_log_warning_impl;
}
core_log_message_t core_log_fatal_impl_get()
{
if (_core_log_fatal_impl == NULL) {
abort();
}
return _core_log_fatal_impl;
}

197
src/main/core/log.h Normal file
View File

@@ -0,0 +1,197 @@
#ifndef CORE_LOG_H
#define CORE_LOG_H
#include <stddef.h>
#include <stdlib.h>
#include "util/defs.h"
/**
* The core log API of bemanitools.
*
* To a large extent, this reflects the AVS logging API and allows for swapping
* out the backends with different implementations. Most games should have some
* version of the AVS API available while some (legacy) games do not. These
* can use a bemanitools private logging implementation by configuring it
* in the bootstrapping process.
*/
/* BUILD_MODULE is passed in as a command-line #define by the makefile */
#ifndef LOG_MODULE
#define LOG_MODULE STRINGIFY(BUILD_MODULE)
#endif
/**
* Log a message on misc level
*
* Always use this interface in your application which hides the currently
* configured implementation.
*
* The macro is required to make things work with varargs.
* The log message is only printed if the log level is set to misc
*
* @param fmt printf format string
* @param ... Additional arguments according to the specified arguments in the
* printf format string
*/
#define log_misc(...) _core_log_misc_impl(LOG_MODULE, __VA_ARGS__)
/**
* Log a message on info level
*
* Always use this interface in your application which hides the currently
* configured implementation.
*
* The macro is required to make things work with varargs.
* The log message is only printed if the log level is set to info or lower
*
* @param fmt printf format string
* @param ... Additional arguments according to the specified arguments in the
* printf format string
*/
#define log_info(...) _core_log_info_impl(LOG_MODULE, __VA_ARGS__)
/**
* Log a message on warning level
*
* Always use this interface in your application which hides the currently
* configured implementation.
*
* The macro is required to make things work with varargs.
* The log message is only printed if the log level is set to warning or lower
*
* @param fmt printf format string
* @param ... Additional arguments according to the specified arguments in the
* printf format string
*/
#define log_warning(...) _core_log_warning_impl(LOG_MODULE, __VA_ARGS__)
/**
* Log a message on fatal level
*
* Always use this interface in your application which hides the currently
* configured implementation.
*
* The macro is required to make things work with varargs.
* The log message is only printed if the log level is set to fatal.
*
* This call will also terminate the application.
*
* @param fmt printf format string
* @param ... Additional arguments according to the specified arguments in the
* printf format string
*/
#define log_fatal(...) \
do { \
_core_log_fatal_impl(LOG_MODULE, __VA_ARGS__); \
abort(); \
} while (0)
/**
* Log a message and terminate the application if given condition fails
*
* Always use this interface in your application which hides the currently
* configured implementation.
*
* The macro is required to make things work with varargs.
*
* @param x Condition to evaluate. If false, the application terminates
*/
#define log_assert(x) \
do { \
if (!(x)) { \
_core_log_fatal_impl( \
"assert", \
"%s:%d: function `%s'", \
__FILE__, \
__LINE__, \
__FUNCTION__); \
abort(); \
} \
} while (0)
/**
* Log a message in an exception handler
*
* Only use this function in an exception handler, e.g. for stack traces. It
* logs the message on fatal level but does not terminate.
*
* @param fmt printf format string
* @param ... Additional arguments according to the specified arguments in the
* printf format string
*/
#define log_exception_handler(...) \
_core_log_fatal_impl("exception", __VA_ARGS__)
typedef void (*core_log_message_t)(const char *module, const char *fmt, ...);
typedef void (*core_log_impl_set_t)(
core_log_message_t misc,
core_log_message_t info,
core_log_message_t warning,
core_log_message_t fatal);
/**
* Configure the log API implementations
*
* Advised to do this as early in your application/library module as possible
* as calls to the getter functions below will return the currently configured
* implementations.
*
* @param misc Pointer to a function implementing logging on misc level
* @param info Pointer to a function implementing logging on info level
* @param warning Pointer to a function implementing logging on warning level
* @param fatal Pointer to a function implementing logging on fatal level
*/
void core_log_impl_set(
core_log_message_t misc,
core_log_message_t info,
core_log_message_t warning,
core_log_message_t fatal);
/**
* Supporting function to inject/assign the currently set implementation
* with the given setter function.
*
* @param impl_set Setter function to call with the currently configured log
* function implementations
*/
void core_log_impl_assign(core_log_impl_set_t impl_set);
/**
* Get the currently configured implementation of the misc level log function
*
* @return Pointer to the currently configured implementation of the function
*/
core_log_message_t core_log_misc_impl_get();
/**
* Get the currently configured implementation of the info level log function
*
* @return Pointer to the currently configured implementation of the function
*/
core_log_message_t core_log_info_impl_get();
/**
* Get the currently configured implementation of the warning level log function
*
* @return Pointer to the currently configured implementation of the function
*/
core_log_message_t core_log_warning_impl_get();
/**
* Get the currently configured implementation of the fatal level log function
*
* @return Pointer to the currently configured implementation of the function
*/
core_log_message_t core_log_fatal_impl_get();
// Do not use these directly.
// These are only here to allow usage in the macros above.
extern core_log_message_t _core_log_misc_impl;
extern core_log_message_t _core_log_info_impl;
extern core_log_message_t _core_log_warning_impl;
extern core_log_message_t _core_log_fatal_impl;
#endif

View File

@@ -0,0 +1,8 @@
#include "core/thread-crt.h"
#include "core/thread.h"
void core_thread_crt_ext_impl_set()
{
core_thread_impl_set(
core_thread_crt_create, core_thread_crt_join, core_thread_crt_destroy);
}

View File

@@ -0,0 +1,9 @@
#ifndef CORE_THREAD_CRT_EXT_H
#define CORE_THREAD_CRT_EXT_H
/**
* Set the current thread API implementation to use the C runtime thread API
*/
void core_thread_crt_ext_impl_set();
#endif

View File

@@ -0,0 +1,62 @@
#include <process.h>
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
#include "core/thread-crt.h"
#include "core/thread.h"
#include "util/defs.h"
struct shim_ctx {
HANDLE barrier;
int (*proc)(void *);
void *ctx;
};
static unsigned int STDCALL crt_thread_shim(void *outer_ctx)
{
struct shim_ctx *sctx = outer_ctx;
int (*proc)(void *);
void *inner_ctx;
proc = sctx->proc;
inner_ctx = sctx->ctx;
SetEvent(sctx->barrier);
return proc(inner_ctx);
}
int core_thread_crt_create(
int (*proc)(void *), void *ctx, uint32_t stack_sz, unsigned int priority)
{
struct shim_ctx sctx;
uintptr_t thread_id;
sctx.barrier = CreateEvent(NULL, TRUE, FALSE, NULL);
sctx.proc = proc;
sctx.ctx = ctx;
thread_id = _beginthreadex(NULL, stack_sz, crt_thread_shim, &sctx, 0, NULL);
WaitForSingleObject(sctx.barrier, INFINITE);
CloseHandle(sctx.barrier);
return (int) thread_id;
}
void core_thread_crt_destroy(int thread_id)
{
CloseHandle((HANDLE) (uintptr_t) thread_id);
}
void core_thread_crt_join(int thread_id, int *result)
{
WaitForSingleObject((HANDLE) (uintptr_t) thread_id, INFINITE);
if (result) {
GetExitCodeThread((HANDLE) (uintptr_t) thread_id, (DWORD *) result);
}
}

View File

@@ -0,0 +1,15 @@
#ifndef CORE_THREAD_CRT_H
#define CORE_THREAD_CRT_H
#include <stdint.h>
/**
* Thread API implementation using the C runtime API
*/
int core_thread_crt_create(
int (*proc)(void *), void *ctx, uint32_t stack_sz, unsigned int priority);
void core_thread_crt_join(int thread_id, int *result);
void core_thread_crt_destroy(int thread_id);
#endif

78
src/main/core/thread.c Normal file
View File

@@ -0,0 +1,78 @@
#include <stdlib.h>
#include "core/log.h"
#include "core/thread.h"
core_thread_create_t core_thread_create_impl;
core_thread_join_t core_thread_join_impl;
core_thread_destroy_t core_thread_destroy_impl;
int core_thread_create(
int (*proc)(void *), void *ctx, uint32_t stack_sz, unsigned int priority)
{
log_assert(core_thread_create_impl);
return core_thread_create_impl(proc, ctx, stack_sz, priority);
}
void core_thread_join(int thread_id, int *result)
{
log_assert(core_thread_join_impl);
core_thread_join_impl(thread_id, result);
}
void core_thread_destroy(int thread_id)
{
log_assert(core_thread_destroy_impl);
core_thread_destroy_impl(thread_id);
}
void core_thread_impl_set(
core_thread_create_t create,
core_thread_join_t join,
core_thread_destroy_t destroy)
{
if (create == NULL || join == NULL || destroy == NULL) {
abort();
}
core_thread_create_impl = create;
core_thread_join_impl = join;
core_thread_destroy_impl = destroy;
}
void core_thread_impl_assign(core_thread_impl_set_t impl_set)
{
if (core_thread_create_impl == NULL || core_thread_join_impl == NULL ||
core_thread_destroy_impl == NULL) {
abort();
}
impl_set(
core_thread_create_impl,
core_thread_join_impl,
core_thread_destroy_impl);
}
core_thread_create_t core_thread_create_impl_get()
{
log_assert(core_thread_create_impl);
return core_thread_create_impl;
}
core_thread_join_t core_thread_join_impl_get()
{
log_assert(core_thread_join_impl);
return core_thread_join_impl;
}
core_thread_destroy_t core_thread_destroy_impl_get()
{
log_assert(core_thread_destroy_impl);
return core_thread_destroy_impl;
}

117
src/main/core/thread.h Normal file
View File

@@ -0,0 +1,117 @@
#ifndef CORE_THREAD_H
#define CORE_THREAD_H
#include <stdint.h>
/**
* The core thread API of bemanitools.
*
* This essentially reflects the AVS threading API and allows for swapping out
* the backends with different implementations. Most games should have some
* version of the AVS API available while some (legacy) games do not. These
* can use a bemanitools private threading implementation by configuring it
* in the bootstrapping process.
*/
/**
* Create a thread
*
* Always use this interface in your application which hides the currently
* configured implementation.
*
* @param proc The function to run in a separate thread
* @param ctx Additional data to pass to the function as a parameter
* @param stack_sz The stack size to allocate for the thread in bytes
* @param priority The thread's priority
* @return The ID of the thread once created and started
*/
int core_thread_create(
int (*proc)(void *), void *ctx, uint32_t stack_sz, unsigned int priority);
/**
* Wait for a thread to finish
*
* Always use this interface in your application which hides the currently
* configured implementation.
*
* The caller of this function blocks until the thread has finished executing.
*
* @param thread_id ID of the thread to wait for
* @param result Pointer to a variable to write the return value of the function
* the thread executed to
*/
void core_thread_join(int thread_id, int *result);
/**
* Destroy a thread
*
* Always use this interface in your application which hides the currently
* configured implementation.
*
* The thread must have finished execution before calling this. It is advised
* to make threads terminate their execution flow, join them and destroy.
*
* @param thread_id The ID of the thread to destroy.
*/
void core_thread_destroy(int thread_id);
typedef int (*core_thread_create_t)(
int (*proc)(void *), void *ctx, uint32_t stack_sz, unsigned int priority);
typedef void (*core_thread_join_t)(int thread_id, int *result);
typedef void (*core_thread_destroy_t)(int thread_id);
typedef void (*core_thread_impl_set_t)(
core_thread_create_t create,
core_thread_join_t join,
core_thread_destroy_t destroy);
/**
* Configure the thread API implementations
*
* Advised to do this as early in your application/library module as possible
* as calls to the getter functions below will return the currently configured
* implementations.
*
* @param create Pointer to a function implementing thread creation
* @param join Pointer to a function implementing joining of a thread
* @param destroy Pointer to a function implementing destroying of a thread
*/
void core_thread_impl_set(
core_thread_create_t create,
core_thread_join_t join,
core_thread_destroy_t destroy);
/**
* Supporting function to inject/assign the currently set implementation
* with the given setter function.
*
* @param impl_set Setter function to call with the currently configured thread
* function implementations
*/
void core_thread_impl_assign(core_thread_impl_set_t impl_set);
/**
* Get the currently configured implementation for thread_create
*
* @return Pointer to the currently configured implementation of the
* thread_create function
*/
core_thread_create_t core_thread_create_impl_get();
/**
* Get the currently configured implementation for thread_join
*
* @return Pointer to the currently configured implementation of the thread_join
* function
*/
core_thread_join_t core_thread_join_impl_get();
/**
* Get the currently configured implementation for thread_destroy
*
* @return Pointer to the currently configured implementation of the
* thread_destroy function
*/
core_thread_destroy_t core_thread_destroy_impl_get();
#endif

View File

@@ -1,3 +0,0 @@
// 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.

View File

@@ -1,10 +0,0 @@
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 \

View File

@@ -1,146 +0,0 @@
/**
* 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__ */

View File

@@ -1,322 +0,0 @@
/**
* 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;
}

View File

@@ -1,79 +0,0 @@
/**
* 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 */

View File

@@ -1,116 +0,0 @@
/**
* 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

File diff suppressed because it is too large Load Diff

View File

@@ -1,516 +0,0 @@
/**
* 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

File diff suppressed because it is too large Load Diff

View File

@@ -1,236 +0,0 @@
/**
* 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

View File

@@ -1,171 +0,0 @@
/**
* 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;
}

View File

@@ -1,12 +0,0 @@
/**
* 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

View File

@@ -1,227 +0,0 @@
/**
* 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, &params[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, &params[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, &params[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, &params[0]);
return params[3];
}

View File

@@ -1,109 +0,0 @@
/**
* 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

View File

@@ -1,19 +0,0 @@
/**
* 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

View File

@@ -1,22 +0,0 @@
#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

View File

@@ -1,108 +0,0 @@
/**
* 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

View File

@@ -1,55 +0,0 @@
/**
* 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

View File

@@ -1,107 +0,0 @@
/**
* 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;
}

View File

@@ -1,50 +0,0 @@
/**
* 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

View File

@@ -1,142 +0,0 @@
/**
* 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);
}

View File

@@ -1,51 +0,0 @@
/**
* 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

View File

@@ -15,12 +15,21 @@ static const size_t apiset_prefix_len = sizeof(apiset_prefix) - 1;
static void hook_table_apply_to_all(
const char *depname, const struct hook_symbol *syms, size_t nsyms);
static void hook_table_revert_to_all(
const char *depname, const struct hook_symbol *syms, size_t nsyms);
static void hook_table_apply_to_iid(
HMODULE target,
const pe_iid_t *iid,
const struct hook_symbol *syms,
size_t nsyms);
static void hook_table_revert_to_iid(
HMODULE target,
const pe_iid_t *iid,
const struct hook_symbol *syms,
size_t nsyms);
static bool hook_table_match_module(
HMODULE target, const char *iid_name, const char *depname);
@@ -44,6 +53,23 @@ static void hook_table_apply_to_all(
}
}
static void hook_table_revert_to_all(
const char *depname, const struct hook_symbol *syms, size_t nsyms)
{
const peb_dll_t *dll;
HMODULE pe;
for (dll = peb_dll_get_first(); dll != NULL; dll = peb_dll_get_next(dll)) {
pe = peb_dll_get_base(dll);
if (pe == NULL) {
continue; /* ?? Happens sometimes. */
}
hook_table_revert(pe, depname, syms, nsyms);
}
}
void hook_table_apply(
HMODULE target,
const char *depname,
@@ -73,6 +99,35 @@ void hook_table_apply(
}
}
void hook_table_revert(
HMODULE target,
const char *depname,
const struct hook_symbol *syms,
size_t nsyms)
{
const pe_iid_t *iid;
const char *iid_name;
assert(depname != NULL);
assert(syms != NULL || nsyms == 0);
if (target == NULL) {
/* Call out, which will then call us back repeatedly. Awkward, but
viewed from the outside it's good for usability. */
hook_table_revert_to_all(depname, syms, nsyms);
} else {
for (iid = pe_iid_get_first(target); iid != NULL;
iid = pe_iid_get_next(target, iid)) {
iid_name = pe_iid_get_name(target, iid);
if (hook_table_match_module(target, iid_name, depname)) {
hook_table_revert_to_iid(target, iid, syms, nsyms);
}
}
}
}
static void hook_table_apply_to_iid(
HMODULE target,
const pe_iid_t *iid,
@@ -101,6 +156,33 @@ static void hook_table_apply_to_iid(
}
}
static void hook_table_revert_to_iid(
HMODULE target,
const pe_iid_t *iid,
const struct hook_symbol *syms,
size_t nsyms)
{
struct pe_iat_entry iate;
size_t i;
size_t j;
const struct hook_symbol *sym;
i = 0;
while (pe_iid_get_iat_entry(target, iid, i++, &iate) == S_OK) {
for (j = 0; j < nsyms; j++) {
sym = &syms[j];
if (hook_table_match_proc(&iate, sym)) {
// Only revert-able if the original pointer was stored previously
if (sym->link != NULL && *sym->link != NULL) {
pe_patch(iate.ppointer, sym->link, sizeof(*sym->link));
}
}
}
}
}
static bool hook_table_match_module(
HMODULE target, const char *iid_name, const char *depname)
{

View File

@@ -6,4 +6,3 @@ libs_iidxhook-d3d9 := \
src_iidxhook-d3d9 := \
bb-scale-hd.c \
frame-pace.c \

View File

@@ -1,194 +0,0 @@
#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);
}
}

View File

@@ -1,13 +0,0 @@
#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

View File

@@ -16,6 +16,5 @@ src_iidxhook-util := \
d3d9.c \
eamuse.c \
effector.c \
frame-graph.c \
log-server.c \
settings.c \

View File

@@ -13,7 +13,6 @@
#include "hook/table.h"
#include "iidxhook-util/d3d9.h"
#include "iidxhook-util/frame-graph.h"
#include "iidxhook-util/vertex-shader.h"
#include "util/defs.h"
@@ -233,7 +232,7 @@ iidxhook_util_d3d9_log_create_device_params(struct hook_d3d9_irp *irp)
"hDeviceWindow %p, Windowed %d, "
"EnableAutoDepthStencil "
"%d, AutoDepthStencilFormat %d, Flags %lX, "
"FullScreen_RefreshRateInHz %d, PresentationInterval %d",
"FullScreen_RefreshRateInHz %d",
irp->args.ctx_create_device.pp->BackBufferWidth,
irp->args.ctx_create_device.pp->BackBufferHeight,
irp->args.ctx_create_device.pp->BackBufferFormat,
@@ -245,8 +244,7 @@ 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->AutoDepthStencilFormat,
irp->args.ctx_create_device.pp->Flags,
irp->args.ctx_create_device.pp->FullScreen_RefreshRateInHz,
irp->args.ctx_create_device.pp->PresentationInterval);
irp->args.ctx_create_device.pp->FullScreen_RefreshRateInHz);
}
static void
@@ -269,9 +267,6 @@ static void iidxhook_util_d3d9_fix_create_device_apply_window_mode(
log_assert(irp->op == HOOK_D3D9_IRP_OP_CTX_CREATE_DEVICE);
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) {
pp->Windowed = TRUE;
pp->FullScreen_RefreshRateInHz = 0;
@@ -1195,14 +1190,7 @@ iidxhook_util_d3d9_irp_handler(struct hook_d3d9_irp *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:
iidxhook_util_d3d9_scale_render_target_to_back_buffer(irp);
iidxhook_util_d3d9_set_back_buffer_rt(irp);

View File

@@ -1,374 +0,0 @@
#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);
}

View File

@@ -1,10 +0,0 @@
#ifndef IIDXHOOK_FRAME_GRAPH_H
#define IIDXHOOK_FRAME_GRAPH_H
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
void DrawFrameGraph(IDirect3DDevice9* device);
#endif

View File

@@ -116,8 +116,6 @@ static int log_thread_proc(void *ctx)
static void
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)) {
return;
}

View File

@@ -261,8 +261,7 @@ skip:
BOOL WINAPI DllMain(HMODULE mod, DWORD reason, void *ctx)
{
if (reason == DLL_PROCESS_ATTACH) {
// TODO switched to null writer to see if it addresses performance issues
log_to_writer(log_writer_null, NULL);
log_to_writer(log_writer_debug, NULL);
/* Bootstrap hook for further init tasks (see above) */

View File

@@ -254,8 +254,7 @@ skip:
BOOL WINAPI DllMain(HMODULE mod, DWORD reason, void *ctx)
{
if (reason == DLL_PROCESS_ATTACH) {
// TODO switched to null writer to see if it addresses performance issues
log_to_writer(log_writer_null, NULL);
log_to_writer(log_writer_debug, NULL);
/* Bootstrap hook for further init tasks (see above) */

View File

@@ -6,7 +6,6 @@ ldflags_iidxhook3 := \
libs_iidxhook3 := \
iidxhook-util \
iidxhook-d3d9 \
ezusb-emu \
ezusb-iidx-16seg-emu \
ezusb2-emu \

View File

@@ -27,8 +27,6 @@
#include "hooklib/rs232.h"
#include "hooklib/setupapi.h"
#include "iidxhook-d3d9/frame-pace.h"
#include "iidxhook-util/acio.h"
#include "iidxhook-util/chart-patch.h"
#include "iidxhook-util/clock.h"
@@ -54,9 +52,6 @@
static const hook_d3d9_irp_handler_t iidxhook_d3d9_handlers[] = {
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);
@@ -74,8 +69,6 @@ iidxhook3_setup_d3d9_hooks(const struct iidxhook_config_gfx *config_gfx)
{
struct iidxhook_util_d3d9_config d3d9_config;
iidxhook_d3d9_frame_pace_init(GetCurrentThreadId(), 60.0);
iidxhook_util_d3d9_init_config(&d3d9_config);
d3d9_config.windowed = config_gfx->windowed;

View File

@@ -1,9 +0,0 @@
dlls += iidxio-async
ldflags_iidxio-async := \
libs_iidxio-async := \
util \
src_iidxio-async := \
iidxio.c \

View File

@@ -1,18 +0,0 @@
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

View File

@@ -1,414 +0,0 @@
#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;
}

View File

@@ -1,11 +0,0 @@
dlls += iidxio-perf
ldflags_iidxio-perf := \
-lws2_32
libs_iidxio-perf := \
hdr-histogram \
util \
src_iidxio-perf := \
iidxio.c \

View File

@@ -1,18 +0,0 @@
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

View File

@@ -1,306 +0,0 @@
#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;
}