ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
Query

Files

file  query.h
 Runtime variable query tool API for debug builds.

Functions

int query_init (int preferred_port)
 Initialize the query tool by spawning the controller process.
void query_destroy (void)
 Shutdown the query tool and terminate the controller process.
bool query_is_active (void)
 Check if the query tool controller is currently active.
int query_get_port (void)
 Get the port number of the active query server.

Convenience Macros

These macros provide a convenient interface that compiles out completely in release builds. Use these instead of calling the functions directly.

#define QUERY_INIT(port)
 Initialize query tool (debug builds only).
#define QUERY_SHUTDOWN()
 Shutdown query tool (debug builds only).
#define QUERY_ACTIVE()
 Check if query tool is active (debug builds only).
#define QUERY_PORT()
 Get query server port (debug builds only).

Detailed Description

This header provides the public C API for the query tool, which enables runtime variable inspection via HTTP queries. The tool uses an external LLDB process to attach to the running application and read variable values.

Architecture

  • Target process (ascii-chat) runs normally with debug symbols
  • Controller process (ascii-query-server) attaches via LLDB
  • HTTP server in controller accepts curl requests
  • When target is stopped at breakpoint, controller is still running

Example Usage

// In your application startup (debug builds only)
int port = QUERY_INIT(9999);
if (port > 0) {
printf("Query server at http://localhost:%d\n", port);
}
// ... application runs ...
// Query variables via curl:
// curl 'localhost:9999/query?file=src/server.c&line=100&name=client_count'
// curl 'localhost:9999/query?file=src/server.c&line=100&name=client.socket.fd&break'
// curl -X POST 'localhost:9999/continue'
// On shutdown
#define QUERY_INIT(port)
Initialize query tool (debug builds only).
Definition query.h:123
#define QUERY_SHUTDOWN()
Shutdown query tool (debug builds only).
Definition query.h:128

Note: All functions and macros compile out completely in release builds (when NDEBUG is defined). The query tool has zero runtime overhead in production builds.

See also
docs/tooling/query.md for full documentation
docs/tooling/QUERY_TOOL_PLAN.md for implementation details

Macro Definition Documentation

◆ QUERY_ACTIVE

#define QUERY_ACTIVE ( )

#include <include/ascii-chat/tooling/query/query.h>

Value:
bool query_is_active(void)
Check if the query tool controller is currently active.
Definition query.c:373

Check if query tool is active (debug builds only).

Returns
true if active, false otherwise (always false in release builds)

Definition at line 134 of file query.h.

◆ QUERY_INIT

#define QUERY_INIT ( port)

#include <include/ascii-chat/tooling/query/query.h>

Value:
int query_init(int preferred_port)
Initialize the query tool by spawning the controller process.
Definition query.c:219

Initialize query tool (debug builds only).

Parameters
portPreferred HTTP server port
Returns
Port number on success, -1 on failure (or in release builds)

Definition at line 123 of file query.h.

◆ QUERY_PORT

#define QUERY_PORT ( )

#include <include/ascii-chat/tooling/query/query.h>

Value:
int query_get_port(void)
Get the port number of the active query server.
Definition query.c:405

Get query server port (debug builds only).

Returns
Port number if active, -1 otherwise (always -1 in release builds)

Definition at line 140 of file query.h.

◆ QUERY_SHUTDOWN

#define QUERY_SHUTDOWN ( )

#include <include/ascii-chat/tooling/query/query.h>

Value:
void query_destroy(void)
Shutdown the query tool and terminate the controller process.
Definition query.c:316

Shutdown query tool (debug builds only).

Definition at line 128 of file query.h.

Function Documentation

◆ query_destroy()

void query_destroy ( void )

#include <include/ascii-chat/tooling/query/query.h>

Shutdown the query tool and terminate the controller process.

This function cleanly terminates the ascii-query-server process. The target process continues running normally after shutdown.

Note
Safe to call even if query_init() was not called or failed
Only available in debug builds (NDEBUG not defined)

Definition at line 316 of file query.c.

316 {
317 if (!g_query_active && g_controller_pid <= 0) {
318 return;
319 }
320
321 fprintf(stderr, "[query] Shutting down query server...\n");
322
323#ifdef _WIN32
324 if (g_controller_handle != NULL) {
325 // Send termination signal
326 TerminateProcess(g_controller_handle, 0);
327
328 // Wait for process to exit (with timeout)
329 WaitForSingleObject(g_controller_handle, 3000);
330
331 CloseHandle(g_controller_handle);
332 g_controller_handle = NULL;
333 g_controller_pid = 0;
334 }
335
336 // Winsock cleanup handled by platform_cleanup()
337#else
338 if (g_controller_pid > 0) {
339 // Send SIGTERM for graceful shutdown
340 kill(g_controller_pid, SIGTERM);
341
342 // Wait for process to exit (with timeout)
343 int status;
344 int wait_count = 0;
345 while (wait_count < 30) { // 3 second timeout
346 pid_t result = waitpid(g_controller_pid, &status, WNOHANG);
347 if (result == g_controller_pid) {
348 break;
349 }
350 if (result < 0) {
351 break;
352 }
353 usleep(100 * US_PER_MS_INT); // 100ms
354 wait_count++;
355 }
356
357 // If still running, force kill
358 if (wait_count >= 30) {
359 kill(g_controller_pid, SIGKILL);
360 waitpid(g_controller_pid, &status, 0);
361 }
362
363 g_controller_pid = -1;
364 }
365#endif
366
367 g_query_active = false;
368 g_query_port = -1;
369
370 fprintf(stderr, "[query] Query server stopped\n");
371}
#define US_PER_MS_INT
Definition time.h:172

References US_PER_MS_INT.

Referenced by query_init().

◆ query_get_port()

int query_get_port ( void )

#include <include/ascii-chat/tooling/query/query.h>

Get the port number of the active query server.

Returns
The port number if active, -1 if not active
Note
Only available in debug builds (NDEBUG not defined)

Definition at line 405 of file query.c.

405 {
406 return g_query_port;
407}

◆ query_init()

int query_init ( int preferred_port)

#include <include/ascii-chat/tooling/query/query.h>

Initialize the query tool by spawning the controller process.

This function spawns the ascii-query-server process, which attaches to the current process via LLDB and starts an HTTP server on the specified port.

The controller process is completely separate from the target process:

  • Target can be stopped at breakpoints while controller serves HTTP
  • Controller sends LLDB commands to read variables and control execution
  • No instrumentation or code modification in the target is required
Parameters
preferred_portThe port number for the HTTP server (e.g., 9999)
Returns
The actual port number on success, -1 on failure
Note
Only available in debug builds (NDEBUG not defined)
The controller may take a moment to attach; this function waits for the HTTP server to become ready before returning

Platform notes:

  • macOS: May require code signing with get-task-allow entitlement
  • Linux: May require ptrace permissions (check /proc/sys/kernel/yama/ptrace_scope)
  • Windows: Uses CreateProcess instead of fork/exec

Definition at line 219 of file query.c.

219 {
220 // Already initialized?
221 if (g_query_active) {
222 return g_query_port;
223 }
224
225 // Find the query server executable
226 char server_path[PLATFORM_MAX_PATH_LENGTH];
227 if (!find_query_server_path(server_path, sizeof(server_path))) {
228 fprintf(stderr, "[query] Could not find ascii-query-server executable\n");
229 fprintf(stderr, "[query] Set ASCIICHAT_QUERY_SERVER environment variable or ensure "
230 "it's in .deps-cache/query-tool/\n");
231 return -1;
232 }
233
234#ifdef _WIN32
235 // Windows implementation using CreateProcess
236 // Winsock is already initialized by platform_init()
237
239 safe_snprintf(cmdline, sizeof(cmdline), "\"%s\" --attach %lu --port %d", server_path,
240 (unsigned long)GetCurrentProcessId(), preferred_port);
241
242 STARTUPINFOA si;
243 PROCESS_INFORMATION pi;
244 memset(&si, 0, sizeof(si));
245 si.cb = sizeof(si);
246 memset(&pi, 0, sizeof(pi));
247
248 // Create the controller process
249 if (!CreateProcessA(NULL, // Use command line
250 cmdline, // Command line
251 NULL, // Process security attributes
252 NULL, // Thread security attributes
253 FALSE, // Don't inherit handles
254 CREATE_NEW_CONSOLE, // Creation flags
255 NULL, // Use parent's environment
256 NULL, // Use parent's directory
257 &si, &pi)) {
258 fprintf(stderr, "[query] Failed to start query server: error %lu\n", GetLastError());
259 return -1;
260 }
261
262 g_controller_handle = pi.hProcess;
263 g_controller_pid = pi.dwProcessId;
264 CloseHandle(pi.hThread); // Don't need the thread handle
265
266 fprintf(stderr, "[query] Started query server (PID %lu) on port %d\n", (unsigned long)g_controller_pid,
267 preferred_port);
268
269#else
270 // Unix implementation using fork/exec
271 pid_t self_pid = getpid();
272
273 char port_str[TOOLING_QUERY_PORT_BUFFER_SIZE];
274 char pid_str[TOOLING_QUERY_PID_BUFFER_SIZE];
275 safe_snprintf(port_str, sizeof(port_str), "%d", preferred_port);
276 safe_snprintf(pid_str, sizeof(pid_str), "%d", self_pid);
277
278 pid_t child = fork();
279 if (child < 0) {
280 fprintf(stderr, "[query] fork() failed: %s\n", strerror(errno));
281 return -1;
282 }
283
284 if (child == 0) {
285 // Child process: exec the controller
286 // Redirect stdout/stderr to /dev/null or a log file to avoid clutter
287 // (The controller has its own logging)
288
289 execl(server_path, "ascii-query-server", "--attach", pid_str, "--port", port_str, (char *)NULL);
290
291 // If exec fails
292 fprintf(stderr, "[query] exec(%s) failed: %s\n", server_path, strerror(errno));
293 exit(1);
294 }
295
296 // Parent process
297 g_controller_pid = child;
298 fprintf(stderr, "[query] Started query server (PID %d) on port %d\n", child, preferred_port);
299#endif
300
301 // Wait for the HTTP server to become ready
302 fprintf(stderr, "[query] Waiting for HTTP server to be ready...\n");
303 if (!wait_for_http_ready(preferred_port, HEALTH_CHECK_TIMEOUT_NS)) {
304 fprintf(stderr, "[query] Timeout waiting for query server to start\n");
306 return -1;
307 }
308
309 g_query_active = true;
310 g_query_port = preferred_port;
311
312 fprintf(stderr, "[query] Query server ready at http://localhost:%d\n", preferred_port);
313 return preferred_port;
314}
#define PLATFORM_MAX_PATH_LENGTH
Definition common.h:95
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Platform-safe snprintf wrapper.
int errno
#define TOOLING_QUERY_PID_BUFFER_SIZE
Definition query.c:16
#define HEALTH_CHECK_TIMEOUT_NS
Definition query.c:45
#define TOOLING_QUERY_PORT_BUFFER_SIZE
Definition query.c:15
#define TOOLING_QUERY_CMDLINE_BUFFER_SIZE
Definition query.c:14

References errno, HEALTH_CHECK_TIMEOUT_NS, PLATFORM_MAX_PATH_LENGTH, query_destroy(), safe_snprintf(), TOOLING_QUERY_CMDLINE_BUFFER_SIZE, TOOLING_QUERY_PID_BUFFER_SIZE, and TOOLING_QUERY_PORT_BUFFER_SIZE.

◆ query_is_active()

bool query_is_active ( void )

#include <include/ascii-chat/tooling/query/query.h>

Check if the query tool controller is currently active.

Returns
true if the controller process is running and responsive
false if the controller is not running or not responding
Note
Only available in debug builds (NDEBUG not defined)

Definition at line 373 of file query.c.

373 {
374 if (!g_query_active) {
375 return false;
376 }
377
378// Verify the controller is still running
379#ifdef _WIN32
380 if (g_controller_handle != NULL) {
381 DWORD exit_code;
382 if (GetExitCodeProcess(g_controller_handle, &exit_code)) {
383 if (exit_code != STILL_ACTIVE) {
384 g_query_active = false;
385 g_controller_handle = NULL;
386 g_controller_pid = 0;
387 return false;
388 }
389 }
390 }
391#else
392 if (g_controller_pid > 0) {
393 // Check if process is still alive
394 if (kill(g_controller_pid, 0) != 0) {
395 g_query_active = false;
396 g_controller_pid = -1;
397 return false;
398 }
399 }
400#endif
401
402 return g_query_active;
403}