1108 lines
37 KiB
C++
1108 lines
37 KiB
C++
// Tencent is pleased to support the open source community by making LuaPanda available.
|
||
// Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
|
||
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
||
// https://opensource.org/licenses/BSD-3-Clause
|
||
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||
|
||
#include "libpdebug.h"
|
||
#include <ctime>
|
||
#include <list>
|
||
#include <map>
|
||
#include <string>
|
||
|
||
//using namespace std;
|
||
static int cur_run_state = 0; //当前运行状态, c 和 lua 都可能改变这个状态,要保持同步
|
||
static int cur_hook_state = 0; //当前hook状态, c 和 lua 都可能改变这个状态
|
||
static int logLevel = 1; //日志等级(从lua同步)
|
||
static int pathCaseSensitivity = 1; //大小写敏感标志位(从lua同步)
|
||
//static int autoPathMode = 0; //自动路径标是否开启志位
|
||
static int BPhit = 0; //BP命中标志位
|
||
static int stackdeep_counter = 0; //step用的栈深度计数器
|
||
static char hookLog[1024] = { 0 };
|
||
const char* debug_file_path; //debugger的文件路径
|
||
int debug_file_path_len;
|
||
const char* tools_file_path; //tools的文件路径
|
||
int tools_file_path_len;
|
||
char config_ext[32] = ""; //后缀(从lua同步)
|
||
const char* config_cwd = ""; //cwd(从lua同步)
|
||
const char* config_tempfile_path = "";
|
||
time_t recvMsgSeconds = 0;
|
||
const char* last_source;
|
||
int ar_current_line = 0;
|
||
int ar_def_line = 0;
|
||
int ar_lastdef_line = 0;
|
||
int bp_twice_check_res = 1;
|
||
int lua_debugger_ver = 0; // luapanda.lua的版本,便于做向下兼容
|
||
struct path_transfer_node;
|
||
struct breakpoint;
|
||
// 路径缓存队列 getinfo -> format
|
||
std::list<path_transfer_node*> getinfo_to_format_cache;
|
||
// 存放断点map,key为source
|
||
std::map<std::string, std::map<int, breakpoint> > all_breakpoint_map;
|
||
|
||
enum run_state
|
||
{
|
||
DISCONNECT = 0,
|
||
WAIT_CMD = 1,
|
||
STOP_ON_ENTRY = 2,
|
||
RUN = 3,
|
||
STEPOVER = 4,
|
||
STEPIN = 5,
|
||
STEPOUT = 6,
|
||
STEPOVER_STOP = 7,
|
||
STEPIN_STOP = 8,
|
||
STEPOUT_STOP = 9,
|
||
HIT_BREAKPOINT = 10
|
||
};
|
||
|
||
enum hook_state
|
||
{
|
||
DISCONNECT_HOOK = 0,
|
||
LITE_HOOK = 1, //全局无断点
|
||
MID_HOOK = 2, //全局有断点,本文件无断点
|
||
ALL_HOOK = 3,
|
||
};
|
||
|
||
enum hook_event
|
||
{
|
||
CALL = 0,
|
||
RETURN =1,
|
||
LINE =2,
|
||
TAILRET=4
|
||
};
|
||
|
||
enum breakpoint_type
|
||
{
|
||
CONDITION_BREAKPOINT = 0,
|
||
LOG_POINT,
|
||
LINE_BREAKPOINT
|
||
};
|
||
|
||
//用来缓存路径的结构体
|
||
struct path_transfer_node{
|
||
std::string src;
|
||
std::string dst;
|
||
path_transfer_node(std::string _src, std::string _dst){
|
||
src = _src;
|
||
dst = _dst;
|
||
}
|
||
};
|
||
|
||
// 断点信息
|
||
struct breakpoint {
|
||
breakpoint_type type;
|
||
std::string info;
|
||
};
|
||
|
||
struct debug_auto_stack {
|
||
explicit debug_auto_stack(lua_State* l) {
|
||
this->L = l;
|
||
this->top = lua_gettop(L);
|
||
}
|
||
~debug_auto_stack() {
|
||
lua_settop(this->L, this->top);
|
||
}
|
||
lua_State* L;
|
||
int top;
|
||
};
|
||
|
||
//内部方法声明
|
||
void debug_hook_c(lua_State *L, lua_Debug *ar);
|
||
void check_hook_state(lua_State *L, const char* source, int current_line, int def_line, int last_line, int event = -1);
|
||
void print_to_vscode(lua_State *L, const char* msg, int level = 0);
|
||
void load(lua_State* L);
|
||
|
||
//打印断点信息
|
||
void print_all_breakpoint_map(lua_State *L, int print_level = 0) {
|
||
if (print_level < logLevel) {
|
||
return;
|
||
}
|
||
std::map<std::string, std::map<int, breakpoint> >::iterator iter1;
|
||
std::map<int, breakpoint>::iterator iter2;
|
||
std::string log_message = "[breakpoints in chook:]\n";
|
||
for (iter1 = all_breakpoint_map.begin(); iter1 != all_breakpoint_map.end(); ++iter1) {
|
||
log_message += iter1->first;
|
||
log_message += '\n';
|
||
for (iter2 = iter1->second.begin(); iter2 != iter1->second.end(); ++iter2) {
|
||
log_message += std::string(" line: ");
|
||
log_message += std::to_string(iter2->first);
|
||
log_message += std::string(" type: ");
|
||
switch (iter2->second.type) {
|
||
case CONDITION_BREAKPOINT:
|
||
log_message += std::string("condition breakpoint info: ");
|
||
log_message += iter2->second.info;
|
||
break;
|
||
|
||
case LOG_POINT:
|
||
log_message += std::string("log point info: ");
|
||
log_message += iter2->second.info;
|
||
break;
|
||
|
||
case LINE_BREAKPOINT:
|
||
log_message += std::string("line breakpoint info: ");
|
||
log_message += iter2->second.info;
|
||
break;
|
||
|
||
default:
|
||
log_message += std::string("Invalid breakpoint type!");
|
||
log_message += std::to_string(iter2->second.type);
|
||
break;
|
||
}
|
||
log_message += '\n';
|
||
}
|
||
}
|
||
print_to_vscode(L, log_message.c_str(), print_level);
|
||
}
|
||
|
||
//push_arg Template
|
||
template <typename T>
|
||
void push_arg(lua_State *L, T value);
|
||
|
||
template <>
|
||
void push_arg(lua_State *L, int value){
|
||
lua_pushnumber(L, value);
|
||
}
|
||
|
||
template <>
|
||
void push_arg(lua_State *L, double value){
|
||
lua_pushnumber(L, value);
|
||
}
|
||
|
||
template <>
|
||
void push_arg(lua_State *L, const char * value){
|
||
lua_pushstring(L, value);
|
||
}
|
||
|
||
void push_args(lua_State *L){}
|
||
//push_arg Template End
|
||
|
||
template <typename T>
|
||
void push_arg(lua_State *L, T value){
|
||
push_arg<T>(L, value);
|
||
}
|
||
|
||
template <typename T, typename ... ARGS>
|
||
void push_args(lua_State *L, T value ,ARGS... args){
|
||
push_arg<T>(L, value);
|
||
push_args(L, std::forward<ARGS>(args) ...);
|
||
}
|
||
//push_args End
|
||
|
||
template <typename ... ARGS>
|
||
int call_lua_function(lua_State *L, const char * lua_function_name, int retCount , ARGS... args){
|
||
lua_getglobal(L, LUA_DEBUGGER_NAME);
|
||
if (!lua_istable(L, -1)) {
|
||
const char *err_msg = "[C Module Error]:call_lua_function Get LUA_DEBUGGER_NAME error.\n";
|
||
print_to_vscode(L, err_msg, 2);
|
||
return -1;
|
||
}
|
||
|
||
lua_getfield(L, -1, lua_function_name);
|
||
if (!lua_isfunction(L, -1)) {
|
||
char err_msg[100];
|
||
snprintf(err_msg, sizeof(err_msg), "[C Module Error]:call_lua_function Get lua function '%s' error\n.", lua_function_name);
|
||
print_to_vscode(L, err_msg, 2);
|
||
return -1;
|
||
}
|
||
|
||
push_args(L, args...);
|
||
int err_code = lua_pcall(L, sizeof...(args), retCount, 0);
|
||
if (err_code) {
|
||
char err_msg[1024];
|
||
const char *lua_error = lua_tostring(L, -1);
|
||
snprintf(err_msg, sizeof(err_msg), "[C Module Error]:call_lua_function Call '%s' error. ErrorCode: %d, ErrorMessage: %s.\n", lua_function_name, err_code, lua_error);
|
||
print_to_vscode(L, err_msg, 2);
|
||
lua_pop(L, 1);
|
||
return err_code;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
//------------Lua同步数据接口------------
|
||
//lua层主动清除路径缓存
|
||
extern "C" int clear_pathcache(lua_State *L)
|
||
{
|
||
getinfo_to_format_cache.clear();
|
||
return 0;
|
||
}
|
||
|
||
//lua主动调用从c获取current_hook_state状态
|
||
extern "C" int get_libhook_state(lua_State *L)
|
||
{
|
||
lua_pushnumber(L, cur_hook_state);
|
||
return 1;
|
||
}
|
||
|
||
//lua主动调用从c获取last_source
|
||
extern "C" int get_last_source(lua_State *L)
|
||
{
|
||
lua_pushstring(L, last_source);
|
||
return 1;
|
||
}
|
||
|
||
//同步luapanda.lua的版本号
|
||
extern "C" int sync_lua_debugger_ver(lua_State *L)
|
||
{
|
||
lua_debugger_ver = static_cast<int>(luaL_checkinteger(L, 1));
|
||
return 0;
|
||
}
|
||
|
||
//同步断点命中标识
|
||
extern "C" int sync_bp_hit(lua_State *L) {
|
||
if(cur_hook_state == DISCONNECT_HOOK){
|
||
//返回参数个数
|
||
return 0;
|
||
}
|
||
|
||
BPhit = static_cast<int>(luaL_checkinteger(L, 1));
|
||
return 0;
|
||
}
|
||
|
||
//同步设置 -- 日志等级, 是否debug代码段, 是否使用忽略大小写
|
||
extern "C" int sync_config(lua_State *L) {
|
||
logLevel = static_cast<int>(luaL_checkinteger(L, 1));
|
||
pathCaseSensitivity = static_cast<int>(luaL_checkinteger(L, 2));
|
||
// autoPathMode = static_cast<int>(luaL_optinteger(L, 3, 0));
|
||
return 0;
|
||
}
|
||
|
||
//同步临时文件路径
|
||
extern "C" int sync_tempfile_path(lua_State *L) {
|
||
config_tempfile_path = luaL_checkstring(L, 1);
|
||
return 0;
|
||
}
|
||
|
||
|
||
//同步临时文件路径
|
||
extern "C" int set_bp_twice_check_res(lua_State *L) {
|
||
bp_twice_check_res = luaL_checknumber(L, 1);
|
||
return 0;
|
||
}
|
||
|
||
//lua 获取版本号
|
||
extern "C" int sync_getLibVersion(lua_State *L) {
|
||
lua_pushstring(L, HOOK_LIB_VERSION);
|
||
lua_pushnumber(L, LUA_VERSION_NUM);
|
||
return 2;
|
||
}
|
||
|
||
//同步文件后缀
|
||
extern "C" int sync_file_ext(lua_State *L) {
|
||
const char *ext = luaL_checkstring(L, 1);
|
||
snprintf(config_ext, sizeof(config_ext), ".%s", ext);
|
||
return 0;
|
||
}
|
||
|
||
//debugger路径
|
||
extern "C" int sync_debugger_path(lua_State *L) {
|
||
debug_file_path = luaL_checkstring(L, 1);
|
||
debug_file_path_len = strlen(debug_file_path);
|
||
return 0;
|
||
}
|
||
|
||
//tools路径
|
||
extern "C" int sync_tools_path(lua_State *L) {
|
||
tools_file_path = luaL_checkstring(L, 1);
|
||
tools_file_path_len = strlen(tools_file_path);
|
||
return 0;
|
||
}
|
||
|
||
//cwd
|
||
extern "C" int sync_cwd(lua_State *L) {
|
||
config_cwd = luaL_checkstring(L, 1);
|
||
return 0;
|
||
}
|
||
|
||
//同步运行状态给Lua C->lua
|
||
void sync_runstate_toLua(lua_State *L, int state) {
|
||
debug_auto_stack _tt(L);
|
||
cur_run_state = state;
|
||
call_lua_function(L, "changeRunState", 0, state, 1);
|
||
return;
|
||
}
|
||
|
||
//这个接口给lua调用,用来同步状态 lua->C
|
||
extern "C" int lua_set_runstate(lua_State *L) {
|
||
cur_run_state = static_cast<int>(luaL_checkinteger(L, 1));
|
||
return 0;
|
||
}
|
||
|
||
//根据运行状态修改hook状态
|
||
void sethookstate(lua_State *L, int state){
|
||
cur_hook_state = state;
|
||
switch(state){
|
||
case DISCONNECT_HOOK:
|
||
lua_sethook(L, debug_hook_c, LUA_MASKRET , 1000000);
|
||
break;
|
||
case LITE_HOOK:
|
||
lua_sethook(L, debug_hook_c, LUA_MASKRET , 0);
|
||
break;
|
||
case MID_HOOK:
|
||
lua_sethook(L, debug_hook_c, LUA_MASKCALL | LUA_MASKRET , 0);
|
||
break;
|
||
case ALL_HOOK:
|
||
lua_sethook(L, debug_hook_c, LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE, 0);
|
||
break;
|
||
}
|
||
}
|
||
|
||
//这个接口给lua调用,用来同步hook状态 lua->C
|
||
extern "C" int lua_set_hookstate(lua_State *L) {
|
||
cur_hook_state = static_cast<int>(luaL_checkinteger(L, 1));
|
||
sethookstate(L, cur_hook_state);
|
||
return 0;
|
||
}
|
||
|
||
void print_to_vscode(lua_State *L, const char* msg, int level) {
|
||
if ( DISCONNECT != cur_run_state && level >= logLevel) {
|
||
//打印
|
||
call_lua_function(L, "printToVSCode", 0, msg, level);
|
||
}
|
||
}
|
||
|
||
//获取路径(带缓存)
|
||
const char* getPath(lua_State *L,const char* source){
|
||
debug_auto_stack _tt(L);
|
||
|
||
if(source == nullptr){
|
||
print_to_vscode(L, "[C Module Error]: getPath Exception: source == nullptr", 2);
|
||
return "";
|
||
}
|
||
|
||
//检查缓存
|
||
for(auto iter = getinfo_to_format_cache.begin();iter != getinfo_to_format_cache.end();iter++)
|
||
{
|
||
if(!strcmp((*iter)->src.c_str(), source)){
|
||
return (*iter)->dst.c_str();
|
||
}
|
||
}
|
||
|
||
//若缓存中没有,到lua中转换
|
||
int lua_ret = call_lua_function(L, "getPath", 1 , source);
|
||
if (lua_ret != 0) {
|
||
return "";
|
||
}
|
||
const char* retSource = lua_tostring(L, -1);
|
||
//加入缓存,返回
|
||
path_transfer_node *nd = new path_transfer_node(source, retSource );
|
||
getinfo_to_format_cache.push_back(nd);
|
||
|
||
return retSource;
|
||
}
|
||
|
||
// 向 lua 中 checkRealHitBreakpoint 查询是否在缓存中,以判断是否真正命中断点
|
||
const int checkRealHitBreakpoint(lua_State *L,const char* source, int line){
|
||
debug_auto_stack _tt(L);
|
||
|
||
if(source == nullptr){
|
||
print_to_vscode(L, "[C Module Error]: checkRealHitBreakpoint Exception: source == nullptr", 2);
|
||
return 0;
|
||
}
|
||
|
||
//若缓存中没有,到lua中转换
|
||
int lua_ret = call_lua_function(L, "checkRealHitBreakpoint", 1 , source, line);
|
||
if (lua_ret != 0) {
|
||
return 0;
|
||
}
|
||
int realHit = lua_toboolean(L, -1);
|
||
return realHit;
|
||
}
|
||
|
||
//供lua调用,把断点列表同步给c端
|
||
extern "C" int sync_breakpoints(lua_State *L) {
|
||
debug_auto_stack _tt(L);
|
||
//取数组
|
||
lua_getglobal(L, LUA_DEBUGGER_NAME); //-1 LuaPanda
|
||
if (!lua_istable(L, -1)) {
|
||
print_to_vscode(L, "[C Module Error] debug_ishit_bk get LUA_DEBUGGER_NAME error", 2);
|
||
return -1;
|
||
}
|
||
|
||
lua_getfield(L, -1, "breaks");
|
||
if (!lua_istable(L, -1)) {
|
||
print_to_vscode(L, "[C Module Error] debug_ishit_bk get breaks error", 2);
|
||
return -1;
|
||
}
|
||
|
||
//遍历breaks
|
||
all_breakpoint_map.clear();
|
||
lua_pushnil(L);//breaks nil
|
||
while (lua_next(L, -2)) {
|
||
//breaks k(string) v(table)
|
||
const char* source = luaL_checkstring(L, -2);
|
||
|
||
std::map<int, breakpoint> file_breakpoint_map;
|
||
lua_pushnil(L);//k,v, nil
|
||
while (lua_next(L, -2)) {
|
||
if(lua_debugger_ver >= 30150){
|
||
lua_pushnil(L);//k,v, nil
|
||
while (lua_next(L, -2)) {
|
||
//k,v,k,v
|
||
lua_getfield(L, -1, "line"); //k,v,k,v,line
|
||
int line = (int)lua_tointeger(L, -1);
|
||
lua_pop(L, 1); // line
|
||
|
||
lua_getfield(L, -1, "type");
|
||
int type = (int)lua_tointeger(L, -1);
|
||
lua_pop(L, 1); // type
|
||
|
||
struct breakpoint bp;
|
||
switch (type) {
|
||
case CONDITION_BREAKPOINT: {
|
||
bp.type = CONDITION_BREAKPOINT;
|
||
|
||
lua_getfield(L, -1, "condition");
|
||
const char* condition = luaL_checkstring(L, -1);
|
||
lua_pop(L, 1); // condition
|
||
bp.info = condition;
|
||
break;
|
||
}
|
||
|
||
case LOG_POINT: {
|
||
bp.type = LOG_POINT;
|
||
|
||
lua_getfield(L, -1, "logMessage");
|
||
const char* log_message = luaL_checkstring(L, -1);
|
||
lua_pop(L, 1); // logMessage
|
||
bp.info = log_message;
|
||
break;
|
||
}
|
||
|
||
case LINE_BREAKPOINT:
|
||
bp.type = LINE_BREAKPOINT;
|
||
|
||
bp.info = std::to_string(line);
|
||
break;
|
||
|
||
default:
|
||
print_to_vscode(L, "[C Module Error] Invalid breakpoint type!", 2);
|
||
return -1;
|
||
}
|
||
|
||
file_breakpoint_map[line] = bp;
|
||
|
||
lua_pop(L, 1);//value
|
||
//k,v,k
|
||
}
|
||
lua_pop(L, 1);//value
|
||
}else{
|
||
// 兼容 < 3.1.5 版本的luapanda.lua
|
||
|
||
//k,v,k,v
|
||
lua_getfield(L, -1, "line"); //k,v,k,v,line
|
||
int line = (int)lua_tointeger(L, -1);
|
||
lua_pop(L, 1); // line
|
||
|
||
lua_getfield(L, -1, "type");
|
||
int type = (int)lua_tointeger(L, -1);
|
||
lua_pop(L, 1); // type
|
||
|
||
struct breakpoint bp;
|
||
switch (type) {
|
||
case CONDITION_BREAKPOINT: {
|
||
bp.type = CONDITION_BREAKPOINT;
|
||
|
||
lua_getfield(L, -1, "condition");
|
||
const char* condition = luaL_checkstring(L, -1);
|
||
lua_pop(L, 1); // condition
|
||
bp.info = condition;
|
||
break;
|
||
}
|
||
|
||
case LOG_POINT: {
|
||
bp.type = LOG_POINT;
|
||
|
||
lua_getfield(L, -1, "logMessage");
|
||
const char* log_message = luaL_checkstring(L, -1);
|
||
lua_pop(L, 1); // logMessage
|
||
bp.info = log_message;
|
||
break;
|
||
}
|
||
|
||
case LINE_BREAKPOINT:
|
||
bp.type = LINE_BREAKPOINT;
|
||
|
||
bp.info = std::to_string(line);
|
||
break;
|
||
|
||
default:
|
||
print_to_vscode(L, "[C Module Error] Invalid breakpoint type!", 2);
|
||
return -1;
|
||
}
|
||
|
||
file_breakpoint_map[line] = bp;
|
||
|
||
lua_pop(L, 1);//value
|
||
|
||
}
|
||
}
|
||
all_breakpoint_map[std::string(source)] = file_breakpoint_map;
|
||
//k,v
|
||
lua_pop(L, 1);//外部每次循环
|
||
//k
|
||
}
|
||
lua_pop(L, 1);//外部每次循环
|
||
|
||
print_all_breakpoint_map(L);
|
||
check_hook_state(L, last_source, ar_current_line ,ar_def_line, ar_lastdef_line);
|
||
return 0;
|
||
}
|
||
|
||
//断点命中判断
|
||
int debug_ishit_bk(lua_State *L, const char * curPath, int current_line) {
|
||
debug_auto_stack _tt(L);
|
||
// 获取标准路径[文件名.后缀]
|
||
const char *standardPath = getPath(L, curPath);
|
||
// 判断是否存在同名文件
|
||
std::map<std::string, std::map<int, struct breakpoint> >::const_iterator const_iter1 = all_breakpoint_map.find(std::string(standardPath));
|
||
if (const_iter1 == all_breakpoint_map.end()) {
|
||
return 0;
|
||
}
|
||
|
||
// c++ all_breakpoint_map 的数据结构保持不变,和lua不一样
|
||
// 根据是否存在相同行号
|
||
std::map<int, struct breakpoint>::const_iterator const_iter2 = const_iter1->second.find(current_line);
|
||
if (const_iter2 == const_iter1->second.end()) {
|
||
return 0;
|
||
}
|
||
|
||
if(lua_debugger_ver >= 30160){
|
||
// luapanda.lua >= 3.1.6 才会调用
|
||
// 初步命中,到lua层中检测是否真正命中,以及断点类型
|
||
int lua_ret = call_lua_function(L, "isHitBreakpoint", 1, standardPath, curPath, current_line);
|
||
if (lua_ret != 0) {
|
||
// 调用出错时,按命中处理
|
||
return 1;
|
||
}
|
||
|
||
int realHit = lua_toboolean(L, -1);
|
||
return realHit;
|
||
}else{
|
||
// 兼容旧版本
|
||
// 条件断点
|
||
if (const_iter2->second.type == CONDITION_BREAKPOINT) {
|
||
std::string condition = const_iter2->second.info;
|
||
int lua_ret = call_lua_function(L, "IsMeetCondition", 1, condition.c_str());
|
||
if (lua_ret != 0) {
|
||
return 0;
|
||
}
|
||
// if (!lua_isboolean(L, -1)) {
|
||
// print_to_vscode(L, "[Debug Lib Error] debug_ishit_bk process condition expression result error!", 2);
|
||
// return 0;
|
||
// }
|
||
int is_meet_condition = lua_toboolean(L, -1);
|
||
lua_pop(L, 1);
|
||
return is_meet_condition;
|
||
}
|
||
|
||
// 记录点
|
||
if (const_iter2->second.type == LOG_POINT) {
|
||
std::string log_message = "[log point output]: ";
|
||
log_message.append(const_iter2->second.info);
|
||
print_to_vscode(L, log_message.c_str() , 1);
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
//判断字符串是否匹配[string "
|
||
int isCodeSection(char *str) {
|
||
if (strlen(str) > 9) {
|
||
if (str[0] == '[' && str[7] == ' ' && str[8] == '"') {
|
||
return 1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
//断点命中判断 retuen : is_hit
|
||
int breakpoint_process(lua_State *L, lua_Debug *ar){
|
||
int is_hit = 0;
|
||
if (ar->event == LINE) {
|
||
is_hit = debug_ishit_bk(L, ar->source, ar->currentline);
|
||
// 同名文件可能会命中假断点 folder1/a.lua 和 folder2/a.lua 截取文件名都是 a.lua, 可能导致命中混淆
|
||
if(is_hit && lua_debugger_ver >= 30160){
|
||
// luapanda.lua >= 3.1.6 版本才会调用
|
||
is_hit = checkRealHitBreakpoint(L, ar->source, ar->currentline);
|
||
}
|
||
|
||
if (is_hit == 1 || BPhit) {
|
||
print_to_vscode(L, "[C Module] Breakpoint hit!");
|
||
int record_stackdeep_counter = stackdeep_counter;
|
||
int record_cur_run_state = cur_run_state;
|
||
stackdeep_counter = 0;
|
||
sync_runstate_toLua(L, HIT_BREAKPOINT);
|
||
bp_twice_check_res = 1;
|
||
//c层掌握 STEPOVER 计数器,状态机放在lua层,c主要去读(毕竟C作为lua的扩展)
|
||
//通知lua层,lua层阻塞,发消息
|
||
if(BPhit){
|
||
BPhit = 0;
|
||
call_lua_function(L, "SendMsgWithStack", 0, "stopOnCodeBreakpoint");
|
||
}else{
|
||
call_lua_function(L, "SendMsgWithStack", 0, "stopOnBreakpoint");
|
||
if( bp_twice_check_res == 0 ){
|
||
is_hit = 0;
|
||
stackdeep_counter = record_stackdeep_counter;
|
||
sync_runstate_toLua(L, record_cur_run_state);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return is_hit;
|
||
}
|
||
|
||
//单步处理
|
||
void step_process(lua_State *L, lua_Debug *ar){
|
||
//目前没有判断jump flag
|
||
if (cur_run_state == STEPOVER) {
|
||
if (ar->event == LINE && stackdeep_counter <= 0) {
|
||
sync_runstate_toLua(L, STEPOVER_STOP);
|
||
call_lua_function(L, "SendMsgWithStack", 0,"stopOnStep");
|
||
}
|
||
else if (ar->event == CALL) {
|
||
stackdeep_counter++;
|
||
}
|
||
//5.3 的tailcall暂时不需要处理。
|
||
else if (ar->event == RETURN) {
|
||
if (stackdeep_counter != 0) {
|
||
stackdeep_counter--;
|
||
}
|
||
}
|
||
}
|
||
else if (cur_run_state == STEPIN) {
|
||
if (ar->event == LINE) {
|
||
sync_runstate_toLua(L, STEPIN_STOP);
|
||
call_lua_function(L, "SendMsgWithStack", 0,"stopOnStepIn");
|
||
}
|
||
|
||
}
|
||
else if (cur_run_state == STEPOUT) {
|
||
if (ar->event == LINE) {
|
||
if (stackdeep_counter <= -1) {
|
||
stackdeep_counter = 0;
|
||
sync_runstate_toLua(L, STEPOUT_STOP);
|
||
call_lua_function(L, "SendMsgWithStack", 0,"stopOnStepOut");
|
||
}
|
||
}
|
||
else if (ar->event == CALL) {
|
||
stackdeep_counter++;
|
||
}
|
||
//5.3 的tailcall暂时不需要处理。
|
||
else if (ar->event == RETURN) {
|
||
stackdeep_counter--;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 无需reconnect返回1 ,需要重连时返回0
|
||
int hook_process_reconnect(lua_State *L){
|
||
time_t currentSecs = time(static_cast<time_t*>(NULL));
|
||
if(cur_hook_state == DISCONNECT_HOOK){
|
||
if (currentSecs - recvMsgSeconds > 1) {
|
||
call_lua_function(L, "reConnect", 0);
|
||
recvMsgSeconds = currentSecs;
|
||
}
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
void litehook_recv_message(lua_State *L){
|
||
time_t currentSecs = time(static_cast<time_t*>(NULL));
|
||
//2.定时接收消息 -- 这里的状态不只是run
|
||
if (cur_hook_state == LITE_HOOK && currentSecs - recvMsgSeconds > 1) {
|
||
call_lua_function(L, "debugger_wait_msg", 0);
|
||
recvMsgSeconds = currentSecs;
|
||
}
|
||
}
|
||
|
||
void hook_process_recv_message(lua_State *L){
|
||
time_t currentSecs = time(static_cast<time_t*>(NULL));
|
||
if ((cur_run_state == RUN ||
|
||
cur_run_state == STEPOVER ||
|
||
cur_run_state == STEPIN ||
|
||
cur_run_state == STEPOUT)
|
||
&& currentSecs - recvMsgSeconds > 1) {
|
||
call_lua_function(L, "debugger_wait_msg", 0);
|
||
recvMsgSeconds = currentSecs;
|
||
}
|
||
}
|
||
|
||
int hook_process_cfunction(lua_State *L, lua_Debug *ar){
|
||
if (!(strcmp(ar->what, "C")) || ar->currentline < 0) {
|
||
//Lua5.1 tail return会走到这里
|
||
if(!(strcmp(ar->source, "=(tail call)")) && ar -> event == TAILRET && (cur_run_state == STEPOVER || cur_run_state == STEPOUT )){
|
||
stackdeep_counter --;
|
||
}
|
||
//5.1
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
int hook_process_code_section(lua_State *L, lua_Debug *ar){
|
||
//测试[string ...]形式的路径
|
||
int isCodeSec = isCodeSection(ar->short_src);
|
||
if (isCodeSec == 1) {
|
||
//short_src是[string ]开头
|
||
if(strchr(ar->source, '\n') || strchr(ar->source, ';') || strchr(ar->source, '=')){
|
||
print_to_vscode(L, "hook jump Code String");
|
||
return 0;
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
//检查函数中是否有断点。int check_has_breakpoint 0:全局无断点 , 1:全局有断点但本文件中无断点 , 2:本文件中有断点 , 3:函数中有断点
|
||
int checkHasBreakpoint(lua_State *L, const char * src1, int current_line, int sline , int eline){
|
||
debug_auto_stack tt(L);
|
||
|
||
const char *src = getPath(L, src1);
|
||
if(!strcmp(src,"")){
|
||
// 路径完全一致
|
||
return ALL_HOOK;
|
||
}
|
||
|
||
if(all_breakpoint_map.empty() == true) {
|
||
// 全局没有断点
|
||
return LITE_HOOK;
|
||
}
|
||
|
||
std::map<std::string, std::map<int, breakpoint> >::iterator iter1;
|
||
for (iter1 = all_breakpoint_map.begin(); iter1 != all_breakpoint_map.end(); ++iter1) {
|
||
if (iter1->first == std::string(src)) {
|
||
// compare()
|
||
return ALL_HOOK;
|
||
}
|
||
}
|
||
|
||
//文件没有断点,MIDHOOK
|
||
return MID_HOOK;
|
||
}
|
||
|
||
void check_hook_state(lua_State *L, const char* source , int current_line, int def_line, int last_line ,int event){
|
||
if (source == NULL) {
|
||
return;
|
||
}
|
||
if(cur_run_state == RUN && cur_hook_state != DISCONNECT_HOOK){
|
||
int stats = checkHasBreakpoint(L, source, current_line, def_line, last_line);
|
||
if(stats == LITE_HOOK){
|
||
sethookstate(L, LITE_HOOK);
|
||
}else if(stats == MID_HOOK){
|
||
sethookstate(L, MID_HOOK);
|
||
}else if (stats == ALL_HOOK){
|
||
sethookstate(L, ALL_HOOK);
|
||
}
|
||
|
||
if( (event == RETURN || event == TAILRET) && cur_hook_state == MID_HOOK){
|
||
sethookstate(L, ALL_HOOK);
|
||
}
|
||
}
|
||
}
|
||
|
||
//这个函数要获取的消息 当前状态,断点列表
|
||
void debug_hook_c(lua_State *L, lua_Debug *ar) {
|
||
debug_auto_stack _tt(L);
|
||
if(!hook_process_reconnect(L)) return;
|
||
if(cur_hook_state == LITE_HOOK) {
|
||
litehook_recv_message(L);
|
||
return;
|
||
}
|
||
|
||
hook_process_recv_message(L);
|
||
|
||
if (lua_getinfo(L, "Slf", ar) != 0) {
|
||
//if in c function , return
|
||
if(!hook_process_cfunction(L, ar)) return;
|
||
//if in debugger , return
|
||
int source_len = strlen(ar->source);
|
||
if (debug_file_path_len == source_len) {
|
||
if (!strcmp(debug_file_path, ar->source)) return;
|
||
}
|
||
if (tools_file_path_len == source_len) {
|
||
if (!strcmp(tools_file_path, ar->source)) return;
|
||
}
|
||
//slua "temp buffer"
|
||
if (11 == source_len) {
|
||
if (!strcmp("temp buffer", ar->source)) return;
|
||
}
|
||
//xlua "chunk"
|
||
if (5 == source_len) {
|
||
if (!strcmp("chunk", ar->source)) return;
|
||
}
|
||
|
||
//code section
|
||
if(!hook_process_code_section(L, ar)) return;
|
||
|
||
//output debug info
|
||
if (logLevel == 0) {
|
||
snprintf(hookLog, sizeof(hookLog), "[hook state] event:%d | source: %s | short_src: %s | line:%d | defined:%d | laseDefined:%d | currentState:%d | currentHookState:%d \n", ar->event, ar->source, ar->short_src, ar->currentline, ar->linedefined, ar->lastlinedefined, cur_run_state, cur_hook_state);
|
||
print_to_vscode(L, hookLog);
|
||
}
|
||
|
||
//hook_state
|
||
last_source = ar->source;
|
||
ar_def_line = ar->linedefined;
|
||
ar_lastdef_line = ar->lastlinedefined;
|
||
ar_current_line = ar->currentline;
|
||
|
||
int is_hit = breakpoint_process(L, ar); //断点命中标记位 //line + 预判
|
||
|
||
//STOP_ON_ENTRY
|
||
int stop_on_entry = 0;
|
||
if (cur_run_state == STOP_ON_ENTRY && is_hit != 1) {
|
||
//STOP_ON_ENTRY
|
||
if (ar->event == LINE) {
|
||
//命中
|
||
stop_on_entry = 1;
|
||
stackdeep_counter = 0;
|
||
call_lua_function(L, "SendMsgWithStack", 0,"stopOnEntry");
|
||
}
|
||
}
|
||
if (is_hit == 1 || stop_on_entry == 1) {
|
||
return;
|
||
}
|
||
step_process(L, ar);
|
||
check_hook_state(L, last_source, ar_current_line, ar_def_line, ar_lastdef_line, ar->event);
|
||
}
|
||
}
|
||
|
||
//结束hook
|
||
extern "C" int endHook(lua_State *L)
|
||
{
|
||
cur_hook_state = DISCONNECT_HOOK;
|
||
lua_sethook(L, NULL, 0, 0);
|
||
all_breakpoint_map.clear();
|
||
return 0;
|
||
}
|
||
|
||
static luaL_Reg libpdebug[] = {
|
||
{ "sync_breakpoints", sync_breakpoints }, //lua同步断点给c,同步发生在新增、删除断点,连接开始时
|
||
{ "lua_set_hookstate", lua_set_hookstate }, //lua设置hook状态。lua中发生状态切换时,同步到C
|
||
{ "lua_set_runstate", lua_set_runstate }, //同步运行状态
|
||
{ "sync_debugger_path", sync_debugger_path }, //同步debugger文件路径
|
||
{ "sync_tools_path", sync_tools_path }, //同步debugger文件路径
|
||
{ "sync_config", sync_config }, //同步日志等级
|
||
{ "sync_cwd", sync_cwd }, //同步cwd
|
||
{ "sync_file_ext", sync_file_ext }, //同步文件后缀
|
||
{ "sync_getLibVersion", sync_getLibVersion }, //hook version
|
||
{ "sync_bp_hit", sync_bp_hit }, //set BP lua向C同步状态
|
||
{ "sync_tempfile_path", sync_tempfile_path }, //sync_tempfile_path
|
||
{ "endHook", endHook }, //结束hook,停止调试
|
||
{ "get_libhook_state", get_libhook_state },
|
||
{ "get_last_source", get_last_source },
|
||
{ "clear_pathcache", clear_pathcache },
|
||
{ "set_bp_twice_check_res", set_bp_twice_check_res },
|
||
{ "sync_lua_debugger_ver", sync_lua_debugger_ver },
|
||
{ NULL, NULL }
|
||
};
|
||
|
||
#ifdef USE_SOURCE_CODE
|
||
extern "C" void pdebug_init(lua_State* L) {
|
||
debug_auto_stack _tt(L);
|
||
//把libhook压入_G,里面方法填上
|
||
lua_newtable(L);
|
||
for (size_t i = 0; i < sizeof(libpdebug) / sizeof(luaL_Reg); i++) {
|
||
if (libpdebug[i].name == NULL) {
|
||
break;
|
||
}
|
||
lua_pushcfunction(L, libpdebug[i].func);
|
||
lua_setfield(L, -2, libpdebug[i].name);
|
||
}
|
||
lua_setglobal(L, "luapanda_chook");
|
||
}
|
||
#else // !USE_SOURCE_CODE
|
||
|
||
#ifdef _WIN32
|
||
#define DEBUG_API extern "C" __declspec(dllexport)
|
||
#else
|
||
#define DEBUG_API extern "C"
|
||
#endif
|
||
|
||
DEBUG_API int luaopen_libpdebug(lua_State* L)
|
||
{
|
||
#ifdef _WIN32
|
||
load(L);
|
||
#endif
|
||
|
||
#ifdef _WIN32
|
||
|
||
#if LUA_VERSION_NUM == 501
|
||
// 在windows平台编译时,luaL_register等是函数指针,运行时查找。
|
||
if(luaL_register != NULL){
|
||
luaL_register(L, "libpdebug", libpdebug);
|
||
}
|
||
#elif LUA_VERSION_NUM > 501
|
||
if(lua_createtable != NULL && luaL_setfuncs != NULL){
|
||
lua_newtable(L);
|
||
luaL_setfuncs(L, libpdebug, 0);
|
||
}
|
||
#endif // LUA_VERSION_NUM
|
||
|
||
#else // !defined(_WIN32))
|
||
|
||
#if LUA_VERSION_NUM == 501
|
||
// 在macOS编译时,luaL_register等是函数,定义在lua.h中。
|
||
luaL_register(L, "libpdebug", libpdebug);
|
||
#elif LUA_VERSION_NUM > 501
|
||
lua_newtable(L);
|
||
luaL_setfuncs(L, libpdebug, 0);
|
||
#endif // LUA_VERSION_NUM
|
||
|
||
#endif // ifdef _WIN32
|
||
|
||
return 1;
|
||
}
|
||
|
||
#endif // USE_SOURCE_CODE
|
||
|
||
//WIN32下函数处理方法
|
||
#if !defined(USE_SOURCE_CODE) && defined(_WIN32)
|
||
//slua-ue template function
|
||
#if LUA_VERSION_NUM > 501
|
||
template<typename T, typename RET>
|
||
RET callLuaFunction(lua_State *L) {
|
||
return getInter()->T(L);
|
||
}
|
||
|
||
template<typename T, T>
|
||
struct Invoker;
|
||
|
||
template<typename T, typename RET, typename... ARGS, RET(T::*F)(ARGS...)>
|
||
struct Invoker<RET(T::*)(ARGS...), F> {
|
||
|
||
static RET invoke(const ARGS&... args) {
|
||
return (getInter()->*F)(args...);
|
||
}
|
||
|
||
};
|
||
|
||
template<typename T, typename... ARGS, void(T::*F)(ARGS...)>
|
||
struct Invoker<void(T::*)(ARGS...), F> {
|
||
|
||
static void invoke(const ARGS&... args) {
|
||
return (getInter()->*F)(args...);
|
||
}
|
||
|
||
};
|
||
|
||
template<typename T, T t>
|
||
struct LuaCppBinding;
|
||
|
||
template<typename T, typename RET, typename... ARGS, RET(T::*F)(ARGS...)>
|
||
struct LuaCppBinding<RET(T::*)(ARGS...), F> {
|
||
static RET luaCFunction(ARGS... args) {
|
||
return Invoker<decltype(F), F>::invoke(args...);
|
||
}
|
||
};
|
||
|
||
template<typename T, typename... ARGS, void(T::*F)(ARGS...)>
|
||
struct LuaCppBinding<void(T::*)(ARGS...), F> {
|
||
|
||
static void luaCFunction(ARGS... args) {
|
||
return Invoker<decltype(F), F>::invoke(args...);
|
||
}
|
||
};
|
||
#endif // LUA_VERSION_NUM > 501
|
||
|
||
void Slua_UE_find_function()
|
||
{ //slua - ue Lua 5.3
|
||
#if LUA_VERSION_NUM > 501
|
||
#define SLUABINDING(f) LuaCppBinding<decltype(f), f>::luaCFunction;
|
||
lua_version = SLUABINDING(&slua::LuaInterface::lua_version);
|
||
lua_pushstring = SLUABINDING(&slua::LuaInterface::lua_pushstring);
|
||
lua_gettop = (luaDLL_gettop)SLUABINDING(&slua::LuaInterface::lua_gettop);
|
||
lua_settop = (luaDLL_settop)SLUABINDING(&slua::LuaInterface::lua_settop);
|
||
lua_pcallk = SLUABINDING(&slua::LuaInterface::lua_pcallk);
|
||
lua_pushnumber = (luaDLL_pushnumber)SLUABINDING(&slua::LuaInterface::lua_pushnumber);
|
||
luaL_checklstring = SLUABINDING(&slua::LuaInterface::luaL_checklstring);
|
||
lua_tolstring = (luaDLL_tolstring)SLUABINDING(&slua::LuaInterface::lua_tolstring);
|
||
lua_type = (luaDLL_type)SLUABINDING(&slua::LuaInterface::lua_type);
|
||
lua_tointegerx = (luaDLL_tointegerx)SLUABINDING(&slua::LuaInterface::lua_tointegerx);
|
||
lua_pushnil = (luaDLL_pushnil)SLUABINDING(&slua::LuaInterface::lua_pushnil);
|
||
lua_getfield = (luaDLL_getfield)SLUABINDING(&slua::LuaInterface::lua_getfield);
|
||
lua_next = (luaDLL_next)SLUABINDING(&slua::LuaInterface::lua_next);
|
||
lua_getinfo = (luaDLL_getinfo)SLUABINDING(&slua::LuaInterface::lua_getinfo);
|
||
lua_sethook = (luaDLL_sethook)SLUABINDING(&slua::LuaInterface::lua_sethook);
|
||
luaL_checknumber = SLUABINDING(&slua::LuaInterface::luaL_checknumber);
|
||
lua_createtable = (luaDLL_createtable)SLUABINDING(&slua::LuaInterface::lua_createtable);
|
||
luaL_setfuncs = SLUABINDING(&slua::LuaInterface::luaL_setfuncs);
|
||
lua_getglobal = SLUABINDING(&slua::LuaInterface::lua_getglobal);
|
||
lua_toboolean = (luaDLL_toboolean)SLUABINDING(&slua::LuaInterface::lua_toboolean);
|
||
#endif
|
||
}
|
||
|
||
void general_find_function() {
|
||
//slua, xlua
|
||
#if LUA_VERSION_NUM == 501
|
||
luaL_register = (luaLDLL_register)GetProcAddress(hInstLibrary, "luaL_register");//501
|
||
lua_pcall = (luaDLL_pcall)GetProcAddress(hInstLibrary, "lua_pcall");//501
|
||
lua_tointeger = (luaDLL_tointeger)GetProcAddress(hInstLibrary, "lua_tointeger");//501
|
||
#endif
|
||
luaL_checkinteger = (luaDLL_checkinteger)GetProcAddress(hInstLibrary, "luaL_checkinteger");
|
||
lua_version = (luaDLL_version)GetProcAddress(hInstLibrary, "lua_version");
|
||
lua_pushstring = (luaDLL_pushstring)GetProcAddress(hInstLibrary, "lua_pushstring");
|
||
lua_gettop = (luaDLL_gettop)GetProcAddress(hInstLibrary, "lua_gettop");
|
||
lua_settop = (luaDLL_settop)GetProcAddress(hInstLibrary, "lua_settop");
|
||
lua_pushnumber = (luaDLL_pushnumber)GetProcAddress(hInstLibrary, "lua_pushnumber");
|
||
luaL_checklstring = (luaDLL_checklstring)GetProcAddress(hInstLibrary, "luaL_checklstring");
|
||
lua_tolstring = (luaDLL_tolstring)GetProcAddress(hInstLibrary, "lua_tolstring");
|
||
lua_type = (luaDLL_type)GetProcAddress(hInstLibrary, "lua_type");
|
||
lua_pushnil = (luaDLL_pushnil)GetProcAddress(hInstLibrary, "lua_pushnil");
|
||
lua_getfield = (luaDLL_getfield)GetProcAddress(hInstLibrary, "lua_getfield");
|
||
lua_next = (luaDLL_next)GetProcAddress(hInstLibrary, "lua_next");
|
||
lua_getinfo = (luaDLL_getinfo)GetProcAddress(hInstLibrary, "lua_getinfo");
|
||
lua_sethook = (luaDLL_sethook)GetProcAddress(hInstLibrary, "lua_sethook");
|
||
luaL_checknumber = (luaDLL_checknumber)GetProcAddress(hInstLibrary, "luaL_checknumber");
|
||
lua_pushinteger = (luaDLL_pushinteger)GetProcAddress(hInstLibrary, "lua_pushinteger");
|
||
lua_toboolean = (luaDLL_toboolean)GetProcAddress(hInstLibrary, "lua_toboolean");
|
||
//5.3
|
||
#if LUA_VERSION_NUM > 501
|
||
lua_pcallk = (luaDLL_pcallk)GetProcAddress(hInstLibrary, "lua_pcallk");
|
||
lua_tointegerx = (luaDLL_tointegerx)GetProcAddress(hInstLibrary, "lua_tointegerx");
|
||
lua_createtable = (luaDLL_createtable)GetProcAddress(hInstLibrary, "lua_createtable");
|
||
luaL_setfuncs = (luaDLL_setfuncs)GetProcAddress(hInstLibrary, "luaL_setfuncs");
|
||
lua_getglobal = (luaDLL_getglobal)GetProcAddress(hInstLibrary, "lua_getglobal");
|
||
#endif
|
||
}
|
||
|
||
void load(lua_State* L) {
|
||
|
||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
|
||
if (INVALID_HANDLE_VALUE == hSnapshot)
|
||
{
|
||
//load fail
|
||
return;
|
||
}
|
||
MODULEENTRY32 mi;
|
||
mi.dwSize = sizeof(MODULEENTRY32);
|
||
BOOL bRet = Module32First(hSnapshot, &mi);
|
||
while (bRet)
|
||
{
|
||
#if LUA_VERSION_NUM > 501
|
||
// find slua-ue dll
|
||
dll_GetLuaInterface interPtr = (dll_GetLuaInterface)GetProcAddress(mi.hModule, "GetLuaInterface");
|
||
if (interPtr != NULL) {
|
||
hInstLibrary = mi.hModule;
|
||
getInter = interPtr;
|
||
Slua_UE_find_function();
|
||
break;
|
||
}
|
||
#endif
|
||
// find general lua dll
|
||
void* versionPtr = (luaDLL_sethook)GetProcAddress(mi.hModule, "lua_sethook");
|
||
if (versionPtr != NULL) {
|
||
hInstLibrary = mi.hModule;
|
||
general_find_function();
|
||
break;
|
||
}
|
||
|
||
//travel next dll
|
||
bRet = Module32Next(hSnapshot, &mi);
|
||
}
|
||
}
|
||
#endif
|