# CallTrace - 実装仕様書
## 1. モジュール別実装仕様
### 1.1 Core Module (`src/core/`)
#### 1.1.1 calltrace.c - メインAPI
```c
// グローバル状態管理
static calltrace_state_t g_state = {
.initialized = false,
.active = false,
.mutex = PTHREAD_MUTEX_INITIALIZER,
.filter_count = 0
};
// 初期化関数実装
calltrace_error_t calltrace_init(const calltrace_config_t* config) {
pthread_mutex_lock(&g_state.mutex);
if (g_state.initialized) {
pthread_mutex_unlock(&g_state.mutex);
return CALLTRACE_ERROR_INIT;
}
// 設定コピー
if (config) {
memcpy(&g_state.config, config, sizeof(calltrace_config_t));
} else {
load_default_config(&g_state.config);
}
// 出力ストリーム初期化
if (g_state.config.output_file) {
g_state.output_stream = fopen(g_state.config.output_file, "w");
if (!g_state.output_stream) {
pthread_mutex_unlock(&g_state.mutex);
return CALLTRACE_ERROR_IO;
}
} else {
g_state.output_stream = stdout;
}
// JSON出力ヘッダー書き込み
calltrace_json_write_header(g_state.output_stream);
g_state.initialized = true;
pthread_mutex_unlock(&g_state.mutex);
return CALLTRACE_OK;
}
```
#### 1.1.2 hooks.c - 関数フック機構
```c
// 関数ポインタテーブル
typedef struct {
const char* name;
void** original_ptr;
void* hook_ptr;
} hook_entry_t;
// 動的フック登録
static hook_entry_t* hook_table = NULL;
static size_t hook_count = 0;
static size_t hook_capacity = 0;
calltrace_error_t register_hook(const char* func_name, void* hook_func) {
// テーブル拡張チェック
if (hook_count >= hook_capacity) {
size_t new_capacity = hook_capacity ? hook_capacity * 2 : 16;
hook_entry_t* new_table = realloc(hook_table,
new_capacity * sizeof(hook_entry_t));
if (!new_table) return CALLTRACE_ERROR_MEMORY;
hook_table = new_table;
hook_capacity = new_capacity;
}
// オリジナル関数ポインタ取得
void* original = dlsym(RTLD_NEXT, func_name);
if (!original) {
original = dlsym(RTLD_DEFAULT, func_name);
}
hook_table[hook_count] = (hook_entry_t){
.name = strdup(func_name),
.original_ptr = &original,
.hook_ptr = hook_func
};
hook_count++;
return CALLTRACE_OK;
}
// 関数エントリーフック
void calltrace_function_entry(const char* func_name, void* func_addr) {
if (!calltrace_should_trace(func_name)) return;
thread_context_t* ctx = calltrace_get_thread_context();
if (!ctx || ctx->current_depth >= g_state.config.max_depth) return;
// 呼び出し情報作成
calltrace_call_info_t call_info = {
.function_name = func_name,
.address = func_addr,
.timestamp = calltrace_get_timestamp(),
.thread_id = pthread_self(),
.depth = ctx->current_depth
};
// ライブラリ名解決
Dl_info dl_info;
if (dladdr(func_addr, &dl_info)) {
call_info.library_name = dl_info.dli_fname;
}
// スタック管理
if (ctx->current_depth < MAX_CALL_DEPTH) {
strncpy(ctx->call_stack[ctx->current_depth], func_name,
MAX_FUNCTION_NAME_LEN - 1);
ctx->call_timestamps[ctx->current_depth] = call_info.timestamp;
ctx->current_depth++;
}
// JSON出力
calltrace_json_write_call(g_state.output_stream, &call_info);
// コールバック実行
if (g_state.callback) {
g_state.callback(&call_info);
}
}
```
#### 1.1.3 json_output.c - JSON出力エンジン
```c
// JSON出力状態管理
typedef struct {
size_t indent_level;
bool first_call;
bool in_thread_array;
bool in_calls_array;
pthread_mutex_t output_mutex;
} json_state_t;
static json_state_t json_state = {
.indent_level = 0,
.first_call = true,
.output_mutex = PTHREAD_MUTEX_INITIALIZER
};
// インデント出力
static void write_indent(FILE* stream, size_t level) {
for (size_t i = 0; i < level; i++) {
fprintf(stream, " ");
}
}
// JSON文字列エスケープ
static void write_escaped_string(FILE* stream, const char* str) {
fputc('"', stream);
for (const char* p = str; *p; p++) {
switch (*p) {
case '"': fputs("\\\"", stream); break;
case '\\': fputs("\\\\", stream); break;
case '\n': fputs("\\n", stream); break;
case '\r': fputs("\\r", stream); break;
case '\t': fputs("\\t", stream); break;
default: fputc(*p, stream); break;
}
}
fputc('"', stream);
}
// 呼び出し情報JSON出力
calltrace_error_t calltrace_json_write_call(FILE* stream,
const calltrace_call_info_t* call_info) {
pthread_mutex_lock(&json_state.output_mutex);
if (!json_state.first_call) {
fprintf(stream, ",\n");
} else {
json_state.first_call = false;
}
write_indent(stream, call_info->depth + 2);
fprintf(stream, "{\n");
write_indent(stream, call_info->depth + 3);
fprintf(stream, "\"function\": ");
write_escaped_string(stream, call_info->function_name);
fprintf(stream, ",\n");
if (call_info->library_name) {
write_indent(stream, call_info->depth + 3);
fprintf(stream, "\"library\": ");
write_escaped_string(stream, call_info->library_name);
fprintf(stream, ",\n");
}
write_indent(stream, call_info->depth + 3);
fprintf(stream, "\"address\": \"0x%lx\",\n",
(unsigned long)call_info->address);
write_indent(stream, call_info->depth + 3);
fprintf(stream, "\"timestamp\": %lu,\n", call_info->timestamp);
write_indent(stream, call_info->depth + 3);
fprintf(stream, "\"thread_id\": %lu,\n", call_info->thread_id);
write_indent(stream, call_info->depth + 3);
fprintf(stream, "\"depth\": %zu\n", call_info->depth);
write_indent(stream, call_info->depth + 2);
fprintf(stream, "}");
fflush(stream);
pthread_mutex_unlock(&json_state.output_mutex);
return CALLTRACE_OK;
}
```
### 1.2 Utils Module (`src/utils/`)
#### 1.2.1 symbols.c - シンボル解決
```c
// シンボルキャッシュ
typedef struct symbol_cache_entry {
void* address;
char* symbol_name;
char* library_name;
struct symbol_cache_entry* next;
} symbol_cache_entry_t;
#define SYMBOL_CACHE_SIZE 1024
static symbol_cache_entry_t* symbol_cache[SYMBOL_CACHE_SIZE];
static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
// ハッシュ関数
static size_t hash_address(void* addr) {
uintptr_t ptr = (uintptr_t)addr;
return (ptr >> 4) % SYMBOL_CACHE_SIZE;
}
// シンボル名解決(キャッシュ付き)
const char* calltrace_resolve_symbol(void* addr) {
pthread_mutex_lock(&cache_mutex);
size_t hash = hash_address(addr);
symbol_cache_entry_t* entry = symbol_cache[hash];
// キャッシュ検索
while (entry) {
if (entry->address == addr) {
pthread_mutex_unlock(&cache_mutex);
return entry->symbol_name;
}
entry = entry->next;
}
// キャッシュミス - 新規解決
Dl_info dl_info;
if (!dladdr(addr, &dl_info) || !dl_info.dli_sname) {
pthread_mutex_unlock(&cache_mutex);
return "<unknown>";
}
// 新しいエントリーをキャッシュに追加
symbol_cache_entry_t* new_entry = malloc(sizeof(symbol_cache_entry_t));
if (new_entry) {
new_entry->address = addr;
new_entry->symbol_name = strdup(dl_info.dli_sname);
new_entry->library_name = strdup(dl_info.dli_fname);
new_entry->next = symbol_cache[hash];
symbol_cache[hash] = new_entry;
}
pthread_mutex_unlock(&cache_mutex);
return new_entry ? new_entry->symbol_name : "<unknown>";
}
```
#### 1.2.2 filters.c - フィルタエンジン
```c
// フィルタパターン
typedef struct filter_pattern {
char* pattern;
bool is_regex;
regex_t regex;
bool include; // true=include, false=exclude
struct filter_pattern* next;
} filter_pattern_t;
static filter_pattern_t* filter_list = NULL;
static pthread_mutex_t filter_mutex = PTHREAD_MUTEX_INITIALIZER;
// ワイルドカードマッチング
static bool wildcard_match(const char* pattern, const char* text) {
if (*pattern == '\0') return *text == '\0';
if (*pattern == '*') {
while (*text) {
if (wildcard_match(pattern + 1, text)) return true;
text++;
}
return wildcard_match(pattern + 1, text);
}
if (*text != '\0' && (*pattern == *text || *pattern == '?')) {
return wildcard_match(pattern + 1, text + 1);
}
return false;
}
// フィルタマッチング
bool calltrace_should_trace(const char* func_name) {
if (!func_name) return false;
pthread_mutex_lock(&filter_mutex);
bool should_trace = true; // デフォルトはトレース有効
filter_pattern_t* pattern = filter_list;
while (pattern) {
bool matches = false;
if (pattern->is_regex) {
matches = (regexec(&pattern->regex, func_name, 0, NULL, 0) == 0);
} else {
matches = wildcard_match(pattern->pattern, func_name);
}
if (matches) {
should_trace = pattern->include;
break; // 最初にマッチしたルールを適用
}
pattern = pattern->next;
}
pthread_mutex_unlock(&filter_mutex);
return should_trace;
}
// フィルタ追加
calltrace_error_t calltrace_add_filter(const char* pattern) {
if (!pattern) return CALLTRACE_ERROR_CONFIG;
filter_pattern_t* new_filter = malloc(sizeof(filter_pattern_t));
if (!new_filter) return CALLTRACE_ERROR_MEMORY;
new_filter->pattern = strdup(pattern);
new_filter->include = (pattern[0] != '!'); // '!'で始まる場合は除外
new_filter->is_regex = (strchr(pattern, '[') || strchr(pattern, '^') ||
strchr(pattern, '$'));
if (new_filter->is_regex) {
int result = regcomp(&new_filter->regex, pattern, REG_EXTENDED);
if (result != 0) {
free(new_filter->pattern);
free(new_filter);
return CALLTRACE_ERROR_CONFIG;
}
}
pthread_mutex_lock(&filter_mutex);
new_filter->next = filter_list;
filter_list = new_filter;
pthread_mutex_unlock(&filter_mutex);
return CALLTRACE_OK;
}
```
#### 1.2.3 threads.c - スレッド管理
```c
// スレッドコンテキストテーブル
static thread_context_t thread_table[MAX_THREADS];
static bool thread_slots[MAX_THREADS] = {false};
static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER;
static __thread int tls_context_index = -1;
// スレッドコンテキスト取得
thread_context_t* calltrace_get_thread_context(void) {
if (tls_context_index >= 0) {
return &thread_table[tls_context_index];
}
pthread_mutex_lock(&thread_mutex);
// 空きスロット検索
int free_slot = -1;
for (int i = 0; i < MAX_THREADS; i++) {
if (!thread_slots[i]) {
free_slot = i;
break;
}
}
if (free_slot < 0) {
pthread_mutex_unlock(&thread_mutex);
return NULL; // スロット不足
}
// 新しいコンテキスト初期化
thread_context_t* ctx = &thread_table[free_slot];
memset(ctx, 0, sizeof(thread_context_t));
ctx->thread_id = pthread_self();
ctx->tracing_enabled = true;
thread_slots[free_slot] = true;
tls_context_index = free_slot;
pthread_mutex_unlock(&thread_mutex);
return ctx;
}
// スレッド終了時クリーンアップ
__attribute__((destructor))
static void cleanup_thread_context(void) {
if (tls_context_index >= 0) {
pthread_mutex_lock(&thread_mutex);
thread_slots[tls_context_index] = false;
memset(&thread_table[tls_context_index], 0, sizeof(thread_context_t));
pthread_mutex_unlock(&thread_mutex);
}
}
```
### 1.3 Command Line Tools (`src/utils/`)
#### 1.3.1 ctrace.c - メインコマンドラインツール
```c
int main(int argc, char* argv[]) {
// コマンドライン引数解析
calltrace_config_t config = {0};
char* target_program = NULL;
char** target_args = NULL;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) {
config.output_file = argv[++i];
} else if (strcmp(argv[i], "-f") == 0 && i + 1 < argc) {
config.filter_file = argv[++i];
} else if (strcmp(argv[i], "-d") == 0 && i + 1 < argc) {
config.max_depth = atoi(argv[++i]);
} else if (strcmp(argv[i], "--") == 0) {
target_program = argv[++i];
target_args = &argv[i];
break;
}
}
if (!target_program) {
fprintf(stderr, "Usage: ctrace [options] -- program [args...]\n");
return 1;
}
// LD_PRELOAD環境変数設定
char* libpath = getenv("CALLTRACE_LIB_PATH");
if (!libpath) {
libpath = "/usr/local/lib/libcalltrace.so";
}
setenv("LD_PRELOAD", libpath, 1);
// 設定情報を環境変数で渡す
if (config.output_file) {
setenv("CALLTRACE_OUTPUT", config.output_file, 1);
}
if (config.filter_file) {
setenv("CALLTRACE_FILTER_FILE", config.filter_file, 1);
}
if (config.max_depth > 0) {
char depth_str[32];
snprintf(depth_str, sizeof(depth_str), "%zu", config.max_depth);
setenv("CALLTRACE_MAX_DEPTH", depth_str, 1);
}
// 対象プログラム実行
execvp(target_program, target_args);
perror("execvp failed");
return 1;
}
```
## 2. パフォーマンス実装詳細
### 2.1 メモリプール実装
```c
typedef struct memory_pool {
void* memory;
size_t block_size;
size_t block_count;
size_t next_free;
uint8_t* allocation_bitmap;
pthread_mutex_t mutex;
} memory_pool_t;
static memory_pool_t call_info_pool;
calltrace_error_t init_memory_pools(void) {
call_info_pool.block_size = sizeof(calltrace_call_info_t);
call_info_pool.block_count = 10000;
call_info_pool.memory = mmap(NULL,
call_info_pool.block_size * call_info_pool.block_count,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (call_info_pool.memory == MAP_FAILED) {
return CALLTRACE_ERROR_MEMORY;
}
size_t bitmap_size = (call_info_pool.block_count + 7) / 8;
call_info_pool.allocation_bitmap = calloc(bitmap_size, 1);
pthread_mutex_init(&call_info_pool.mutex, NULL);
return CALLTRACE_OK;
}
```
### 2.2 ロックフリーバッファ実装
```c
typedef struct lockfree_ring_buffer {
volatile size_t head;
volatile size_t tail;
size_t capacity;
call_info_t* buffer;
} lockfree_ring_buffer_t;
bool ring_buffer_push(lockfree_ring_buffer_t* rb, const call_info_t* item) {
size_t head = __atomic_load_n(&rb->head, __ATOMIC_ACQUIRE);
size_t next_head = (head + 1) % rb->capacity;
if (next_head == __atomic_load_n(&rb->tail, __ATOMIC_ACQUIRE)) {
return false; // バッファフル
}
rb->buffer[head] = *item;
__atomic_store_n(&rb->head, next_head, __ATOMIC_RELEASE);
return true;
}
```
この実装仕様により、高性能で信頼性の高いコールトレースライブラリの実現を目指します。