#include "trace.h" #include #include #include #include #include #include #if !defined(_countof) #define _countof(a) ((int)(sizeof((a)) / sizeof(*(a)))) #endif enum { k_buffer_size = 4 * 1024 * 1024, }; typedef struct _tf_trace_stack_t tf_trace_stack_t; typedef struct _tf_trace_stack_t { const char* names[256]; int count; tf_trace_stack_t* next; } tf_trace_stack_t; typedef struct _tf_trace_t { char buffer[k_buffer_size]; int write_offset; tf_trace_stack_t* stack; } tf_trace_t; tf_trace_t* tf_trace_create() { tf_trace_t* trace = malloc(sizeof(tf_trace_t)); memset(trace, 0, sizeof(*trace)); return trace; } void tf_trace_destroy(tf_trace_t* trace) { while (trace->stack) { tf_trace_stack_t* stack = trace->stack; trace->stack = stack->next; free(stack); } free(trace); } static int64_t _trace_ts() { int64_t result = 0; struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { result = (ts.tv_sec * 1e9 + ts.tv_nsec) / 1e3; } return result; } static void _trace_append(tf_trace_t* trace, const char* buffer, size_t size) { if (trace->write_offset + size >= k_buffer_size) { trace->buffer[trace->write_offset] = '\0'; trace->write_offset = 0; } if (trace->write_offset + size < k_buffer_size) { memcpy(trace->buffer + trace->write_offset, buffer, size); trace->write_offset += size; trace->buffer[trace->write_offset++] = '\n'; } } void tf_trace_counter(tf_trace_t* trace, const char* name, int argc, const char** arg_names, const int64_t* arg_values) { if (!trace) { return; } char line[1024]; int p = 0; p += snprintf(line + p, sizeof(line) - p, "{\"ph\": \"C\", \"pid\": %d, \"ts\": %" PRId64 ", \"name\": \"%s\", \"args\": {", getpid(), _trace_ts(), name); for (int i = 0; i < argc; i++) { p += snprintf(line + p, sizeof(line) - p, "\"%s\": %" PRId64 "%s", arg_names[i], arg_values[i], i == argc - 1 ? "}}," : ", "); } _trace_append(trace, line, p); } void tf_trace_begin(tf_trace_t* trace, const char* name) { if (!trace) { return; } if (!trace->stack || trace->stack->count + 1 > _countof(trace->stack->names)) { tf_trace_stack_t* stack = malloc(sizeof(tf_trace_stack_t)); memset(stack, 0, sizeof(*stack)); stack->next = trace->stack; trace->stack = stack; } tf_trace_stack_t* stack = trace->stack; while (stack->count == 0 && stack->next && stack->next->count + 1 <= _countof(trace->stack->names)) { stack = stack->next; } stack->names[stack->count++] = name; char line[1024]; int p = snprintf(line, sizeof(line), "{\"ph\": \"B\", \"pid\": %d, \"tid\": 0, \"ts\": %" PRId64 ", \"name\": \"", getpid(), _trace_ts()); for (const char* c = name; *c && p < (int)sizeof(line); c++) { switch (*c) { case '"': case '\\': line[p++] = '\\'; if (p < (int)sizeof(line)) { line[p++] = *c; } break; default: line[p++] = *c; break; } } p += snprintf(line + p, sizeof(line) - p, "\"},"); _trace_append(trace, line, p); } void tf_trace_end(tf_trace_t* trace) { if (!trace) { return; } tf_trace_stack_t* stack = trace->stack; while (stack && stack->count == 0) { stack = stack->next; } const char* name = stack && stack->count > 0 ? stack->names[stack->count - 1] : NULL; char line[1024]; int p = snprintf(line, sizeof(line), "{\"ph\": \"E\", \"pid\": %d, \"tid\": 0, \"ts\": %" PRId64 ", \"name\": \"", getpid(), _trace_ts()); for (const char* c = name; *c && p < (int)sizeof(line); c++) { switch (*c) { case '"': case '\\': line[p++] = '\\'; if (p < (int)sizeof(line)) { line[p++] = *c; } break; default: line[p++] = *c; break; } } p += snprintf(line + p, sizeof(line) - p, "\"},"); _trace_append(trace, line, p); } char* tf_trace_export(tf_trace_t* trace) { if (!trace) { return NULL; } static const int k_extra_size = 1024; char* buffer = malloc(k_buffer_size + k_extra_size); const char* newline = strchr(trace->buffer + trace->write_offset, '\n'); int begin = newline ? newline - trace->buffer : 0; size_t size = 0; size += snprintf(buffer, k_buffer_size, "{\"displayTimeUnit\": \"ns\",\n\"traceEvents\": [\n"); if (begin) { size_t this_size = strlen(trace->buffer + begin); memcpy(buffer + size, trace->buffer + begin, this_size); size += this_size; } memcpy(buffer + size, trace->buffer, trace->write_offset); size += trace->write_offset; if (size > 2 && buffer[size - 1] == '\n' && buffer[size - 2] == ',') { buffer[size - 2] = '\n'; size--; } size += snprintf(buffer + size, k_buffer_size - size, "]}\n"); buffer[size] = '\0'; assert(size < (size_t)k_buffer_size + k_extra_size); return buffer; } static int _tf_trace_sqlite_callback(unsigned int t, void* c, void* p, void* x) { tf_trace_t* trace = c; switch (t) { case SQLITE_TRACE_STMT: { const char* statement = x; if (statement[0] != '-' || statement[1] != '-') { tf_trace_begin(trace, statement); } } break; case SQLITE_TRACE_PROFILE: tf_trace_end(trace); break; } return 0; } void tf_trace_sqlite(tf_trace_t* trace, sqlite3* sqlite) { if (sqlite) { sqlite3_trace_v2(sqlite, SQLITE_TRACE_STMT | SQLITE_TRACE_PROFILE, _tf_trace_sqlite_callback, trace); } else { sqlite3_trace_v2(sqlite, 0, NULL, NULL); } }