packages feed

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 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));-  }-}