ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
instrument_log.c File Reference

Go to the source code of this file.

Data Structures

struct  asciichat_instr_only_selector
 Selector for filtering instrumentation output by file, function, or module. More...
 
struct  asciichat_instr_only_list
 Dynamic array of instrumentation selectors for filtering output. More...
 
struct  asciichat_instr_runtime
 Per-thread instrumentation runtime state. More...
 

Macros

#define ASCII_INSTR_SOURCE_PRINT_HAVE_REGEX   1
 
#define posix_write   write
 
#define ASCII_INSTR_SOURCE_PRINT_DEFAULT_BASENAME   "ascii-instr"
 
#define ASCII_INSTR_SOURCE_PRINT_MAX_LINE   4096
 
#define ASCII_INSTR_SOURCE_PRINT_MAX_SNIPPET   2048
 

Typedefs

typedef enum asciichat_instr_selector_type asciichat_instr_selector_type_t
 
typedef struct asciichat_instr_only_selector asciichat_instr_only_selector_t
 Selector for filtering instrumentation output by file, function, or module.
 
typedef struct asciichat_instr_only_list asciichat_instr_only_list_t
 Dynamic array of instrumentation selectors for filtering output.
 
typedef struct asciichat_instr_runtime asciichat_instr_runtime_t
 Per-thread instrumentation runtime state.
 

Enumerations

enum  asciichat_instr_selector_type { ASCII_INSTR_SOURCE_PRINT_SELECTOR_FILE_SUBSTRING = 0 , ASCII_INSTR_SOURCE_PRINT_SELECTOR_FILE_GLOB = 1 , ASCII_INSTR_SOURCE_PRINT_SELECTOR_FUNCTION_GLOB = 2 , ASCII_INSTR_SOURCE_PRINT_SELECTOR_MODULE = 3 }
 

Functions

asciichat_instr_runtime_tasciichat_instr_runtime_get (void)
 
void asciichat_instr_runtime_destroy (asciichat_instr_runtime_t *runtime)
 
void asciichat_instr_runtime_global_destroy (void)
 
void asciichat_instr_log_line (const char *file_path, uint32_t line_number, const char *function_name, const char *snippet, uint8_t is_macro_expansion)
 
bool asciichat_instr_coverage_enabled (void)
 
void asciichat_instr_log_pc (uintptr_t program_counter)
 

Macro Definition Documentation

◆ ASCII_INSTR_SOURCE_PRINT_DEFAULT_BASENAME

#define ASCII_INSTR_SOURCE_PRINT_DEFAULT_BASENAME   "ascii-instr"

Definition at line 56 of file instrument_log.c.

◆ ASCII_INSTR_SOURCE_PRINT_HAVE_REGEX

#define ASCII_INSTR_SOURCE_PRINT_HAVE_REGEX   1

Definition at line 33 of file instrument_log.c.

◆ ASCII_INSTR_SOURCE_PRINT_MAX_LINE

#define ASCII_INSTR_SOURCE_PRINT_MAX_LINE   4096

Definition at line 60 of file instrument_log.c.

◆ ASCII_INSTR_SOURCE_PRINT_MAX_SNIPPET

#define ASCII_INSTR_SOURCE_PRINT_MAX_SNIPPET   2048

Definition at line 64 of file instrument_log.c.

◆ posix_write

#define posix_write   write

Definition at line 52 of file instrument_log.c.

Typedef Documentation

◆ asciichat_instr_only_list_t

Dynamic array of instrumentation selectors for filtering output.

◆ asciichat_instr_only_selector_t

Selector for filtering instrumentation output by file, function, or module.

◆ asciichat_instr_runtime_t

Per-thread instrumentation runtime state.

Tracks state for source code instrumentation logging, including file descriptors, filter configuration, and rate limiting for the current thread.

◆ asciichat_instr_selector_type_t

Enumeration Type Documentation

◆ asciichat_instr_selector_type

Enumerator
ASCII_INSTR_SOURCE_PRINT_SELECTOR_FILE_SUBSTRING 
ASCII_INSTR_SOURCE_PRINT_SELECTOR_FILE_GLOB 
ASCII_INSTR_SOURCE_PRINT_SELECTOR_FUNCTION_GLOB 
ASCII_INSTR_SOURCE_PRINT_SELECTOR_MODULE 

Definition at line 67 of file instrument_log.c.

67 {
@ ASCII_INSTR_SOURCE_PRINT_SELECTOR_FUNCTION_GLOB
@ ASCII_INSTR_SOURCE_PRINT_SELECTOR_FILE_GLOB
@ ASCII_INSTR_SOURCE_PRINT_SELECTOR_FILE_SUBSTRING
@ ASCII_INSTR_SOURCE_PRINT_SELECTOR_MODULE
enum asciichat_instr_selector_type asciichat_instr_selector_type_t

Function Documentation

◆ asciichat_instr_coverage_enabled()

bool asciichat_instr_coverage_enabled ( void  )

Definition at line 434 of file instrument_log.c.

434 {
435 if (g_disable_write) {
436 return false;
437 }
438
439 // Initialize runtime once using mutex-protected initialization
440 if (!g_runtime_initialized) {
441 if (!g_runtime_mutex_initialized) {
442 mutex_init(&g_runtime_mutex);
443 g_runtime_mutex_initialized = true;
444 }
445
446 mutex_lock(&g_runtime_mutex);
447 if (!g_runtime_initialized) {
448 asciichat_instr_runtime_init_once();
449 }
450 mutex_unlock(&g_runtime_mutex);
451 }
452
453 return g_coverage_enabled;
454}
int mutex_init(mutex_t *mutex)
Definition threading.c:16

References mutex_init().

Referenced by asciichat_instr_log_pc().

◆ asciichat_instr_log_line()

void asciichat_instr_log_line ( const char *  file_path,
uint32_t  line_number,
const char *  function_name,
const char *  snippet,
uint8_t  is_macro_expansion 
)

Definition at line 284 of file instrument_log.c.

285 {
286 // Instrumentation is enabled by default when the binary is built with Source Print.
287 // Set ASCII_INSTR_SOURCE_PRINT_ENABLE=0 to disable tracing at runtime.
288 if (!g_instrumentation_enabled_checked) {
289 const char *enable_env = SAFE_GETENV("ASCII_INSTR_SOURCE_PRINT_ENABLE");
290 // Default to enabled (true) - only disable if explicitly set to "0", "false", "off", or "no"
291 if (enable_env != NULL && enable_env[0] != '\0') {
292 g_instrumentation_enabled = asciichat_instr_env_is_enabled(enable_env);
293 } else {
294 g_instrumentation_enabled = true; // Default to enabled when instrumented
295 }
296 g_instrumentation_enabled_checked = true;
297 }
298
299 if (!g_instrumentation_enabled) {
300 return;
301 }
302
303 if (g_disable_write) {
304 return;
305 }
306
307 if (g_logging_reentry_guard) {
308 return;
309 }
310
311 g_logging_reentry_guard = true;
312
314 if (runtime == NULL) {
315 goto cleanup;
316 }
317
318 if (!asciichat_instr_should_log(runtime, file_path, line_number, function_name)) {
319 goto cleanup;
320 }
321
322 runtime->call_counter++;
323 if (runtime->rate_enabled) {
324 const uint64_t counter = runtime->call_counter;
325 if (((counter - 1U) % runtime->rate) != 0U) {
326 goto cleanup;
327 }
328 }
329
330 if (runtime->fd < 0) {
331 if (asciichat_instr_open_log_file(runtime) != 0) {
332 runtime->stderr_fallback = true;
333 }
334 }
335
336 const int fd = runtime->stderr_fallback ? STDERR_FILENO : runtime->fd;
338 size_t pos = 0;
339
340 uint64_t realtime_ns = time_get_realtime_ns();
341 time_t sec = (time_t)(realtime_ns / NS_PER_SEC_INT);
342 long nsec = (long)(realtime_ns % NS_PER_SEC_INT);
343 struct tm tm_now;
344 memset(&tm_now, 0, sizeof(tm_now));
345 if (platform_gtime(&sec, &tm_now) != ASCIICHAT_OK) {
346 memset(&tm_now, 0, sizeof(tm_now));
347 }
348
349 char timestamp[32];
350 size_t ts_len = strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S", &tm_now);
351 if (ts_len == 0) {
352 SAFE_STRNCPY(timestamp, "1970-01-01T00:00:00", sizeof(timestamp));
353 ts_len = strlen(timestamp);
354 }
355
356 char elapsed_buf[32];
357 elapsed_buf[0] = '\0';
358 if (g_ticks_initialized) {
359 uint64_t now_ns = time_get_ns();
360 uint64_t elapsed_ns = time_elapsed_ns(g_start_ns, now_ns);
361 if (format_duration_ns((double)elapsed_ns, elapsed_buf, sizeof(elapsed_buf)) < 0) {
362 elapsed_buf[0] = '\0';
363 }
364 }
365
366 runtime->sequence++;
367
368 const char *elapsed_field = (elapsed_buf[0] != '\0') ? elapsed_buf : "-";
369
370 const char *safe_file_path = (file_path != NULL) ? file_path : "<unknown>";
371 pos += safe_snprintf(buffer + pos, sizeof(buffer) - pos,
372 "pid=%d tid=%llu seq=%llu ts=%.*s.%09ldZ elapsed=%s file=%s line=%u func=%s macro=%u snippet=",
373 runtime->pid, (unsigned long long)runtime->thread_id, (unsigned long long)runtime->sequence,
374 (int)ts_len, timestamp, nsec, elapsed_field, safe_file_path, line_number,
375 function_name ? function_name : "<unknown>", (unsigned)is_macro_expansion);
376
377 if (snippet != NULL) {
378 size_t snippet_len = strnlen(snippet, ASCII_INSTR_SOURCE_PRINT_MAX_SNIPPET);
379 for (size_t i = 0; i < snippet_len && pos < sizeof(buffer) - 2; ++i) {
380 const char ch = snippet[i];
381 switch (ch) {
382 case '\n':
383 buffer[pos++] = '\\';
384 buffer[pos++] = 'n';
385 break;
386 case '\r':
387 buffer[pos++] = '\\';
388 buffer[pos++] = 'r';
389 break;
390 case '\t':
391 buffer[pos++] = '\\';
392 buffer[pos++] = 't';
393 break;
394 default:
395 buffer[pos++] = ch;
396 break;
397 }
398 }
399 }
400
401 if (pos >= sizeof(buffer) - 1) {
402 pos = sizeof(buffer) - 2;
403 }
404
405 buffer[pos++] = '\n';
406 buffer[pos] = '\0';
407
408 asciichat_instr_write_full(fd, buffer, pos);
409
410 // Also echo to stderr if requested via environment variable
411 if (!g_echo_to_stderr_initialized) {
412 const char *echo_env = SAFE_GETENV("ASCII_CHAT_DEBUG_SELF_SOURCE_CODE_LOG_STDERR");
413 g_echo_to_stderr = asciichat_instr_env_is_enabled(echo_env);
414 g_echo_to_stderr_initialized = true;
415 }
416
417 if (g_echo_to_stderr && !runtime->stderr_fallback) {
418 // Write to stderr in addition to the log file
419 // Suppress Windows deprecation warning for POSIX write function
420#ifdef _WIN32
421#pragma clang diagnostic push
422#pragma clang diagnostic ignored "-Wdeprecated-declarations"
423#endif
424 (void)posix_write(STDERR_FILENO, buffer, pos);
425#ifdef _WIN32
426#pragma clang diagnostic pop
427#endif
428 }
429
430cleanup:
431 g_logging_reentry_guard = false;
432}
#define posix_write
#define ASCII_INSTR_SOURCE_PRINT_MAX_SNIPPET
#define ASCII_INSTR_SOURCE_PRINT_MAX_LINE
asciichat_instr_runtime_t * asciichat_instr_runtime_get(void)
char file_path[PLATFORM_MAX_PATH_LENGTH]
Definition mmap.c:39
Per-thread instrumentation runtime state.
uint64_t call_counter
Total number of instrumentation calls.
bool rate_enabled
Whether rate limiting is enabled.
uint64_t sequence
Sequence number for log entries.
uint32_t rate
Rate limiting: log every Nth call.
int fd
Log file descriptor (-1 if not open)
uint64_t thread_id
Thread ID.
bool stderr_fallback
Use stderr if log file can't be opened.
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:456
uint64_t time_get_ns(void)
Definition util/time.c:48
uint64_t time_get_realtime_ns(void)
Definition util/time.c:59
int format_duration_ns(double nanoseconds, char *buffer, size_t buffer_size)
Definition util/time.c:275
uint64_t time_elapsed_ns(uint64_t start_ns, uint64_t end_ns)
Definition util/time.c:90

References ASCII_INSTR_SOURCE_PRINT_MAX_LINE, ASCII_INSTR_SOURCE_PRINT_MAX_SNIPPET, asciichat_instr_runtime_get(), asciichat_instr_runtime::call_counter, asciichat_instr_runtime::fd, file_path, format_duration_ns(), asciichat_instr_runtime::pid, posix_write, asciichat_instr_runtime::rate, asciichat_instr_runtime::rate_enabled, safe_snprintf(), asciichat_instr_runtime::sequence, asciichat_instr_runtime::stderr_fallback, asciichat_instr_runtime::thread_id, time_elapsed_ns(), time_get_ns(), and time_get_realtime_ns().

Referenced by asciichat_instr_log_pc().

◆ asciichat_instr_log_pc()

void asciichat_instr_log_pc ( uintptr_t  program_counter)

Definition at line 456 of file instrument_log.c.

456 {
458 return;
459 }
460
461 char snippet[64];
462 safe_snprintf(snippet, sizeof(snippet), "pc=0x%zx", (size_t)program_counter);
463 asciichat_instr_log_line("__coverage__", 0, "<coverage>", snippet, ASCII_INSTR_SOURCE_PRINT_MACRO_NONE);
464}
bool asciichat_instr_coverage_enabled(void)
void asciichat_instr_log_line(const char *file_path, uint32_t line_number, const char *function_name, const char *snippet, uint8_t is_macro_expansion)

References asciichat_instr_coverage_enabled(), asciichat_instr_log_line(), and safe_snprintf().

Referenced by __sanitizer_cov_trace_pc_guard().

◆ asciichat_instr_runtime_destroy()

void asciichat_instr_runtime_destroy ( asciichat_instr_runtime_t runtime)

Definition at line 221 of file instrument_log.c.

221 {
222 if (runtime == NULL) {
223 return;
224 }
225
226 if (runtime->fd >= 0) {
227 platform_close(runtime->fd);
228 runtime->fd = -1;
229 }
230
231#if ASCII_INSTR_SOURCE_PRINT_HAVE_REGEX
232 if (runtime->include_regex_valid) {
233 regfree(&runtime->include_regex);
234 runtime->include_regex_valid = false;
235 }
236 if (runtime->exclude_regex_valid) {
237 regfree(&runtime->exclude_regex);
238 runtime->exclude_regex_valid = false;
239 }
240 if (runtime->function_include_regex_valid) {
241 regfree(&runtime->function_include_regex);
242 runtime->function_include_regex_valid = false;
243 }
244 if (runtime->function_exclude_regex_valid) {
245 regfree(&runtime->function_exclude_regex);
246 runtime->function_exclude_regex_valid = false;
247 }
248#endif
249
250 asciichat_instr_only_list_destroy(&runtime->only_selectors);
251 SAFE_FREE(runtime);
252}
bool function_exclude_regex_valid
Whether function_exclude_regex was successfully compiled.
asciichat_instr_only_list_t only_selectors
List of "only" selectors for filtering.
regex_t function_exclude_regex
Compiled regex for function name exclusion.
regex_t exclude_regex
Compiled regex for file path exclusion.
bool function_include_regex_valid
Whether function_include_regex was successfully compiled.
regex_t include_regex
Compiled regex for file path inclusion.
regex_t function_include_regex
Compiled regex for function name inclusion.
bool exclude_regex_valid
Whether exclude_regex was successfully compiled.
bool include_regex_valid
Whether include_regex was successfully compiled.
int platform_close(int fd)

References asciichat_instr_runtime::exclude_regex, asciichat_instr_runtime::exclude_regex_valid, asciichat_instr_runtime::fd, asciichat_instr_runtime::function_exclude_regex, asciichat_instr_runtime::function_exclude_regex_valid, asciichat_instr_runtime::function_include_regex, asciichat_instr_runtime::function_include_regex_valid, asciichat_instr_runtime::include_regex, asciichat_instr_runtime::include_regex_valid, asciichat_instr_runtime::only_selectors, and platform_close().

◆ asciichat_instr_runtime_get()

asciichat_instr_runtime_t * asciichat_instr_runtime_get ( void  )

Definition at line 167 of file instrument_log.c.

167 {
168 if (g_disable_write) {
169 return NULL;
170 }
171
172 // Initialize runtime once using mutex-protected initialization
173 if (!g_runtime_initialized) {
174 // Initialize mutex if needed
175 if (!g_runtime_mutex_initialized) {
176 mutex_init(&g_runtime_mutex);
177 g_runtime_mutex_initialized = true;
178 }
179
180 mutex_lock(&g_runtime_mutex);
181 if (!g_runtime_initialized) {
182 asciichat_instr_runtime_init_once();
183 }
184 mutex_unlock(&g_runtime_mutex);
185 }
186
187 asciichat_instr_runtime_t *runtime = ascii_tls_get(g_runtime_key);
188 if (runtime != NULL) {
189 return runtime;
190 }
191
192 runtime = SAFE_CALLOC(1, sizeof(*runtime), asciichat_instr_runtime_t *);
193 if (runtime == NULL) {
194 return NULL;
195 }
196
197 runtime->pid = platform_get_pid();
199 runtime->sequence = 0;
200 runtime->call_counter = 0;
201 runtime->fd = -1;
202 runtime->filter_include = SAFE_GETENV("ASCII_INSTR_SOURCE_PRINT_INCLUDE");
203 runtime->filter_exclude = SAFE_GETENV("ASCII_INSTR_SOURCE_PRINT_EXCLUDE");
204 runtime->filter_thread = SAFE_GETENV("ASCII_INSTR_SOURCE_PRINT_THREAD");
205 runtime->filter_function_include = NULL;
206 runtime->filter_function_exclude = NULL;
207 runtime->filters_enabled = false;
208 runtime->rate = 1;
209 runtime->rate_enabled = false;
210
211 asciichat_instr_runtime_configure(runtime);
212
213 if (ascii_tls_set(g_runtime_key, runtime) != 0) {
214 SAFE_FREE(runtime);
215 return NULL;
216 }
217
218 return runtime;
219}
const char * filter_exclude
File path substring to exclude (from env var)
const char * filter_include
File path substring to include (from env var)
const char * filter_function_exclude
Function name substring to exclude (from env var)
const char * filter_function_include
Function name substring to include (from env var)
const char * filter_thread
Thread ID filter (from env var)
bool filters_enabled
Whether any filters are active.
int ascii_tls_set(tls_key_t key, void *value)
Definition threading.c:101
void * ascii_tls_get(tls_key_t key)
Definition threading.c:97
uint64_t asciichat_thread_current_id(void)
Definition threading.c:84
pid_t platform_get_pid(void)
Definition wasm/system.c:35

References ascii_tls_get(), ascii_tls_set(), asciichat_thread_current_id(), asciichat_instr_runtime::call_counter, asciichat_instr_runtime::fd, asciichat_instr_runtime::filter_exclude, asciichat_instr_runtime::filter_function_exclude, asciichat_instr_runtime::filter_function_include, asciichat_instr_runtime::filter_include, asciichat_instr_runtime::filter_thread, asciichat_instr_runtime::filters_enabled, mutex_init(), asciichat_instr_runtime::pid, platform_get_pid(), asciichat_instr_runtime::rate, asciichat_instr_runtime::rate_enabled, asciichat_instr_runtime::sequence, and asciichat_instr_runtime::thread_id.

Referenced by asciichat_instr_log_line().

◆ asciichat_instr_runtime_global_destroy()

void asciichat_instr_runtime_global_destroy ( void  )

Definition at line 254 of file instrument_log.c.

254 {
255 if (g_runtime_mutex_initialized) {
256 mutex_lock(&g_runtime_mutex);
257 }
258
259 if (g_runtime_initialized) {
260 g_disable_write = true;
261 ascii_tls_key_delete(g_runtime_key);
262 g_runtime_initialized = false;
263 g_ticks_initialized = false;
264 g_start_ns = 0;
265 g_coverage_enabled = false;
266 g_output_dir_set = false;
267 g_output_dir[0] = '\0';
268 g_instrumentation_enabled_checked = false;
269 g_instrumentation_enabled = false;
270 g_echo_to_stderr_initialized = false;
271 g_echo_to_stderr = false;
272 }
273
274 // Reset g_disable_write so instrumentation can be re-enabled in subsequent tests
275 g_disable_write = false;
276
277 if (g_runtime_mutex_initialized) {
278 mutex_unlock(&g_runtime_mutex);
279 mutex_destroy(&g_runtime_mutex);
280 g_runtime_mutex_initialized = false;
281 }
282}
int ascii_tls_key_delete(tls_key_t key)
Definition threading.c:93
int mutex_destroy(mutex_t *mutex)
Definition threading.c:21

References ascii_tls_key_delete(), and mutex_destroy().