eventlog-socket 0.1.2.0 → 0.1.3.0
raw patch · 12 files changed
+693/−244 lines, 12 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ GHC.Eventlog.Socket: data Hook
+ GHC.Eventlog.Socket: instance GHC.Classes.Eq GHC.Eventlog.Socket.Hook
+ GHC.Eventlog.Socket: instance GHC.Internal.Foreign.Storable.Storable GHC.Eventlog.Socket.Hook
+ GHC.Eventlog.Socket: instance GHC.Internal.Show.Show GHC.Eventlog.Socket.Hook
+ GHC.Eventlog.Socket: pattern HookPostStartEventLogging :: Hook
+ GHC.Eventlog.Socket: pattern HookPreEndEventLogging :: Hook
+ GHC.Eventlog.Socket: registerHook :: Hook -> HookHandler -> IO ()
+ GHC.Eventlog.Socket: type HookHandler = IO ()
Files
- CHANGELOG.md +19/−0
- README.md +86/−111
- cbits/eventlog_socket.c +17/−0
- cbits/eventlog_socket/control.c +55/−15
- cbits/eventlog_socket/hooks.c +149/−0
- cbits/eventlog_socket/hooks.h +17/−0
- cbits/eventlog_socket/hsapi.c +84/−0
- cbits/eventlog_socket/hsapi.h +48/−0
- cbits/eventlog_socket/worker.c +24/−3
- eventlog-socket.cabal +8/−2
- include/eventlog_socket.h +43/−0
- src/GHC/Eventlog/Socket.hsc +143/−113
CHANGELOG.md view
@@ -1,5 +1,24 @@ # Revision history for eventlog-socket +## 0.1.3.0 -- 2026-03-27++- Add support for hooks that fire at certain points in the `eventlog-socket` lifecycle.++ This adds `registerHook` and `eventlog_socket_register_hook` to the Haskell and C APIs, respectively, as well as two kinds of hooks:+ - A _post-startEventLogging_ hook, which is triggered whenever event logging is started.+ - A _pre-endEventLogging_ hook, which is triggered whenever event logging is ended.++ These hooks are intended to give package writers the ability to add their own init events, which are resent event time that a new client connects. For instance, the following code registers a post-startEventLogging hook that greets every new client with a user message event:++ ```haskell+ registerHook HookPostStartEventLogging $+ traceEventIO "Hello, new user!"+ ```++ To avoid races, it is important that these hooks are registered *before* `eventlog-socket` is started.++- Add support for builtin `startProfiling` and `stopProfiling` control commands.+ ## 0.1.2.0 -- 2026-03-25 **Warning**: The C API exposed from this version contains breaking changes over the C API exposed from version 0.1.1.0. This is justified by the fact that the C API exposed from version 0.1.1.0 is broken and that version is deprecated and not known to be in use.
README.md view
@@ -59,7 +59,7 @@ The `eventlog-socket` package installs a C header file, `eventlog_socket.h`, which enables you to instrument your application from a C main function. There is one reason you may want to instrument your application from C, which is that you want to capture *all* of the GHC eventlog. If your application is instrumented from Haskell, it loses the first few events. See the note [above](#default-writer). -For an example of an application instrumented from a custom C main, see [`examples/fibber-c-main`](https://github.com/well-typed/eventlog-socket/tree/eventlog-socket-0.1.2.0/examples/fibber-c-main/).+For an example of an application instrumented from a custom C main, see [`examples/fibber-c-main`](https://github.com/well-typed/eventlog-socket/tree/eventlog-socket-0.1.3.0/examples/fibber-c-main/). ### Configure your application to enable the eventlog @@ -81,19 +81,70 @@ ./my-app +RTS -l -RTS ``` +## Lifecycle Hooks++Every time a new client connects, the GHC RTS resends the eventlog header and initialisation events.+If you develop a package that communicates over the eventlog, you may find that your package *also* needs to resent certain initialisation events.+The `eventlog-socket` package provides lifecycle hooks for this purpose.+There are two supported hooks:++1. The *post-startEventLogging* hook triggers *after* each time event logging is started.+2. The *pre-endEventLogging* hook triggers *before* each time event logging is ended.++To register a handler for lifecycle hook, use the `registerHook` function.+For instance, the following snippet registers a hook that sends a user message event every time event logging is started.++```haskell+module Main where++import Data.Foldable (for_)+import Debug.Trace (traceEventIO)+import GHC.Eventlog.Socket (Hook (..), registerHook, start)+import System.Environment (lookupEnv)++main :: IO ()+main = do+ -- Register the greeting hook:+ registerHook HookPostStartEventLogging $+ traceEventIO "Hello, new user!"++ -- Start eventlog-socket on GHC_EVENTLOG_UNIX_PATH, if set:+ maybeUnixPath <- lookupEnv "GHC_EVENTLOG_UNIX_PATH"+ for_ maybeUnixPath $ \unixPath -> do+ putStrLn "Start eventlog-socket on " <> unixPath+ start unixPath++ -- The rest of your application:+ ...+```++> ⚠️+> To avoid races, it is important that lifecycle hooks are registered *before* `eventlog-socket` is started.++> ⚠️+> The post-startEventLogging and pre-endEventLogging lifecycle hooks usually correspond to clients connecting and disconnecting, respectively.+> The only exception is if your application is instrumented from a C main, in which case the *first* post-startEventLogging hook triggers immediately after the GHC RTS is initialised.+> In this scenario, the first client *does not* trigger the post-startEventLogging hook, but subsequent clients *do*.++For an example of an application that registers lifecycle hooks using the Haskell API, see [`examples/fibber`](https://github.com/well-typed/eventlog-socket/tree/eventlog-socket-0.1.3.0/examples/fibber/).++For an example of an application that registers lifecycle hooks using the C API, see [`examples/fibber-c-main`](https://github.com/well-typed/eventlog-socket/tree/eventlog-socket-0.1.3.0/examples/fibber-c-main/).+ ## Control Commands When compiled with the `+control` feature flag, the eventlog socket supports *control commands*. These are messages that can be *written to* the eventlog socket by the client to control the RTS or execute custom control commands. -The `eventlog-socket` package provides three built-in commands:+The `eventlog-socket` package provides five built-in commands: -1. The `startHeapProfiling` control command starts heap profiling.-2. The `stopHeapProfiling` control command stops heap profiling.-3. The `requestHeapCensus` control command requests a single heap profile.+1. The `startProfiling` control command starts cost-centre profiling.+2. The `stopProfiling` control command stops cost-centre profiling.+3. The `startHeapProfiling` control command starts heap profiling.+4. The `stopHeapProfiling` control command stops heap profiling.+5. The `requestHeapCensus` control command requests a single heap profile. The heap profiling commands require that the RTS is initialised with one of the `-h` options. See [RTS options for heap profiling](https://downloads.haskell.org/ghc/latest/docs/users_guide/profiling.html#rts-options-heap-prof). If any heap profiling option is passed, heap profiling is started by default. To avoid this, pass the `--no-automatic-heap-samples` to the RTS *in addition to* the selected `-h` option, e.g., `./my-app +RTS -hT --no-automatic-heap-samples -RTS`. -The [`eventlog-socket-control`](https://github.com/well-typed/eventlog-socket/tree/eventlog-socket-0.1.2.0/eventlog-socket-control/) package provides utilities for creating control command protocol messages to write to the eventlog socket.+The [`eventlog-socket-control`](https://github.com/well-typed/eventlog-socket/tree/eventlog-socket-0.1.3.0/eventlog-socket-control/) package provides utilities for creating control command protocol messages to write to the eventlog socket. If you want to send control commands from a different programming language, see [Control Command Protocol](#control-command-protocol) for the specification of the binary control command protocol. ### Control Command Protocol@@ -111,11 +162,13 @@ command-id = byte; (* commands are assigned numeric IDs *) ``` -The built-in commands live in the `"eventlog-socket"` namespace and are numbered in order starting at 3.+The built-in commands live in the `"eventlog-socket"` namespace and are numbered in order starting at 1. -1. The message for `startHeapProfiling` is `\xF0\x9E\x97\x8C\x00\x15eventlog-socket\x03`.-2. The message for `stopHeapProfiling` is `\xF0\x9E\x97\x8C\x00\x15eventlog-socket\x04`.-3. The message for `requestHeapCensus` is `\xF0\x9E\x97\x8C\x00\x15eventlog-socket\x05`.+1. The message for `startProfiling` is `\xF0\x9E\x97\x8C\x00\x15eventlog-socket\x01`.+2. The message for `stopProfiling` is `\xF0\x9E\x97\x8C\x00\x15eventlog-socket\x02`.+3. The message for `startHeapProfiling` is `\xF0\x9E\x97\x8C\x00\x15eventlog-socket\x03`.+4. The message for `stopHeapProfiling` is `\xF0\x9E\x97\x8C\x00\x15eventlog-socket\x04`.+5. The message for `requestHeapCensus` is `\xF0\x9E\x97\x8C\x00\x15eventlog-socket\x05`. For example, a simple Python client using the [`socket`](https://docs.python.org/3/library/socket.html) library could request a heap census as follows: @@ -135,120 +188,42 @@ 1. Register a namespace. This must be a string of between 1 and 255 bytes. By convention, this should be your Haskell package name. 2. Register each command. This must be a number between 1 and 255. By convention, this should be the first non-zero number that's still available for this namespace. -Commands can be passed some user data at registration time. This is intended to let you defines multiple different commands using the same handler.--For an example of an application instrumented with custom control commands using the Haskell API, see [`examples/custom-command`](https://github.com/well-typed/eventlog-socket/tree/eventlog-socket-0.1.2.0/examples/custom-command/).--For an example of an application instrumented with custom control commands using the C API, see [`examples/custom-command-c-main`](https://github.com/well-typed/eventlog-socket/tree/eventlog-socket-0.1.2.0/examples/custom-command-c-main/).--The following snippet defines the `registerGreeters` function, which registers two commands via the Haskell API:+To register a namespace, use the `registerNamespace` function.+To register a custom command handler, use the `registerCommand` function.+For instance, the following snippet registers two custom commands under the `"greeters"` namespace which greet C and Haskell, respectively. ```haskell-import GHC.Eventlog.Socket+module Main where +import Data.Foldable (for_)+import Debug.Trace (traceEventIO)+import GHC.Eventlog.Socket (Hook (..), registerHook, start)+import System.Environment (lookupEnv)+ greeter :: String -> IO () greeter name = putStrLn $ "Hello, " <> name <> "!" -registerGreeters :: IO ()-registerGreeters = do+main :: IO ()+main = do+ -- Register the custom command handlers: myNamespace <- registerNamespace "greeters" registerCommand myNamespace (CommandId 1) (greeter "C") registerCommand myNamespace (CommandId 2) (greeter "Haskell")-``` -The following snippet defines the `register_greeters` function, which registers two equivalent commands via the C API:--```c-#include <stdio.h>-#include <stdlib.h>-#include <eventlog_socket.h>--static void greeter(const EventlogSocketControlNamespace *const namespace,- const EventlogSocketControlCommandId command_id,- const void *user_data) {- (void) namespace;- (void) command_id;- printf("Hello, %s!", (const char*)user_data);-}--bool register_greeters(void) {- // Use the name of my Haskell package:- const char* const my_namespace = "my-app";-- // Allocate space for the namespace object:- EventlogSocketControlNamespace *namespace = NULL;-- // Try to register the namespace:- const EventlogSocketStatus namespace_status =- eventlog_socket_control_register_namespace(- strlen(my_namespace), my_namespace, &namespace);-- // Handle any errors:- if (namespace_status.ess_status_code != EVENTLOG_SOCKET_OK) {- char *errmsg = eventlog_socket_strerror(namespace_status);- if (errmsg != NULL) {- fprintf(stderr, "ERROR: %s", errmsg);- free(errmsg);- }- return false;- }- // Otherwise, namespace contains a valid object.-- // Use the first available non-zero command ID for greet_c:- const uint8_t greet_c_id = 1;-- // Allocate data for the greet_c command, if needed:- static const char * const greet_c_data = "C";-- // Try to register the greet_c command:- const EventlogSocketStatus greet_c_status =- eventlog_socket_control_register_command(- namespace, greet_c_id, greeter, greet_c_data);-- // Handle any errors:- if (greet_c_status.ess_status_code != EVENTLOG_SOCKET_OK) {- char *errmsg = eventlog_socket_strerror(greet_c_status);- if (errmsg != NULL) {- fprintf(stderr, "ERROR: %s", errmsg);- free(errmsg);- }- return false;- }- // Otherwise, the command is registered.-- // Use the next available command ID for greet_haskell:- const uint8_t greet_haskell_id = 2;-- // Allocate data for the greet_haskell command, if needed:- static const char * const greet_haskell_data = "Haskell";-- // Try to register the greet_haskell command:- const EventlogSocketStatus greet_haskell_status =- eventlog_socket_control_register_command(- namespace, greet_haskell_id, greeter, greet_haskell_data);-- // Handle any errors:- if (greet_haskell_status.ess_status_code != EVENTLOG_SOCKET_OK) {- char *errmsg = eventlog_socket_strerror(greet_haskell_status);- if (errmsg != NULL) {- fprintf(stderr, "ERROR: %s", errmsg);- free(errmsg);- }- return false;- }- // Otherwise, the command is registered.+ -- Start eventlog-socket on GHC_EVENTLOG_UNIX_PATH, if set:+ maybeUnixPath <- lookupEnv "GHC_EVENTLOG_UNIX_PATH"+ for_ maybeUnixPath $ \unixPath -> do+ putStrLn "Start eventlog-socket on " <> unixPath+ start unixPath - return true;-}+ -- The rest of your application:+ ... ``` -## Contributing--The `scripts` directory contains various scripts for contributors to this repository.--The `./scripts/test.sh` script runs the test suite, which is located in [`eventlog-socket-tests`](https://github.com/well-typed/eventlog-socket/tree/eventlog-socket-0.1.2.0/eventlog-socket-tests/).+> ⚠️+> To avoid races, it is important that custom commands are registered *before* `eventlog-socket` is started. -The `./scripts/pre-commit.sh` script runs the pre-commit hooks, which contains various formatters and linters.+For an example of an application instrumented with custom control commands using the Haskell API, see [`examples/custom-command`](https://github.com/well-typed/eventlog-socket/tree/eventlog-socket-0.1.3.0/examples/custom-command/). -The `./scripts/build-doxygen.sh` and `./scripts/build-haddock.sh` scripts build the documentation for the C and Haskell APIs.+For an example of an application instrumented with custom control commands using the C API, see [`examples/custom-command-c-main`](https://github.com/well-typed/eventlog-socket/tree/eventlog-socket-0.1.3.0/examples/custom-command-c-main/).
cbits/eventlog_socket.c view
@@ -22,6 +22,7 @@ #include "./eventlog_socket/debug.h" #include "./eventlog_socket/error.h"+#include "./eventlog_socket/hooks.h" #include "./eventlog_socket/init_state.h" #include "./eventlog_socket/string.h" #include "./eventlog_socket/worker.h"@@ -340,6 +341,14 @@ RETURN_ON_ERROR(STATUS_FROM_PTHREAD(pthread_mutex_lock(&g_mutex))); // If the RTS is not marked as ready... if (!(g_init_state & EVENTLOG_SOCKET_SIG_RTS_READY)) {+ // If EventLogSocketWriter is already attached...+ if (g_init_state & EVENTLOG_SOCKET_SIG_ATTACHED) {+ // ...trigger the post-startEventLogging hooks.+ DEBUG_DEBUG("%s", "Trigger post-startEventLogging hooks.");+ RETURN_ON_ERROR_CLEANUP(+ es_hooks_handle_hook(EVENTLOG_SOCKET_HOOK_POST_START_EVENT_LOGGING),+ pthread_mutex_unlock(&g_mutex));+ } // ...broadcast on the relevant condition and... RETURN_ON_ERROR_CLEANUP( STATUS_FROM_PTHREAD(pthread_cond_broadcast(&g_ghc_rts_ready_cond)),@@ -627,4 +636,12 @@ #else return STATUS_FROM_CODE(EVENTLOG_SOCKET_OK); #endif /* EVENTLOG_SOCKET_FEATURE_CONTROL */+}++/* PUBLIC - see documentation in eventlog_socket.h */+EventlogSocketStatus+eventlog_socket_register_hook(EventlogSocketHook hook,+ EventlogSocketHookHandler *hook_handler,+ const void *hook_data) {+ return es_hooks_register_hook(hook, hook_handler, hook_data); }
cbits/eventlog_socket/control.c view
@@ -7,7 +7,7 @@ /// and `eventlog_socket_control_register_command`. /// @author Wen Kokke /// @author Matthew Pickering-/// @version 0.1.2.0+/// @version 0.1.3.0 /// @date 2025-2026 /// @copyright BSD-3-Clause License. ///@@ -37,6 +37,7 @@ #include "./poll.h" #include "eventlog_socket.h" #include "init_state.h"+#include "rts/prof/CCS.h" // CONTROL_MAGIC should be the UTF-8 encoding of some code point between // U+010000 and U+10FFFF. Let's pick code point U+01E5CC, for Eventlog@@ -77,11 +78,11 @@ /// @brief The name of the builtin namespace. #define BUILTIN_NAMESPACE "eventlog-socket" -/// @brief The ID reserved for the builtin `StartEventLogging` command.-#define BUILTIN_COMMAND_ID_START_EVENT_LOGGING 1+/// @brief The ID of the builtin `StartProfiling` command.+#define BUILTIN_COMMAND_ID_START_PROFILING 1 -/// @brief The ID reserved for the builtin `EndEventLogging` command.-#define BUILTIN_COMMAND_ID_END_EVENT_LOGGING 2+/// @brief The ID of the builtin `StopProfiling` command.+#define BUILTIN_COMMAND_ID_STOP_PROFILING 2 /// @brief The ID of the builtin `StartHeapProfiling` command. #define BUILTIN_COMMAND_ID_START_HEAP_PROFILING 3@@ -96,7 +97,30 @@ * Handlers for Builtin Commands ******************************************************************************/ -/// @brief Handler for "StartHeapProfiling" command (eventlog-socket::0).+/// @brief Handler for "StartProfiling" command (eventlog-socket::1).+static void es_control_start_profiling(+ const EventlogSocketControlNamespace *const namespace,+ const EventlogSocketControlCommandId command_id, const void *user_data) {+ (void)namespace;+ (void)command_id;+ (void)user_data;+ startProfTimer();+ DEBUG_DEBUG("%s", "Started profiling.");+}++/// @brief Handler for "StopProfiling" command (eventlog-socket::2).+static void+es_control_stop_profiling(const EventlogSocketControlNamespace *const namespace,+ const EventlogSocketControlCommandId command_id,+ const void *user_data) {+ (void)namespace;+ (void)command_id;+ (void)user_data;+ stopProfTimer();+ DEBUG_DEBUG("%s", "Stopped profiling.");+}++/// @brief Handler for "StartHeapProfiling" command (eventlog-socket::3). static void es_control_start_heap_profiling( const EventlogSocketControlNamespace *const namespace, const EventlogSocketControlCommandId command_id, const void *user_data) {@@ -107,7 +131,7 @@ DEBUG_DEBUG("%s", "Started heap profiling."); } -/// @brief Handler for "StopHeapProfiling" command (eventlog-socket::1).+/// @brief Handler for "StopHeapProfiling" command (eventlog-socket::4). static void es_control_stop_heap_profiling( const EventlogSocketControlNamespace *const namespace, const EventlogSocketControlCommandId command_id, const void *user_data) {@@ -118,7 +142,7 @@ DEBUG_DEBUG("%s", "Stopped heap profiling."); } -/// @brief Handler for "RequestHeapCensus" command (eventlog-socket::2).+/// @brief Handler for "RequestHeapCensus" command (eventlog-socket::5). static void es_control_request_heap_census( const EventlogSocketControlNamespace *const namespace, const EventlogSocketControlCommandId command_id, const void *user_data) {@@ -193,21 +217,37 @@ .next = NULL, .command_registry = &(EventlogSocketControlCommand){- .command_id = BUILTIN_COMMAND_ID_START_HEAP_PROFILING,- .command_handler = es_control_start_heap_profiling,+ .command_id = BUILTIN_COMMAND_ID_START_PROFILING,+ .command_handler = es_control_start_profiling, .command_data = NULL, .next = &(EventlogSocketControlCommand){- .command_id = BUILTIN_COMMAND_ID_STOP_HEAP_PROFILING,- .command_handler = es_control_stop_heap_profiling,+ .command_id = BUILTIN_COMMAND_ID_STOP_PROFILING,+ .command_handler = es_control_stop_profiling, .command_data = NULL, .next = &(EventlogSocketControlCommand){ .command_id =- BUILTIN_COMMAND_ID_REQUEST_HEAP_CENSUS,- .command_handler = es_control_request_heap_census,+ BUILTIN_COMMAND_ID_START_HEAP_PROFILING,+ .command_handler = es_control_start_heap_profiling, .command_data = NULL,- .next = NULL,+ .next =+ &(EventlogSocketControlCommand){+ .command_id =+ BUILTIN_COMMAND_ID_STOP_HEAP_PROFILING,+ .command_handler =+ es_control_stop_heap_profiling,+ .command_data = NULL,+ .next =+ &(EventlogSocketControlCommand){+ .command_id =+ BUILTIN_COMMAND_ID_REQUEST_HEAP_CENSUS,+ .command_handler =+ es_control_request_heap_census,+ .command_data = NULL,+ .next = NULL,+ },+ }, }, }, },
+ cbits/eventlog_socket/hooks.c view
@@ -0,0 +1,149 @@+#include <string.h>++#include "./error.h"+#include "./hooks.h"+#include "eventlog_socket.h"++/******************************************************************************+ * Hook Handler Registry+ ******************************************************************************/++/// @brief An entry in the hook registry.+typedef struct EventlogSocketHookHandlerEntry EventlogSocketHookHandlerEntry;++/// @brief An entry in the handler registry for one particular hook.+struct EventlogSocketHookHandlerEntry {+ /// @brief The user-provided hook handler.+ EventlogSocketHookHandler *const hook_handler;+ /// @brief The user-provided data for the hook handler.+ const void *hook_data;+ /// @brief The pointer to the next entry in the hook registry.+ EventlogSocketHookHandlerEntry *next;+};++/// @brief The registry for all hooks.+typedef struct {+ EventlogSocketHookHandlerEntry *on_connect_handlers;+ EventlogSocketHookHandlerEntry *on_disconnect_handlers;+} EventlogSocketHookHandlerRegistry;++/// @brief The global hook handler registry.+static EventlogSocketHookHandlerRegistry g_hook_handler_registry = {+ .on_connect_handlers = NULL,+ .on_disconnect_handlers = NULL,+};++/// @brief The mutex that guards the global hook handler registry.+static pthread_mutex_t g_hook_handler_registry_mutex =+ PTHREAD_MUTEX_INITIALIZER;++/* HIDDEN - see documentation in hooks.h */+HIDDEN EventlogSocketStatus es_hooks_register_hook(+ const EventlogSocketHook hook,+ EventlogSocketHookHandler *const hook_handler, const void *hook_data) {+ // Validate the arguments.+ if (hook_handler == NULL) {+ errno = EINVAL;+ return STATUS_FROM_ERRNO();+ }++ // Acquire the `g_hook_handler_registry_mutex`.+ RETURN_ON_ERROR(+ STATUS_FROM_PTHREAD(pthread_mutex_lock(&g_hook_handler_registry_mutex)));++ // Get a pointer to the *first* entry for the given hook.+ EventlogSocketHookHandlerEntry **entry = NULL;+ switch (hook) {+ case EVENTLOG_SOCKET_HOOK_POST_START_EVENT_LOGGING:+ entry = &g_hook_handler_registry.on_connect_handlers;+ break;+ case EVENTLOG_SOCKET_HOOK_PRE_END_EVENT_LOGGING:+ entry = &g_hook_handler_registry.on_disconnect_handlers;+ break;+ default:+ errno = EINVAL;+ return STATUS_FROM_ERRNO();+ }+ if (entry == NULL) {+ errno = EINVAL;+ return STATUS_FROM_ERRNO();+ }++ // Get a pointer to the *last* entry for the given hook.+ assert(entry != NULL);+ while (*entry != NULL) {+ // TODO: Check if the hook is already registered?+ entry = &(*entry)->next;+ assert(entry != NULL);+ }++ // Allocate memory and write the new hook handler and data.+ assert(*entry == NULL);+ *entry = malloc(sizeof(EventlogSocketHookHandlerEntry));+ if (*entry == NULL) {+ RETURN_ON_ERROR_CLEANUP(+ STATUS_FROM_ERRNO(), // `malloc` sets errno.+ pthread_mutex_unlock(&g_hook_handler_registry_mutex));+ }+ assert(*entry != NULL);+ const EventlogSocketHookHandlerEntry hook_handler_entry =+ (EventlogSocketHookHandlerEntry){+ .hook_handler = hook_handler,+ .hook_data = hook_data,+ .next = NULL,+ };+ memcpy(*entry, &hook_handler_entry, sizeof(EventlogSocketHookHandlerEntry));++ // Release the `g_hook_handler_registry_mutex`.+ RETURN_ON_ERROR(STATUS_FROM_PTHREAD(+ pthread_mutex_unlock(&g_hook_handler_registry_mutex)));++ // Return OK.+ return STATUS_FROM_CODE(EVENTLOG_SOCKET_OK);+}++/* HIDDEN - see documentation in hooks.h */+HIDDEN EventlogSocketStatus+es_hooks_handle_hook(const EventlogSocketHook hook) {++ // Acquire the `g_hook_handler_registry_mutex`.+ RETURN_ON_ERROR(+ STATUS_FROM_PTHREAD(pthread_mutex_lock(&g_hook_handler_registry_mutex)));++ // Get a pointer to the *first* entry for the given hook.+ EventlogSocketHookHandlerEntry **entry = NULL;+ switch (hook) {+ case EVENTLOG_SOCKET_HOOK_POST_START_EVENT_LOGGING:+ entry = &g_hook_handler_registry.on_connect_handlers;+ break;+ case EVENTLOG_SOCKET_HOOK_PRE_END_EVENT_LOGGING:+ entry = &g_hook_handler_registry.on_disconnect_handlers;+ break;+ default:+ errno = EINVAL;+ return STATUS_FROM_ERRNO();+ }+ if (entry == NULL) {+ errno = EINVAL;+ return STATUS_FROM_ERRNO();+ }++ // Loop through and call each hook handler.+ assert(entry != NULL);+ while (*entry != NULL) {+ EventlogSocketHookHandler *const hook_handler = (*entry)->hook_handler;+ if (hook_handler != NULL) {+ const void *hook_data = (*entry)->hook_data;+ hook_handler(hook_data);+ }+ entry = &(*entry)->next;+ assert(entry != NULL);+ }++ // Release the `g_hook_handler_registry_mutex`.+ RETURN_ON_ERROR(STATUS_FROM_PTHREAD(+ pthread_mutex_unlock(&g_hook_handler_registry_mutex)));++ // Return OK.+ return STATUS_FROM_CODE(EVENTLOG_SOCKET_OK);+}
+ cbits/eventlog_socket/hooks.h view
@@ -0,0 +1,17 @@+#ifndef EVENTLOG_SOCKET_HOOKS_H+#define EVENTLOG_SOCKET_HOOKS_H++#include "./macros.h"+#include "eventlog_socket.h"++/// @brief Register a new hook handler.+///+/// @see eventlog_socket_register_hook+HIDDEN EventlogSocketStatus es_hooks_register_hook(+ EventlogSocketHook hook, EventlogSocketHookHandler *hook_handler,+ const void *hook_data);++/// @brief Call all handlers for a given `EventlogSocketHook`.+HIDDEN EventlogSocketStatus es_hooks_handle_hook(EventlogSocketHook hook);++#endif /* EVENTLOG_SOCKET_HOOKS_H */
+ cbits/eventlog_socket/hsapi.c view
@@ -0,0 +1,84 @@+/// @file hsapi.c+/// @brief Helper functions for the @c GHC.Eventlog.Socket module.+/// @details This module defines wrappers for some of the functions+/// exported by @c eventlog_socket.h for use in the Haskell+/// module @c GHC.Eventlog.Socket.+/// @author Wen Kokke+/// @version 0.1.3.0+/// @date 2025-2026+/// @copyright BSD-3-Clause License.+///++#include "./hsapi.h"+#include <string.h>++HIDDEN void es_hsapi_start(EventlogSocketStatus *eventlog_socket_status,+ EventlogSocketAddr *eventlog_socket_addr,+ EventlogSocketOpts *eventlog_socket_opts) {+ const EventlogSocketStatus status =+ eventlog_socket_start(eventlog_socket_addr, eventlog_socket_opts);+ memcpy(eventlog_socket_status, &status, sizeof(EventlogSocketStatus));+}++HIDDEN void es_hsapi_from_env(EventlogSocketStatus *eventlog_socket_status,+ EventlogSocketAddr *eventlog_socket_addr,+ EventlogSocketOpts *eventlog_socket_opts) {+ const EventlogSocketStatus status =+ eventlog_socket_from_env(eventlog_socket_addr, eventlog_socket_opts);+ memcpy(eventlog_socket_status, &status, sizeof(EventlogSocketStatus));+}++HIDDEN char *es_hsapi_strerror(EventlogSocketStatus *eventlog_socket_status) {+ return eventlog_socket_strerror(*eventlog_socket_status);+}++HIDDEN void+es_hsapi_register_hook(EventlogSocketStatus *eventlog_socket_status,+ EventlogSocketHook eventlog_socket_hook,+ EventlogSocketHookHandler eventlog_socket_hook_handler,+ const void *eventlog_socket_hook_data) {+ const EventlogSocketStatus status = eventlog_socket_register_hook(+ eventlog_socket_hook, eventlog_socket_hook_handler,+ eventlog_socket_hook_data);+ memcpy(eventlog_socket_status, &status, sizeof(EventlogSocketStatus));+}++HIDDEN void+es_hsapi_worker_status(EventlogSocketStatus *eventlog_socket_status_out) {+ const EventlogSocketStatus eventlog_socket_status =+ eventlog_socket_worker_status();+ memcpy(eventlog_socket_status_out, &eventlog_socket_status,+ sizeof(EventlogSocketStatus));+}++HIDDEN void+es_hsapi_control_status(EventlogSocketStatus *eventlog_socket_status_out) {+ const EventlogSocketStatus eventlog_socket_status =+ eventlog_socket_control_status();+ memcpy(eventlog_socket_status_out, &eventlog_socket_status,+ sizeof(EventlogSocketStatus));+}++HIDDEN void es_hsapi_control_register_namespace(+ EventlogSocketStatus *eventlog_socket_status,+ uint8_t eventlog_socket_namespace_len,+ char eventlog_socket_namespace[eventlog_socket_namespace_len],+ EventlogSocketControlNamespace **eventlog_socket_namespace_out) {+ const EventlogSocketStatus status =+ eventlog_socket_control_register_namespace(eventlog_socket_namespace_len,+ eventlog_socket_namespace,+ eventlog_socket_namespace_out);+ memcpy(eventlog_socket_status, &status, sizeof(EventlogSocketStatus));+}++HIDDEN void es_hsapi_control_register_command(+ EventlogSocketStatus *eventlog_socket_status,+ EventlogSocketControlNamespace *eventlog_socket_namespace,+ EventlogSocketControlCommandId eventlog_socket_command_id,+ EventlogSocketControlCommandHandler eventlog_socket_command_handler,+ const void *eventlog_socket_command_data) {+ const EventlogSocketStatus status = eventlog_socket_control_register_command(+ eventlog_socket_namespace, eventlog_socket_command_id,+ eventlog_socket_command_handler, eventlog_socket_command_data);+ memcpy(eventlog_socket_status, &status, sizeof(EventlogSocketStatus));+}
+ cbits/eventlog_socket/hsapi.h view
@@ -0,0 +1,48 @@+/// @file hsapi.h+/// @brief Helper functions for the @c GHC.Eventlog.Socket module.+/// @details This module declares wrappers for some of the functions+/// exported by @c eventlog_socket.h for use in the Haskell+/// module @c GHC.Eventlog.Socket.+/// @author Wen Kokke+/// @version 0.1.3.0+/// @date 2025-2026+/// @copyright BSD-3-Clause License.+///++#include "./macros.h"+#include <eventlog_socket.h>++HIDDEN void es_hsapi_start(EventlogSocketStatus *eventlog_socket_status,+ EventlogSocketAddr *eventlog_socket_addr,+ EventlogSocketOpts *eventlog_socket_opts);++HIDDEN void es_hsapi_from_env(EventlogSocketStatus *eventlog_socket_status,+ EventlogSocketAddr *eventlog_socket_addr,+ EventlogSocketOpts *eventlog_socket_opts);++HIDDEN char *es_hsapi_strerror(EventlogSocketStatus *eventlog_socket_status);++HIDDEN void+es_hsapi_register_hook(EventlogSocketStatus *eventlog_socket_status,+ EventlogSocketHook eventlog_socket_hook,+ EventlogSocketHookHandler eventlog_socket_hook_handler,+ const void *eventlog_socket_hook_data);++HIDDEN void+es_hsapi_worker_status(EventlogSocketStatus *eventlog_socket_status_out);++HIDDEN void+es_hsapi_control_status(EventlogSocketStatus *eventlog_socket_status_out);++HIDDEN void es_hsapi_control_register_namespace(+ EventlogSocketStatus *eventlog_socket_status,+ uint8_t eventlog_socket_namespace_len,+ char eventlog_socket_namespace[eventlog_socket_namespace_len],+ EventlogSocketControlNamespace **eventlog_socket_namespace_out);++HIDDEN void es_hsapi_control_register_command(+ EventlogSocketStatus *eventlog_socket_status,+ EventlogSocketControlNamespace *eventlog_socket_namespace,+ EventlogSocketControlCommandId eventlog_socket_command_id,+ EventlogSocketControlCommandHandler eventlog_socket_command_handler,+ const void *eventlog_socket_command_data);
cbits/eventlog_socket/worker.c view
@@ -16,12 +16,17 @@ #include "./debug.h" #include "./error.h"+#include "./hooks.h" #include "./init_state.h" #include "./poll.h" #include "./string.h" #include "./worker.h" #include "eventlog_socket.h" +/******************************************************************************+ * Worker Thread+ ******************************************************************************/+ /// @brief The maximum length of the queue for pending connections. #define LISTEN_BACKLOG 5 @@ -291,7 +296,7 @@ const bool should_stop = // ...some eventlog writer is currently running and either: is_running && (- // ...(1) it's not EventlogSocketWriter, or...+ // ...(1) it's not EventLogSocketWriter, or... !is_attached || // ...(2) we've already had our first connection. //@@ -309,6 +314,13 @@ // We stop event logging. if (should_stop) {+ // If the current eventlog writer *is* EventLogSocketWriter...+ if (is_attached) {+ // ...trigger the pre-endEventLogging hooks.+ DEBUG_DEBUG("%s", "Trigger pre-endEventLogging hooks.");+ EXIT_ON_ERROR(+ es_hooks_handle_hook(EVENTLOG_SOCKET_HOOK_PRE_END_EVENT_LOGGING));+ } DEBUG_DEBUG("%s", "Stopping current event logger."); endEventLogging(); }@@ -325,9 +337,18 @@ // We start event logging with EventlogSocketWriter. if (should_start) { DEBUG_DEBUG("%s", "Starting new event logger.");- // TODO: Add retry loop.- const bool is_started = startEventLogging(&EventLogSocketWriter);+ bool is_started = false;+ while (!is_started) {+ // TODO: Limit number of retries. Since this loop should not be blocking,+ // this requires exiting this listen step and remembering that+ // connection was left in an unfinished state, and retrying+ // startEventLogging the next time a listen step is entered.+ is_started = startEventLogging(&EventLogSocketWriter);+ } DEBUG_TRACE("is_started: %s", is_started ? "true" : "false");+ DEBUG_DEBUG("%s", "Trigger post-startEventLogging hooks.");+ EXIT_ON_ERROR(+ es_hooks_handle_hook(EVENTLOG_SOCKET_HOOK_POST_START_EVENT_LOGGING)); } // Update *g_worker_state.init_state_ptr to record that we've seen our first
eventlog-socket.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: eventlog-socket-version: 0.1.2.0+version: 0.1.3.0 synopsis: Stream GHC eventlog events to external processes. description: The @eventlog-socket@ package lets you stream the GHC eventlog over a socket in realtime.@@ -29,6 +29,10 @@ cbits/eventlog_socket/debug.h cbits/eventlog_socket/error.c cbits/eventlog_socket/error.h+ cbits/eventlog_socket/hooks.c+ cbits/eventlog_socket/hooks.h+ cbits/eventlog_socket/hsapi.c+ cbits/eventlog_socket/hsapi.h cbits/eventlog_socket/init_state.h cbits/eventlog_socket/macros.h cbits/eventlog_socket/poll.h@@ -53,7 +57,7 @@ source-repository this type: git location: https://github.com/well-typed/eventlog-socket.git- tag: eventlog-socket-0.1.2.0+ tag: eventlog-socket-0.1.3.0 subdir: eventlog-socket flag debug@@ -98,6 +102,8 @@ default-extensions: LambdaCase c-sources: cbits/eventlog_socket/error.c+ cbits/eventlog_socket/hooks.c+ cbits/eventlog_socket/hsapi.c cbits/eventlog_socket/string.c cbits/eventlog_socket/worker.c cbits/eventlog_socket/write_buffer.c
include/eventlog_socket.h view
@@ -182,6 +182,7 @@ /// to determine the host name for a TCP/IPv4 socket. /// /// @since 0.1.2.0+// NOTE: Keep in sync with eventlogSocketEnvInetHost in Socket.hsc. #define EVENTLOG_SOCKET_ENV_INET_HOST "GHC_EVENTLOG_INET_HOST" /// @brief@@ -189,6 +190,7 @@ /// to determine the port number for a TCP/IPv4 socket. /// /// @since 0.1.2.0+// NOTE: Keep in sync with eventlogSocketEnvInetPort in Socket.hsc. #define EVENTLOG_SOCKET_ENV_INET_PORT "GHC_EVENTLOG_INET_PORT" /// @brief@@ -476,6 +478,47 @@ /// /// @since 0.1.2.0 EventlogSocketStatus eventlog_socket_control_status(void);++/******************************************************************************+ * Start/End Hooks+ ******************************************************************************/++/// @brief An @c eventlog-socket hook.+typedef enum {+ /// @brief The post-startEventLogging hook is guaranteed to be called *after*+ /// each time that the `EventLogSocketWriter` is attached and event logging is+ /// started.+ ///+ /// If the `EventLogSocketWriter` is attached in a C main, prior to GHC RTS+ /// initialisation, then @c eventlog-socket waits to call this hook for the+ /// first time until the GHC RTS is initialised.+ EVENTLOG_SOCKET_HOOK_POST_START_EVENT_LOGGING,+ /// @brief The pre-endEventLogging hook is guaranteed to be called *before*+ /// each time that the `EventLogSocketWriter` is detached and event logging is+ /// ended.+ EVENTLOG_SOCKET_HOOK_PRE_END_EVENT_LOGGING,+} EventlogSocketHook;++/// @brief An @c eventlog-socket hook handler.+///+/// @since 0.1.3.0+typedef void EventlogSocketHookHandler(const void *hook_data);++/// @brief Register a new hook handler.+///+/// @return Upon successful completion, this function registers the command and+/// returns a status with code @c EVENTLOG_SOCKET_OK.+///+/// @return If a system error occurs, this function returns a status with code+/// @c EVENTLOG_SOCKET_ERR_SYS and the error code set to indicate the error.+///+/// @note The @p hook_handler should not use the functions from this header.+///+/// @since 0.1.3.0+EventlogSocketStatus+eventlog_socket_register_hook(EventlogSocketHook hook,+ EventlogSocketHookHandler *hook_handler,+ const void *hook_data); /****************************************************************************** * Control Commands
src/GHC/Eventlog/Socket.hsc view
@@ -45,6 +45,14 @@ fromEnv, EventlogSocketAddrError(..), + -- ** Hooks #hooks#+ Hook (+ HookPostStartEventLogging,+ HookPreEndEventLogging+ ),+ HookHandler,+ registerHook,+ -- ** Control commands #control_commands# Namespace, CommandId(..),@@ -81,9 +89,8 @@ import GHC.Enum (toEnumError) import System.IO.Unsafe (unsafePerformIO) -#include <eventlog_socket/macros.h> #include <eventlog_socket.h>-#include <string.h>+#include <eventlog_socket/hsapi.h> -------------------------------------------------------------------------------- -- High-level API@@ -250,18 +257,27 @@ -------------------------------------------------------------------------------- -- Environment variables -eventlogSocketEnvUnixPath :: String-eventlogSocketEnvUnixPath = #{const_str EVENTLOG_SOCKET_ENV_UNIX_PATH}+{- |+The name of the environment variable used by `eventlog_socket_from_env`+to determine the host name for a TCP/IPv4 socket. +@since 0.1.2.0+-} eventlogSocketEnvInetHost :: String-eventlogSocketEnvInetHost = #{const_str EVENTLOG_SOCKET_ENV_INET_HOST}+-- NOTE: Keep in sync with EVENTLOG_SOCKET_ENV_INET_HOST in eventlog_socketh.h.+eventlogSocketEnvInetHost = "GHC_EVENTLOG_INET_HOST" -eventlogSocketEnvInetPort :: String-eventlogSocketEnvInetPort = #{const_str EVENTLOG_SOCKET_ENV_INET_PORT} -eventlogSocketEnvWait :: String-eventlogSocketEnvWait = #{const_str EVENTLOG_SOCKET_ENV_WAIT}+{- |+The name of the environment variable used by `eventlog_socket_from_env`+to determine the port number for a TCP/IPv4 socket. +@since 0.1.2.0+-}+eventlogSocketEnvInetPort :: String+-- NOTE: Keep in sync with EVENTLOG_SOCKET_ENV_INET_PORT in eventlog_socketh.h.+eventlogSocketEnvInetPort = "GHC_EVENTLOG_INET_PORT"+ -------------------------------------------------------------------------------- -- Errors @@ -305,7 +321,7 @@ <> " was not set." {- |-Test the current status of the worker thread. If it has failed, throw an `IOException`.+Test the current status of the worker thread. If it has failed, throw an `Control.Exception.IOException`. @since 0.1.2.0 -}@@ -314,7 +330,7 @@ throwEventlogSocketStatusAsIOException =<< workerStatus {- |-Test the current status of the control thread. If it has failed, throw an `IOException`.+Test the current status of the control thread. If it has failed, throw an `Control.Exception.IOException`. @since 0.1.2.0 -}@@ -347,7 +363,7 @@ {- | Internal helper. -Throw an `EventlogSocketStatus` as an `IOException`.+Throw an t`EventlogSocketStatus` as an `Control.Exception.IOException`. -} throwEventlogSocketStatusAsIOException :: EventlogSocketStatus -> IO () throwEventlogSocketStatusAsIOException ess =@@ -357,7 +373,7 @@ {- | Internal helper. -Throw an `EventlogSocketStatus` as an `IOException`.+Throw an t`EventlogSocketStatus` as an `Control.Exception.IOException`. __Warning__: This function _still_ throws an error if the status code is `EVENTLOG_SOCKET_OK`. -}@@ -369,6 +385,98 @@ throwIO $ userError str --------------------------------------------------------------------------------+-- Hooks API+--------------------------------------------------------------------------------++{- |+The type of @eventlog-socket@ hooks.++@since 0.1.3.0+-}+newtype+ {-# CTYPE "eventlog_socket.h" "EventlogSocketHook" #-}+ Hook = Hook+ { unHook :: #{type EventlogSocketHook}+ }+ deriving (Eq, Show, Storable)++{- |+The hook that runs /after/ @startEventLogging@ is called.++@since 0.1.3.0+-}+pattern HookPostStartEventLogging :: Hook+pattern HookPostStartEventLogging = Hook #{const EVENTLOG_SOCKET_HOOK_POST_START_EVENT_LOGGING}++{- |+The hook that runs /before/ @endEventLogging@ is called.++@since 0.1.3.0+-}+pattern HookPreEndEventLogging :: Hook+pattern HookPreEndEventLogging = Hook #{const EVENTLOG_SOCKET_HOOK_PRE_END_EVENT_LOGGING}++{-# COMPLETE+ HookPostStartEventLogging,+ HookPreEndEventLogging #-}++{- |+The type of hook handlers.++The hook handler is evaluated once each time the control socket receives a request for the associated hook.++__Warning__: The hook handler /must not/ call back into the @eventlog-socket@ API.++@since 0.1.3.0+-}+type HookHandler = IO ()++{- |+Register an @eventlog-socket@ hook.++__Warning__: Hooks cannot be unregistered and will be kept in memory until program exit.++@since 0.1.3.0+-}+registerHook ::+ -- | The hook.+ Hook ->+ -- | The hook handler.+ HookHandler ->+ IO ()+registerHook hook hookHandler = do+ -- Wrap the Haskell hook handler for the C API+ let c_hookHandler hookDataPtr =+ assert (hookDataPtr == nullPtr) $+ hookHandler++ bracketOnError (makeHookHandlerFunPtr c_hookHandler) freeHaskellFunPtr $ \c_hookHandlerPtr -> do++ -- Try to register the command:+ status <-+ -- Allocate space for the return status:+ allocaBytes #{size EventlogSocketStatus} $ \essPtr -> do++ -- Try to register the command:+ eventlog_socket_register_hook+ essPtr+ hook+ c_hookHandlerPtr+ nullPtr++ -- Marshal the return status:+ peekEventlogSocketStatus essPtr++ -- Handle the return status:+ case essStatusCode status of+ -- The return status is OK.+ EVENTLOG_SOCKET_OK -> pure ()++ -- The remaining errors are all system errors:+ _otherwise ->+ vacuous $ withEventlogSocketStatus status throwEventlogSocketStatus++-------------------------------------------------------------------------------- -- Control Commands API -------------------------------------------------------------------------------- @@ -898,53 +1006,23 @@ -------------------------------------------------------------------------------- -- eventlog_socket_start -foreign import capi safe "GHC/Eventlog/Socket_hsc.h eventlog_socket_wrap_start"+foreign import capi safe "eventlog_socket/hsapi.h es_hsapi_start" eventlog_socket_start :: Ptr EventlogSocketStatus -> Ptr EventlogSocketAddr -> Ptr EventlogSocketOpts -> IO () -#{def- HIDDEN void eventlog_socket_wrap_start(- EventlogSocketStatus *eventlog_socket_status,- EventlogSocketAddr *eventlog_socket_addr,- EventlogSocketOpts *eventlog_socket_opts- )- {- const EventlogSocketStatus status = eventlog_socket_start(- eventlog_socket_addr,- eventlog_socket_opts- );- memcpy(eventlog_socket_status, &status, sizeof(EventlogSocketStatus));- }-}- -------------------------------------------------------------------------------- -- eventlog_socket_from_env -foreign import capi safe "GHC/Eventlog/Socket_hsc.h eventlog_socket_wrap_from_env"+foreign import capi safe "eventlog_socket/hsapi.h es_hsapi_from_env" eventlog_socket_from_env :: Ptr EventlogSocketStatus -> Ptr EventlogSocketAddr -> Ptr EventlogSocketOpts -> IO () -#{def- HIDDEN void eventlog_socket_wrap_from_env(- EventlogSocketStatus *eventlog_socket_status,- EventlogSocketAddr *eventlog_socket_addr,- EventlogSocketOpts *eventlog_socket_opts- )- {- const EventlogSocketStatus status = eventlog_socket_from_env(- eventlog_socket_addr,- eventlog_socket_opts- );- memcpy(eventlog_socket_status, &status, sizeof(EventlogSocketStatus));- }-}- -------------------------------------------------------------------------------- -- eventlog_socket_wait @@ -954,56 +1032,42 @@ -------------------------------------------------------------------------------- -- eventlog_socket_strerror -foreign import capi safe "GHC/Eventlog/Socket_hsc.h eventlog_socket_wrap_strerror"+foreign import capi safe "eventlog_socket/hsapi.h es_hsapi_strerror" eventlog_socket_strerror :: Ptr EventlogSocketStatus -> IO CString -#{def- HIDDEN char *eventlog_socket_wrap_strerror(- EventlogSocketStatus *eventlog_socket_status- )- {- return eventlog_socket_strerror(*eventlog_socket_status);- }-}+--------------------------------------------------------------------------------+-- eventlog_socket_register_hook +foreign import capi safe "eventlog_socket/hsapi.h es_hsapi_register_hook"+ eventlog_socket_register_hook ::+ Ptr EventlogSocketStatus ->+ Hook ->+ FunPtr (Ptr a -> IO ()) ->+ Ptr a ->+ IO ()++foreign import ccall "wrapper"+ makeHookHandlerFunPtr ::+ (Ptr a -> IO ()) ->+ IO (FunPtr (Ptr a -> IO ()))+ -------------------------------------------------------------------------------- -- eventlog_socket_worker_status -foreign import capi safe "GHC/Eventlog/Socket_hsc.h eventlog_socket_wrap_worker_status"+foreign import capi safe "eventlog_socket/hsapi.h es_hsapi_worker_status" eventlog_socket_worker_status :: Ptr EventlogSocketStatus -> IO ()--#{def- HIDDEN void eventlog_socket_wrap_worker_status(- EventlogSocketStatus *eventlog_socket_status_out- )- {- const EventlogSocketStatus eventlog_socket_status = eventlog_socket_worker_status();- memcpy(eventlog_socket_status_out, &eventlog_socket_status, sizeof(EventlogSocketStatus));- }-}- -------------------------------------------------------------------------------- -- eventlog_socket_control_status -foreign import capi safe "GHC/Eventlog/Socket_hsc.h eventlog_socket_wrap_control_status"+foreign import capi safe "eventlog_socket/hsapi.h es_hsapi_control_status" eventlog_socket_control_status :: Ptr EventlogSocketStatus -> IO () -#{def- HIDDEN void eventlog_socket_wrap_control_status(- EventlogSocketStatus *eventlog_socket_status_out- )- {- const EventlogSocketStatus eventlog_socket_status = eventlog_socket_control_status();- memcpy(eventlog_socket_status_out, &eventlog_socket_status, sizeof(EventlogSocketStatus));- }-}- -------------------------------------------------------------------------------- -- eventlog_socket_control_strnamespace @@ -1018,7 +1082,7 @@ -------------------------------------------------------------------------------- -- eventlog_socket_control_register_namespace -foreign import capi safe "GHC/Eventlog/Socket_hsc.h eventlog_socket_wrap_control_register_namespace"+foreign import capi safe "eventlog_socket/hsapi.h es_hsapi_control_register_namespace" eventlog_socket_control_register_namespace :: Ptr EventlogSocketStatus -> ( #{type uint8_t} ) ->@@ -1026,26 +1090,10 @@ Ptr (Ptr Namespace) -> IO () -#{def- HIDDEN void eventlog_socket_wrap_control_register_namespace(- EventlogSocketStatus *eventlog_socket_status,- uint8_t eventlog_socket_namespace_len,- char eventlog_socket_namespace[eventlog_socket_namespace_len],- EventlogSocketControlNamespace **eventlog_socket_namespace_out- ) {- const EventlogSocketStatus status = eventlog_socket_control_register_namespace(- eventlog_socket_namespace_len,- eventlog_socket_namespace,- eventlog_socket_namespace_out- );- memcpy(eventlog_socket_status, &status, sizeof(EventlogSocketStatus));- }-}- -------------------------------------------------------------------------------- -- eventlog_socket_control_register_command -foreign import capi safe "GHC/Eventlog/Socket_hsc.h eventlog_socket_wrap_control_register_command"+foreign import capi safe "eventlog_socket/hsapi.h es_hsapi_control_register_command" eventlog_socket_control_register_command :: Ptr EventlogSocketStatus -> Ptr Namespace ->@@ -1058,21 +1106,3 @@ makeCommandHandlerFunPtr :: (Ptr Namespace -> CommandId -> Ptr a -> IO ()) -> IO (FunPtr (Ptr Namespace -> CommandId -> Ptr a -> IO ()))--#{def- HIDDEN void eventlog_socket_wrap_control_register_command(- EventlogSocketStatus *eventlog_socket_status,- EventlogSocketControlNamespace *eventlog_socket_namespace,- EventlogSocketControlCommandId eventlog_socket_command_id,- EventlogSocketControlCommandHandler eventlog_socket_command_handler,- const void *eventlog_socket_command_data- ) {- const EventlogSocketStatus status = eventlog_socket_control_register_command(- eventlog_socket_namespace,- eventlog_socket_command_id,- eventlog_socket_command_handler,- eventlog_socket_command_data- );- memcpy(eventlog_socket_status, &status, sizeof(EventlogSocketStatus));- }-}