MySQL的Debug Package,涉及到源文件,主要是两个文件my_dbug.h和dbug.cc
几个比较重要的结构:
Struct Settings
struct settings {
uint flags; /* Current settings flags */
uint maxdepth; /* Current maximum trace depth */
uint delay; /* Delay after each output line */
uint sub_level; /* Sub this from code_state->level */
FILE *out_file; /* Current output stream */
FILE *prof_file; /* Current profiling stream */
char name[FN_REFLEN]; /* Name of output file */
struct link *functions; /* List of functions */
struct link *p_functions; /* List of profiled functions */
struct link *keywords; /* List of debug keywords */
struct link *processes; /* List of process names */
struct settings *next; /* Next settings in the list */
};
用于记录配置项,其中前面介绍的flags以及flags后面紧跟的配置设置值,会被记录在该struct的实例中。
Struct CODE_STATE
struct CODE_STATE {
const char *process; /* Pointer to process name; usually argv[0] */
const char *func; /* Name of current user function */
int func_len; /* How many bytes to print from func */
const char *file; /* Name of current user file */
struct _db_stack_frame_ *framep; /* Pointer to current frame */
struct settings *stack; /* debugging settings */
int lineno; /* Current debugger output line number */
uint level; /* Current function nesting level */
/*
* The following variables are used to hold the state information
* between the call to _db_pargs_() and _db_doprnt_(), during
* expansion of the DBUG_PRINT macro. This is the only macro
* that currently uses these variables.
*
* These variables are currently used only by _db_pargs_() and
* _db_doprnt_().
*/
uint u_line; /* User source code line number */
int locked; /* If locked with _db_lock_file_ */
const char *u_keyword; /* Keyword for current macro */
uint m_read_lock_count;
};
核心的数据结构,配置值,函数调用堆栈,调用信息的输出展示,均需要该结构。
struct _db_stack_frame_
struct _db_stack_frame_ {
const char *func; /* function name of the previous stack frame */
int func_len; /* how much to print from func */
const char *file; /* filename of the function of previous frame */
unsigned int level; /* this nesting level, highest bit enables tracing */
struct _db_stack_frame_ *prev; /* pointer to the previous frame */
};
函数调用栈的主要结构,记录了function名称,func_len,该函数所在的源文件,函数调用堆栈中的层级。
核心函数
code_state 函数
static CODE_STATE *code_state(void) {
CODE_STATE *cs, **cs_ptr;
//如果全局init_settings没有初始化,对全局init_settings进行一次初始化
if (!init_done) {
init_done = true;
native_mutex_init(&THR_LOCK_dbug, nullptr);
native_mutex_init(&THR_LOCK_gcov, nullptr);
native_rw_init(&THR_LOCK_init_settings);
memset(&init_settings, 0, sizeof(init_settings));
init_settings.out_file = stderr;
init_settings.flags = OPEN_APPEND;
}
//获取调用函数code_state的线程是否初始化了自己的code_sate对象。如果cs_ptr为空,说明是在该线程初始化之前调用了code_state。
if (!(cs_ptr = my_thread_var_dbug()))
return nullptr; /* Thread not initialised */
//这一步,为该线程初始化一个code_state对象。
if (!(cs = *cs_ptr)) {
cs = (CODE_STATE *)DbugMalloc(sizeof(*cs));
memset(cs, 0, sizeof(*cs));
cs->process = db_process ? db_process : "dbug";
cs->func = "?func";
cs->file = "?file";
cs->stack = &init_settings;
cs->m_read_lock_count = 0;
*cs_ptr = cs;
}
// 将该线程的code_state对象的地址返回出去,后续返回的都将是本线程的code_state对象。
return cs;
}
_db_enter_ 函数
//将当前_stack_frame_的信息赋值给code_state对象
void _db_enter_(const char *_func_, int func_len, const char *_file_,
uint _line_, struct _db_stack_frame_ *_stack_frame_) {
int save_errno;
CODE_STATE *cs;
if (!((cs = code_state()))) {
_stack_frame_->level =
0; /* Set to avoid valgrind warnings if dbug is enabled later */
_stack_frame_->prev = nullptr;
return;
}
save_errno = errno;
read_lock_stack(cs);
//将code_state中的当前信息,保存在_stack_frame_中,
_stack_frame_->func = cs->func;
_stack_frame_->func_len = cs->func_len;
_stack_frame_->file = cs->file;
//更新code_state当前信息
cs->func = _func_;
cs->func_len = func_len;
cs->file = _file_;
//对_stack_frame_进行压栈处理
_stack_frame_->prev = cs->framep;
_stack_frame_->level = ++cs->level | framep_trace_flag(cs, cs->framep);
cs->framep = _stack_frame_;
//判断trace设置状态
switch (DoTrace(cs)) {
case ENABLE_TRACE:
cs->framep->level |= TRACE_ON;
if (!TRACING) break;
[[fallthrough]];
case DO_TRACE:
//如果trace中,则打印函数信息
if (TRACING) {
if (!cs->locked) native_mutex_lock(&THR_LOCK_dbug);
DoPrefix(cs, _line_);
Indent(cs, cs->level);
(void)fprintf(cs->stack->out_file, ">%.*s\n", cs->func_len, cs->func);
//刷新到trace输出文件。
DbugFlush(cs); /* This does a unlock */
}
break;
case DISABLE_TRACE:
cs->framep->level &= ~TRACE_ON;
[[fallthrough]];
case DONT_TRACE:
break;
}
errno = save_errno;
unlock_stack(cs);
}
_db_return_函数
调用该函数,把 case_date stack_frame 堆栈当前头部的frame打印出出来,同时进行出栈出来,并销毁该stack_frame
void _db_return_(uint _line_, struct _db_stack_frame_ *_stack_frame_) {
int save_errno = errno;
uint _slevel_ = _stack_frame_->level & ~TRACE_ON;
CODE_STATE *cs;
//宏定义,将本线程的code_state对象指针传给cs
get_code_state_or_return;
//和_db_enter配对,在enter中压栈的stack_frame肯定和同函数中调用return 出栈的stack_frame相同,否则报错
if (cs->framep != _stack_frame_) {
char buf[512];
snprintf(buf, sizeof(buf), ERR_MISSING_RETURN, cs->func);
DbugExit(buf);
}
read_lock_stack(cs);
if (DoTrace(cs) & DO_TRACE) {
if (TRACING) {
if (!cs->locked) native_mutex_lock(&THR_LOCK_dbug);
//根据debug设置,打印前缀信息
DoPrefix(cs, _line_);
//根据level信息,打印空格,level深的,其前面空格就多,方便观看函数调用层级
Indent(cs, cs->level);
//打印函数名,以及源文件行号
if (_line_ == 0) {
(void)fprintf(cs->stack->out_file, "<%.*s\n", cs->func_len, cs->func);
} else {
(void)fprintf(cs->stack->out_file, "<%.*s %u\n", cs->func_len, cs->func,
_line_);
}
DbugFlush(cs);
}
}
//将code_state 对象里的函数信息,level信息,file信息等修改为和传入的_stack_frame_相同。
//同时将cs_freamep堆栈移到下一层
/*
Check to not set level < 0. This can happen if DBUG was disabled when
function was entered and enabled in function.
*/
cs->level = _slevel_ != 0 ? _slevel_ - 1 : 0;
cs->func = _stack_frame_->func;
cs->func_len = _stack_frame_->func_len;
cs->file = _stack_frame_->file;
if (cs->framep != nullptr) cs->framep = cs->framep->prev;
errno = save_errno;
unlock_stack(cs);
}