ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
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_shutdown (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)   query_init(port)
 Initialize query tool (debug builds only)
 
#define QUERY_SHUTDOWN()   query_shutdown()
 Shutdown query tool (debug builds only)
 
#define QUERY_ACTIVE()   query_is_active()
 Check if query tool is active (debug builds only)
 
#define QUERY_PORT()   query_get_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

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 ( )    query_is_active()

#include <query.h>

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)    query_init(port)

#include <query.h>

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 ( )    query_get_port()

#include <query.h>

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 ( )    query_shutdown()

#include <query.h>

Shutdown query tool (debug builds only)

Definition at line 128 of file query.h.

Function Documentation

◆ query_get_port()

int query_get_port ( void  )

#include <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 407 of file query.c.

407 {
408 return g_query_port;
409}

◆ query_init()

int query_init ( int  preferred_port)

#include <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 216 of file query.c.

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

References errno, HEALTH_CHECK_TIMEOUT_MS, and query_shutdown().

◆ query_is_active()

bool query_is_active ( void  )

#include <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 375 of file query.c.

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

◆ query_shutdown()

void query_shutdown ( void  )

#include <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 317 of file query.c.

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

Referenced by query_init().