Skip to content
13 changes: 13 additions & 0 deletions ext/mariadb_profiler/mariadb_profiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ PHP_RINIT_FUNCTION(mariadb_profiler)
profiler_ensure_log_dir(TSRMLS_C);
/* Load active jobs at request start */
profiler_job_refresh_active_jobs();
#if PHP_VERSION_ID >= 70000
/* Initialize prepared statement query template storage */
ALLOC_HASHTABLE(PROFILER_G(stmt_queries));
zend_hash_init(PROFILER_G(stmt_queries), 16, NULL, ZVAL_PTR_DTOR, 0);
#endif
}

return SUCCESS;
Expand All @@ -187,6 +192,14 @@ PHP_RSHUTDOWN_FUNCTION(mariadb_profiler)
if (PROFILER_G(enabled)) {
profiler_tag_clear_all();
profiler_job_free_active_jobs();
#if PHP_VERSION_ID >= 70000
/* Free prepared statement query template storage */
if (PROFILER_G(stmt_queries)) {
zend_hash_destroy(PROFILER_G(stmt_queries));
FREE_HASHTABLE(PROFILER_G(stmt_queries));
PROFILER_G(stmt_queries) = NULL;
}
#endif
}
return SUCCESS;
}
Expand Down
13 changes: 10 additions & 3 deletions ext/mariadb_profiler/php_mariadb_profiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ ZEND_BEGIN_MODULE_GLOBALS(mariadb_profiler)
int tag_depth;
/* Trace settings */
zend_long trace_depth; /* 0=disabled, N=capture N frames */
#if PHP_VERSION_ID >= 70000
/* Prepared statement query template storage (PHP 7.0+) */
HashTable *stmt_queries; /* stmt ptr -> query template string */
#endif
ZEND_END_MODULE_GLOBALS(mariadb_profiler)

/* Globals accessor: extern declaration for use across compilation units */
Expand Down Expand Up @@ -95,10 +99,13 @@ void profiler_job_free_active_jobs(void);
int profiler_job_is_any_active(void);
char **profiler_job_get_active_list(int *count);

/* Logging */
void profiler_log_query(const char *query, size_t query_len);
/* Logging – status is "ok" or "err" (NULL treated as "ok") */
void profiler_log_query(const char *query, size_t query_len, const char *status);
void profiler_log_query_with_params(const char *query, size_t query_len,
const char *params_json, const char *status);
void profiler_log_raw(const char *job_key, const char *query, size_t query_len,
const char *tag, const char *trace_json);
const char *tag, const char *trace_json,
const char *params_json, const char *status);
void profiler_log_init(void);
void profiler_log_shutdown(void);

Expand Down
12 changes: 12 additions & 0 deletions ext/mariadb_profiler/php_mariadb_profiler_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,16 @@ typedef long zend_long;
zend_hash_str_find((ht), (key), (key_len))
#endif

/*
* ---- bool type compatibility for mysqlnd stmt dtor ----
*
* PHP 8.0+: uses C99 bool
* PHP 7.x: uses zend_bool (unsigned char)
*/
#if PHP_VERSION_ID >= 80000
# define PROFILER_BOOL_T bool
#else
# define PROFILER_BOOL_T zend_bool
#endif

#endif /* PHP_MARIADB_PROFILER_COMPAT_H */
63 changes: 51 additions & 12 deletions ext/mariadb_profiler/profiler_log.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,10 @@ static double profiler_log_get_microtime(void)

/* {{{ profiler_log_raw
* Write raw query to job's raw log file.
* tag and trace_json may be NULL. */
* tag, trace_json, params_json, and status may be NULL. */
void profiler_log_raw(const char *job_key, const char *query, size_t query_len,
const char *tag, const char *trace_json)
const char *tag, const char *trace_json,
const char *params_json, const char *status)
{
char *filepath;
FILE *fp;
Expand All @@ -132,9 +133,16 @@ void profiler_log_raw(const char *job_key, const char *query, size_t query_len,
timestamp = profiler_log_get_timestamp();

if (tag) {
fprintf(fp, "[%s] [%s] %.*s\n", timestamp, tag, (int)query_len, query);
fprintf(fp, "[%s] [%s] [%s] %.*s\n", timestamp,
status ? status : "ok", tag, (int)query_len, query);
} else {
fprintf(fp, "[%s] %.*s\n", timestamp, (int)query_len, query);
fprintf(fp, "[%s] [%s] %.*s\n", timestamp,
status ? status : "ok", (int)query_len, query);
}

/* Append bound parameter values if present */
if (params_json && params_json[0] == '[' && params_json[1] != ']') {
fprintf(fp, " params: %s\n", params_json);
}

/* Append trace lines (indented with arrow prefix) */
Expand Down Expand Up @@ -196,10 +204,11 @@ void profiler_log_raw(const char *job_key, const char *query, size_t query_len,

/* {{{ profiler_log_jsonl
* Write JSON line to job's parsed log file.
* tag and trace_json may be NULL.
* tag, trace_json, params_json, and status may be NULL.
* SQL parsing (table/column extraction) is done by the CLI tool. */
static void profiler_log_jsonl(const char *job_key, const char *query, size_t query_len,
const char *tag, const char *trace_json)
const char *tag, const char *trace_json,
const char *params_json, const char *status)
{
char *filepath;
FILE *fp;
Expand Down Expand Up @@ -227,18 +236,27 @@ static void profiler_log_jsonl(const char *job_key, const char *query, size_t qu
}
ts = profiler_log_get_microtime();

/* Build JSON line with optional tag and trace fields */
/* Build JSON line with optional tag, params, and trace fields */
fprintf(fp, "{\"k\":\"%s\",\"q\":\"%s\"", escaped_key, escaped_query);

if (escaped_tag) {
fprintf(fp, ",\"tag\":\"%s\"", escaped_tag);
}

/* params_json is already a valid JSON array string e.g. ["123","active",null] */
if (params_json) {
fprintf(fp, ",\"params\":%s", params_json);
}

if (trace_json) {
/* trace_json is already a valid JSON array string */
fprintf(fp, ",\"trace\":%s", trace_json);
}

if (status) {
fprintf(fp, ",\"s\":\"%s\"", status);
}

fprintf(fp, ",\"ts\":%.6f}\n", ts);

efree(escaped_query);
Expand All @@ -252,10 +270,12 @@ static void profiler_log_jsonl(const char *job_key, const char *query, size_t qu
}
/* }}} */

/* {{{ profiler_log_query
* Main entry point: log a query to all active jobs.
/* {{{ profiler_log_query_internal
* Internal: log a query to all active jobs with optional params and status.
* Captures the current context tag and PHP trace once, shared across all jobs. */
void profiler_log_query(const char *query, size_t query_len)
static void profiler_log_query_internal(const char *query, size_t query_len,
const char *params_json,
const char *status)
{
char **jobs;
int job_count;
Expand All @@ -276,11 +296,11 @@ void profiler_log_query(const char *query, size_t query_len)

for (i = 0; i < job_count; i++) {
/* Write JSONL entry */
profiler_log_jsonl(jobs[i], query, query_len, tag, trace_json);
profiler_log_jsonl(jobs[i], query, query_len, tag, trace_json, params_json, status);

/* Write raw log if enabled */
if (PROFILER_G(raw_log)) {
profiler_log_raw(jobs[i], query, query_len, tag, trace_json);
profiler_log_raw(jobs[i], query, query_len, tag, trace_json, params_json, status);
}
}

Expand All @@ -290,6 +310,25 @@ void profiler_log_query(const char *query, size_t query_len)
}
/* }}} */

/* {{{ profiler_log_query
* Main entry point: log a query (without params) to all active jobs.
* status is "ok" or "err" (NULL treated as "ok"). */
void profiler_log_query(const char *query, size_t query_len, const char *status)
{
profiler_log_query_internal(query, query_len, NULL, status);
}
/* }}} */

/* {{{ profiler_log_query_with_params
* Log a prepared statement query with bound parameter values to all active jobs.
* status is "ok" or "err" (NULL treated as "ok"). */
void profiler_log_query_with_params(const char *query, size_t query_len,
const char *params_json, const char *status)
{
profiler_log_query_internal(query, query_len, params_json, status);
}
/* }}} */

/* {{{ profiler_log_init */
void profiler_log_init(void)
{
Expand Down
Loading