ascii-chat 0.11.33
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)   query_init(port)
 Initialize query tool (debug builds only)
 
#define QUERY_SHUTDOWN()   query_destroy()
 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_destroy()

#include <query.h>

Shutdown query tool (debug builds only)

Definition at line 128 of file query.h.

Function Documentation

◆ query_destroy()

void query_destroy ( 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 311 of file query.c.

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

References US_PER_MS_INT.

Referenced by query_init().

◆ 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 400 of file query.c.

400 {
401 return g_query_port;
402}

◆ 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 214 of file query.c.

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

References errno, HEALTH_CHECK_TIMEOUT_NS, PLATFORM_MAX_PATH_LENGTH, query_destroy(), and safe_snprintf().

◆ 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 368 of file query.c.

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