|
ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
|
🚨 Per-statement logging for crash debugging in C programs More...
Files | |
| file | instrument_log.h |
| 🔍 Debug instrumentation logging runtime for ascii-chat line tracing | |
Macros | |
| #define | ASCII_INSTR_SOURCE_PRINT_SIGNAL_HANDLER |
| Attribute to mark signal handler functions. | |
Typedefs | |
| typedef struct asciichat_instr_runtime | asciichat_instr_runtime_t |
| Opaque runtime context for instrumentation logging. | |
Enumerations | |
| enum | { ASCII_INSTR_SOURCE_PRINT_MACRO_NONE = 0u , ASCII_INSTR_SOURCE_PRINT_MACRO_EXPANSION = 1u , ASCII_INSTR_SOURCE_PRINT_MACRO_INVOCATION = 2u } |
| Macro expansion indicator values for asciichat_instr_log_line() More... | |
Functions | |
| asciichat_instr_runtime_t * | asciichat_instr_runtime_get (void) |
| Get or create the thread-local runtime context. | |
| void | asciichat_instr_runtime_destroy (asciichat_instr_runtime_t *runtime) |
| Destroy a runtime context and release resources. | |
| void | asciichat_instr_runtime_global_shutdown (void) |
| Global shutdown of the instrumentation system. | |
| 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) |
| Log a source line execution event. | |
| bool | asciichat_instr_coverage_enabled (void) |
| Check if coverage logging is enabled. | |
| void | asciichat_instr_log_pc (uintptr_t program_counter) |
| Log a program counter for coverage analysis. | |
🚨 Per-statement logging for crash debugging in C programs
This module provides the runtime logging infrastructure for the source-print instrumentation system. When code is instrumented with ascii-instr-panic, calls to asciichat_instr_log_line() are inserted at each statement to trace execution flow.
ASCII_INSTR_SOURCE_PRINT_ENABLE: Enable/disable tracing (default: enabled)ASCII_INSTR_SOURCE_PRINT_OUTPUT_DIR: Directory for log filesASCII_INSTR_SOURCE_PRINT_INCLUDE: Substring filter for file paths (include)ASCII_INSTR_SOURCE_PRINT_EXCLUDE: Substring filter for file paths (exclude)ASCII_INSTR_SOURCE_PRINT_FUNCTION_INCLUDE: Substring filter for functionsASCII_INSTR_SOURCE_PRINT_FUNCTION_EXCLUDE: Substring filter for functionsASCII_INSTR_SOURCE_PRINT_THREAD: Filter by thread ID(s)ASCII_INSTR_SOURCE_PRINT_RATE: Log every Nth entry (rate limiting)ASCII_INSTR_SOURCE_PRINT_ONLY: Comma-separated selector filtersASCII_INSTR_SOURCE_PRINT_ENABLE_COVERAGE: Enable PC logging for coverageRegex variants (Unix only):
ASCII_INSTR_SOURCE_PRINT_INCLUDE_REGEXASCII_INSTR_SOURCE_PRINT_EXCLUDE_REGEXASCII_INSTR_SOURCE_PRINT_FUNCTION_INCLUDE_REGEXASCII_INSTR_SOURCE_PRINT_FUNCTION_EXCLUDE_REGEXPanic instrumentation provides per-statement logging for debugging crashes in C programs. The core idea is simple: the last line printed before a crash is the crashing line (or the line immediately before it in most cases).
Implementation: src/tooling/panic/tool.cpp
Key Features:
ascii-panic-report toolThe Problem: C programs crash without error messages. When a segfault or other crash occurs, you get no information about which line of code caused it. Traditional debugging approaches have limitations:
The Solution: What if we automatically insert a log before every line of code? When the program crashes, the last line printed tells you exactly where it crashed.
Why This Works:
Why Hasn't This Been Done Before?
Tools like this exist in various forms (SanitizerCoverage, -finstrument-functions, dynamic binary instrumentation), but they typically:
This approach is deliberately simple: print the actual source code text, so the crash location is immediately readable. No symbolization, no post-processing.
With multiple threads, the "last printed line globally" might be from a different thread than the one that crashed. The solution:
ascii-instr-<pid>-<tid>.log)ascii-panic-report to find the last line from each threadUse panic instrumentation when:
The panic instrumentation tool transforms your C source code to print each statement before executing it:
Single-Threaded Programs: The last log line is the crash location.
Multi-Threaded Programs: Each thread writes to its own log file (ascii-instr-<pid>-<tid>.log). Use ascii-panic-report to summarize the last statement executed by each thread.
For multi-threaded programs, ascii-panic-report parses all log files and shows the last executed statement per thread:
Each log line contains:
pid: Process IDtid: Thread IDseq: Sequence number for this threadts: Timestamp (ISO 8601 format)elapsed: Time since runtime initializationfile: Source file pathline: Line numberfunc: Function namemacro: Macro flag (0=normal, 1=from macro expansion, 2=macro invocation)snippet: The source code being executedReduce log volume by filtering what gets logged. Filters are checked before formatting to minimize overhead.
| Variable | Purpose |
|---|---|
ASCII_INSTR_SOURCE_PRINT_INCLUDE | Only log files matching substring |
ASCII_INSTR_SOURCE_PRINT_EXCLUDE | Skip files matching substring |
ASCII_INSTR_SOURCE_PRINT_THREAD | Comma-separated thread IDs to log |
ASCII_INSTR_SOURCE_PRINT_FUNCTION_INCLUDE | Only log functions matching substring |
ASCII_INSTR_SOURCE_PRINT_FUNCTION_EXCLUDE | Skip functions matching substring |
ASCII_INSTR_SOURCE_PRINT_OUTPUT_DIR | Directory for log files |
ASCII_INSTR_SOURCE_PRINT_RATE | Log every Nth statement (throttle) |
ASCII_INSTR_SOURCE_PRINT_ENABLE | Set to "0" to disable at runtime |
| Variable | Purpose |
|---|---|
ASCII_INSTR_SOURCE_PRINT_INCLUDE_REGEX | POSIX regex include filter on file path |
ASCII_INSTR_SOURCE_PRINT_EXCLUDE_REGEX | POSIX regex exclude filter on file path |
ASCII_INSTR_SOURCE_PRINT_FUNCTION_INCLUDE_REGEX | POSIX regex include filter on function name |
ASCII_INSTR_SOURCE_PRINT_FUNCTION_EXCLUDE_REGEX | POSIX regex exclude filter on function name |
The ASCII_INSTR_SOURCE_PRINT_ONLY variable supports sophisticated allow-list filtering using comma-separated selectors:
| Syntax | Description |
|---|---|
file=<glob> | Match files by glob pattern |
func=<glob> | Match functions by glob pattern |
module=<name> | Match files under /name/ directory |
module=<name>:<glob> | Match files under /name/ with basename matching glob |
<name>:<glob> | Shorthand for module=<name>:<glob> |
<substring> | Match files containing substring |
Examples:
For debugging specific issues, you can direct all output to a single file:
Mark functions that must remain async-signal-safe:
The panic instrumentation tool skips any function carrying that annotation, ensuring inserted logging calls never appear inside signal handlers.
build/instrumented/write() call, so concurrent threads cannot interleave recordsASCII_PANIC_THREAD.src/tooling/panic/tool.cpp (Clang libTooling rewriter)lib/tooling/panic/instrument_log.c and .hlib/tooling/panic/instrument_cov.ccmake/tooling/Panic.cmakecmake/tooling/run_panic_instrumentation.shsrc/tooling/panic/report.cbuild/instrumented/| Option | Default | Description |
|---|---|---|
ASCIICHAT_BUILD_WITH_PANIC | OFF | Enable panic instrumentation |
ASCIICHAT_PANIC_TOOL | "" | Path to pre-built tool (faster rebuilds) |
For lightweight edge coverage without full statement logging:
Coverage mode logs compact pc=0x... entries that can be symbolized with llvm-symbolizer or addr2line to map back to source locations.
The ascii-instr-panic tool supports:
| Option | Description |
|---|---|
--output-dir <path> | Where to write instrumented sources |
--input-root <path> | Root for computing relative paths |
--log-macro-expansions | Instrument statements from macro expansions |
--log-macro-invocations | Emit synthetic log for macro call sites |
--filter-file <substring> | Only instrument files matching substring |
--filter-function <substring> | Only instrument functions matching substring |
--signal-handler-annotation <name> | Annotation to skip (default: ASCII_INSTR_PANIC_SIGNAL_HANDLER) |
No logs emitted:
unset ASCII_INSTR_SOURCE_PRINT_INCLUDE)ASCII_INSTR_SOURCE_PRINT_ENABLE isn't set to "0"**"Refusing to overwrite" errors**:
build/instrumented/ directoryPartial log files:
Thread IDs don't match:
| Tool | Approach | Pros | Cons |
|---|---|---|---|
| Panic instrumentation | Source rewriting | Readable output, no symbolization | High overhead |
| SanitizerCoverage | Edge callbacks | Lower overhead | Needs symbolization |
| -finstrument-functions | Function entry/exit | Very low overhead | Function granularity only |
| rr (record/replay) | Deterministic replay | Perfect reproduction | Linux only, heavyweight |
| ASan/UBSan | Memory/UB detection | Catches specific bugs | Not for all crash types |
| gdb/lldb | Debugger | Full control | May perturb timing |
Panic instrumentation is best when you need:
| #define ASCII_INSTR_SOURCE_PRINT_SIGNAL_HANDLER |
#include <instrument_log.h>
Attribute to mark signal handler functions.
Functions marked with this attribute are recognized by the instrumentation tool and may receive special handling to avoid signal-unsafe operations.
Definition at line 76 of file instrument_log.h.
| typedef struct asciichat_instr_runtime asciichat_instr_runtime_t |
#include <instrument_log.h>
Opaque runtime context for instrumentation logging.
Each thread has its own runtime context that tracks:
Definition at line 88 of file instrument_log.h.
| anonymous enum |
#include <instrument_log.h>
Macro expansion indicator values for asciichat_instr_log_line()
These values indicate whether a logged line originates from a macro expansion, the macro invocation site, or regular code.
Definition at line 61 of file instrument_log.h.
| bool asciichat_instr_coverage_enabled | ( | void | ) |
#include <instrument_log.h>
Check if coverage logging is enabled.
Coverage mode logs program counter addresses instead of full source info, useful for generating coverage reports with lower overhead.
Definition at line 425 of file instrument_log.c.
References mutex_init(), mutex_lock, and mutex_unlock.
Referenced by asciichat_instr_log_pc().
| 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 | ||
| ) |
#include <instrument_log.h>
Log a source line execution event.
Called by instrumented code at each statement to record execution trace. The log entry includes timestamp, thread ID, sequence number, file location, function name, and source code snippet.
| file_path | Source file path (typically from FILE) |
| line_number | Line number in the source file |
| function_name | Name of the containing function |
| snippet | Source code snippet for this line |
| is_macro_expansion | One of ASCII_INSTR_SOURCE_PRINT_MACRO_* values |
Definition at line 272 of file instrument_log.c.
References ASCII_INSTR_SOURCE_PRINT_MAX_LINE, ASCII_INSTR_SOURCE_PRINT_MAX_SNIPPET, asciichat_instr_runtime_get(), ASCIICHAT_OK, asciichat_instr_runtime::call_counter, asciichat_instr_runtime::fd, file_path, format_duration_ns(), asciichat_instr_runtime::pid, platform_gtime(), posix_write, asciichat_instr_runtime::rate, asciichat_instr_runtime::rate_enabled, SAFE_GETENV, SAFE_STRNCPY, asciichat_instr_runtime::sequence, asciichat_instr_runtime::stderr_fallback, and asciichat_instr_runtime::thread_id.
Referenced by asciichat_instr_log_pc().
| void asciichat_instr_log_pc | ( | uintptr_t | program_counter | ) |
#include <instrument_log.h>
Log a program counter for coverage analysis.
Records just the PC address with minimal metadata, intended for post-processing into coverage reports.
| program_counter | The instruction address to log |
Definition at line 447 of file instrument_log.c.
References ASCII_INSTR_SOURCE_PRINT_MACRO_NONE, asciichat_instr_coverage_enabled(), and asciichat_instr_log_line().
Referenced by __sanitizer_cov_trace_pc_guard().
| void asciichat_instr_runtime_destroy | ( | asciichat_instr_runtime_t * | runtime | ) |
#include <instrument_log.h>
Destroy a runtime context and release resources.
Closes the log file descriptor, frees compiled regexes, and deallocates the runtime structure. Called automatically by TLS destructor on thread exit.
| runtime | Context to destroy (safe to pass NULL) |
Definition at line 209 of file instrument_log.c.
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, platform_close(), and SAFE_FREE.
| asciichat_instr_runtime_t * asciichat_instr_runtime_get | ( | void | ) |
#include <instrument_log.h>
Get or create the thread-local runtime context.
Returns the instrumentation runtime for the current thread, creating one if it doesn't exist. The runtime is stored in thread-local storage and cleaned up automatically when the thread exits.
Definition at line 155 of file instrument_log.c.
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(), mutex_lock, mutex_unlock, asciichat_instr_runtime::pid, platform_get_pid(), asciichat_instr_runtime::rate, asciichat_instr_runtime::rate_enabled, SAFE_CALLOC, SAFE_FREE, SAFE_GETENV, asciichat_instr_runtime::sequence, and asciichat_instr_runtime::thread_id.
Referenced by asciichat_instr_log_line().
| void asciichat_instr_runtime_global_shutdown | ( | void | ) |
#include <instrument_log.h>
Global shutdown of the instrumentation system.
Disables all logging, cleans up the TLS key, and resets global state. Should be called at program exit to ensure clean shutdown.
Definition at line 242 of file instrument_log.c.
References ascii_tls_key_delete(), mutex_destroy(), mutex_lock, and mutex_unlock.