前言

昨天更新了一下lfunctimer,主要把hook更改为c api的形式,并且初步加了util和config的扩展

想要试用的同学的话可以点击上面clone下来,或者安装luarocks后执行下面命令安装~

luarocks install --server=http://luarocks.org/manifests/utmhikari lfunctimer

言归正传,利用lua原生的c api做debug相关操作会比lua自带的debug.getinfo来的快许多,我们可以来一探究竟

debug.getinfo源码分析

我们可以从官方下载源码搜索debug.getinfo的实现,此处以版本5.3.5为例。

debug.getinfo对应的源码是ldblib.cdb_getinfo函数,我们可以简单在ldblib.c的末尾中查到~

static const luaL_Reg dblib[] = {// 略过前面{"getinfo", db_getinfo},// 略过后面
};LUAMOD_API int luaopen_debug (lua_State *L) {luaL_newlib(L, dblib);return 1;
}

lua提供一个注册表(Registry)机制去把我们的函数名跟函数对应起来,然后最后要让lua识别这个模块的话,就定义一个函数,名称为luaopen_模块名,然后通过luaL_newlib方法读取前面的luaL_Reg注册表数据变成一个函数名——函数的table就ok了。

debug之类的lua内置库,则在起lua的时候就调用linit.c里的luaL_openlibs方法就载入;而如果是第三方库,你在require的时候,则会通过loadlib.c中的方法去寻找模块有无载入过,如果载入过则用载入过的模块(package.loaded),如果没载入过就载入,并且加到载入过模块中。这部分不再细究啦,有兴趣的同学可以自行发掘,我有空的话也会自己再踩踩坑~

我们再回头看ldblib.cdb_getinfo函数,也就是debug.getinfo对应的实现,代码如下:

// debug.getinfo([thread,] f [,what]): 获取当前运行函数的信息
// thread (optional): 要获取函数所在的lua state(默认当前lua state)
// f: 调用栈层次(0是当前函数,1是当前函数caller,往上类推)
// what (optional):获取的信息有哪些(默认全部)
static int db_getinfo (lua_State *L) {lua_Debug ar;int arg;// 参数检验部分lua_State *L1 = getthread(L, &arg);  // 读取thread参数const char *options = luaL_optstring(L, arg+2, "flnStu");checkstack(L, L1, 3);// 获取func本身/CallInfoif (lua_isfunction(L, arg + 1)) {  /* info about a function? */options = lua_pushfstring(L, ">%s", options);  /* add '>' to 'options' */lua_pushvalue(L, arg + 1);  /* move function to 'L1' stack */lua_xmove(L, L1, 1);}else {  /* stack level */if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) {lua_pushnil(L);  /* level out of range */return 1;}}// 获取附加信息flnStu之类的if (!lua_getinfo(L1, options, &ar))return luaL_argerror(L, arg+2, "invalid option");// push table,作为最后结果lua_newtable(L);  /* table to collect results */if (strchr(options, 'S')) {settabss(L, "source", ar.source);settabss(L, "short_src", ar.short_src);settabsi(L, "linedefined", ar.linedefined);settabsi(L, "lastlinedefined", ar.lastlinedefined);settabss(L, "what", ar.what);}if (strchr(options, 'l'))settabsi(L, "currentline", ar.currentline);if (strchr(options, 'u')) {settabsi(L, "nups", ar.nups);settabsi(L, "nparams", ar.nparams);settabsb(L, "isvararg", ar.isvararg);}if (strchr(options, 'n')) {settabss(L, "name", ar.name);settabss(L, "namewhat", ar.namewhat);}if (strchr(options, 't'))settabsb(L, "istailcall", ar.istailcall);if (strchr(options, 'L'))treatstackoption(L, L1, "activelines");if (strchr(options, 'f'))treatstackoption(L, L1, "func");return 1;  /* return table */
}

可以看到如果在每次调用debug.getinfo会经历参数检验——获取func或CallInfo信息——获取附加信息(what)的过程,每次都会创建一个lua_Debug结构体,在lua中返回的是一个table,相对繁琐一些。为了提高效率,我们可以直接用db_getinfo中的lua_getstacklua_getinfo方法去在我们的c hook中获取函数信息。

用C API重写lfunctimer的hook

在新版lfunctimer中,第一版C Hook实现如下:

#ifndef LFUNCTIMER_UPVAL_IDX
#define LFUNCTIMER_UPVAL_IDX 1
#endif#ifndef LFUNCTIMER_FUNCMAP_UPVAL_IDX
#define LFUNCTIMER_FUNCMAP_UPVAL_IDX 2
#endif// push function name to top of stack
// not the same as builtin "getfuncname" method
static int lfunctimer_getfuncname(lua_State *L) {lua_Debug ar;if (!lua_getstack(L,  LFUNCTIMER_STKLVL, &ar)) {return 0;}// check if function name already existslua_getinfo(L, "f", &ar);lua_pushvalue(L, -1);lua_gettable(L, lua_upvalueindex(LFUNCTIMER_FUNCMAP_UPVAL_IDX));if (lua_toboolean(L, -1)) {lua_remove(L, 1);  // remove funcinforeturn 1;}lua_pop(L, 1);// check if it's a builtin functionlua_getinfo(L, "Sn", &ar);const char *what = ar.what;if (strcmp(what, "C") == 0) {lua_pushfstring(L, "<Builtin> %s", ar.name);} else if (ar.namewhat[0] == '\0') {lua_pushfstring(L, "<%s:%d> ::UNKNOWN::", ar.short_src, ar.linedefined); } else {lua_pushfstring(L, "<%s:%d> %s", ar.short_src, ar.linedefined, ar.name);}// save function name to function maplua_pushvalue(L, 1);lua_pushstring(L, lua_tostring(L, -2));lua_settable(L, lua_upvalueindex(LFUNCTIMER_FUNCMAP_UPVAL_IDX));// remove funcinfolua_remove(L, 1);return 1;
}

在某些情况下(比如loadstring)时,我们希望能够不侵入lua原生api去获取函数名称信息。因此可以这样操作——在hook函数中去上一个栈层次(level=1)的caller,我们就可以getstack后把函数信息暂存在一个lua_Debug结构体中,并在后续依据需求getinfo相应的内容。

获取函数名称的总体逻辑和上一版的lfunctimer(lua hook)基本类似,如果能获取到函数名称,就把函数名称push到lua_State的栈上。在后边的逻辑中,call跟return事件的handler都被分离了出来,这样就显得更加模块化了。

// debug hook of lfunctimer
static int lfunctimer_debug_hook(lua_State *L) {const char *evt = lua_tostring(L, 1);lua_settop(L, 0);// get function nameif (lfunctimer_getfuncname(L) == 0) {return 0;}// print loglua_getfield(L, lua_upvalueindex(LFUNCTIMER_CFG_UPVAL_IDX), "verbose");if (lua_toboolean(L, -1)) {lua_getfield(L, lua_upvalueindex(LFUNCTIMER_UPVAL_IDX), "log");lua_pushfstring(L, "%s: %s", lua_tostring(L, 1), evt);lua_call(L, 1, 0);}lua_pop(L, 1);// dispatch eventsif (evt[0] == 'c') {return handle_call(L);} else if (evt[0] == 'r') {return handle_return(L);}return 0;
}

总结

过后真的要多抽空研读下各种源码,打好基础= =mlgb的,最近为了领导毕业论文的事情,都憔悴了。

【Lua杂谈】debug.getinfo源码分析——使用C API重写lfunctimer相关推荐

  1. 【Android 插件化】VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )

    文章目录 一.目前的 API 现状 二.安装应用源码分析 1.安装按钮执行的操作 2.返回到 HomeActivity 执行的操作 一.目前的 API 现状 下图是 VirtualApp 官方给出的集 ...

  2. nova创建虚拟机源码分析系列之六 api入口create方法

    openstack 版本:Newton 注:博文图片采用了很多大牛博客图片,仅作为总结学习,非商用. 该图全面的说明了nova创建虚机的过程,从逻辑的角度清晰的描述了前端请求创建虚拟机之后发生的一系列 ...

  3. 云客Drupal源码分析之字段API(上)

    字段API提供字段储存.类型化.验证等功能,它是内容实体的基石,是学习实体之前不可忽略的内容,而她又是建立在类型化组件之上的. "字段"含义的区别: 字段API组件中的" ...

  4. 【GitHub探索】python调试利器——pysnooper源码分析

    前言 这次又开了个新坑--GitHub探索,主要内容是试水当期GitHub上较火的repo 虽然top榜上各路新手教程跟经典老不死项目占据了大半江山,但清流总是会有的. 第一期就试水一下pysnoop ...

  5. Spring Ioc 源码分析(一)--Spring Ioc容器的加载

    1.目标:熟练使用spring,并分析其源码,了解其中的思想.这篇主要介绍spring ioc 容器的加载 2.前提条件:会使用debug 3.源码分析方法:Intellj idea debug 模式 ...

  6. android字符显示流程图,Android应用层View绘制流程与源码分析

    1  背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原 ...

  7. NIO - Selector源码分析

    1. 背景 SelectableChannel对象的多路复用器. 可以通过调用Selector.open()方法创建Selector对象.Selector.open()方法会利用系统默认的Select ...

  8. Lua源码分析 - 虚拟机篇 - 语义解析之Opcode执行(18)

    目录 一.虚拟机篇 - 指令执行状态机luaV_execute 二.虚拟机篇 - 状态机的具体实现原理 一.虚拟机篇 - 指令执行状态机luaV_execute 在<Lua源码分析 - 主流程篇 ...

  9. Nmap源码分析(脚本引擎)

    Nmap提供了强大的脚本引擎(NSE),以支持通过Lua编程来扩展Nmap的功能.目前脚本库已经包含300多个常用的Lua脚本,辅助完成Nmap的主机发现.端口扫描.服务侦测.操作系统侦测四个基本功能 ...

  10. Docker源码分析(五):Docker Server的创建

    http://www.infoq.com/cn/articles/docker-source-code-analysis-part5 1.Docker Server简介 Docker架构中,Docke ...

最新文章

  1. SpringBoot应用部署于外置Tomcat容器
  2. easyui复杂表单_EasyUI中实现form表单提交的示例分享
  3. 杂牌手柄模拟xboxone手柄_震了,Xbox One 精英手柄2代摸了一次就不舍得放下了
  4. 懒与馋的平衡:餐饮O2O市场广阔,发展不易
  5. 2021江西高考成绩查询方式6,2021年江西高考成绩6月23日公布 多种查分方式
  6. 【XS128】Link error L1822 symbol _FADD / _FSUB/ _FDIV/ _FMUL.....错误解决的方法
  7. ifix与mysql_基于ODBC技术实现iFix组态软件与关系数据库通讯接口
  8. 人脸关键点检测PFLD论文解读
  9. NRF52840学习历程(十一)幻彩RGB灯之WS2812B
  10. android 系统时间不准确,小米手机时间不准确怎么调小米手机时间不准确怎样调...
  11. 《数据库系统概论》-02 中级SQL 约束、授权、索引
  12. Ubuntu 14.04 T430s 安装指纹识别
  13. js中如何截取小数点后两位数字
  14. KBL410-ASEMI适配高端电源整流桥
  15. PCB模拟信号线与数字信号线布线技巧
  16. 华中科技大学计算机学院培养计划,华中科技大学计算机专业培养计划
  17. Android数据加密传输
  18. C#搭建Json RPC2.0 Server/Client
  19. Ubuntu18.04使用anaconda3安装tensorflow-gpu、pytorch
  20. 美国 Sinclair 电视台网络全面瘫痪,罪魁祸首系勒索软件

热门文章

  1. win7安装VisualStudio2017
  2. 芝麻信用分有哪些计算维度?关于大数据风控的87个问题
  3. 王船山的哲思深度:五百年来,真通天人之故者,船山一人而已
  4. python图片马赛克_利用Python对图片进行马赛克处理
  5. 【Matlab综合设计】开环Buck-Boost升压-降压式变换器Simulink仿真(含仿真模块选择和参数计算过程)
  6. 【视频目标检测数据集收集】B站、YouTube等各大网站视频下载工具:Annie(现更名为lux)的下载与安装教程
  7. 升压电路的原理-原文地址:http://www.52solution.com/basic/1218
  8. 你怎么看:就算老公一毛钱股份都没拿到,在我心里,他依然是最牛逼的创业者...
  9. [NISACTF 2022]上
  10. 如何区分前后端 BUG