前面两篇我们对性能做了一个优化,接下来继续来丰富调试器的特性。
我们前面提到过,函数内并不是所有行都是有效行,空行和注释行就不是有效行。我们之前在添加断点的时候,并没有对行号进行检查,任何行号都能成功添加断点。所以如果添加的断点行号是无效的,那么永远也不会断到那里。但是钩子里并不知道它是无效的,call事件仍然会以为函数有断点从而启动line事件,造成CPU的浪费。
所以本篇,我们将对断点的行号进行检查,对于不在函数范围内的行号直接添加断点失败;在函数范围内的行号则自动修正为下一个有效的行号;另外支持不指定行号,默认为函数的第一个有效行。
Lua中如何实现类似gdb的断点调试--05优化断点信息数据结构
在上一篇04优化钩子事件处理中,我们在钩子函数中引入了call和return事件的处理,对性能进行了优化。
细心的同学可能已经发现了,我们的hook函数中call事件和line都需要对整个断点表进行遍历,这其中其实是存在着一些冗余的。因为call事件只关心函数是否有断点,而line事件则只关心本函数内有哪些断点。所以我们可以想办法优化一下断点信息的数据结构,进一步提升性能。
Lua中如何实现类似gdb的断点调试--04优化钩子事件处理
在第一篇的01最小实现中,我们实现了一个断点调试的最小实现,在设置钩子函数时只加了line事件,显然这会对性能有很大的影响。而后来两篇02通用变量打印和03通用变量修改及调用栈回溯则是提供了一些辅助的调试接口,并没有对钩子函数进行修改。
我们本篇将在钩子中引入call和return事件的处理,尝试对性能进行优化。
Lua中如何实现类似gdb的断点调试--03通用变量修改及调用栈回溯
在前面两篇01最小实现及02通用变量打印中,我们已经实现了设置断点、删除断点及通用变量打印接口。
本篇将继续新增两个辅助的调试接口:调用栈回溯打印接口、通用变量设置接口。前者打印调用栈的回溯信息,后者可以方便地修改变量的值,支持局部变量、upvalue以及全局的_ENV
中的变量。
Lua中如何实现类似gdb的断点调试--02通用变量打印
在前一篇01最小实现中,我们实现了Lua断点调试的的一个最小实现。我们编写了一个模块,提供了两个基本的接口:设置断点和删除断点。
虽然我们已经支持在断点进行变量的打印,但是需要自己指定层数以及变量索引,使用起来不是很方便。要进行upvalue打印的话,操作会更加麻烦。为了提升调试的方便性,我们决定封装一个通用的变量打印函数,可以通过变量名查找到对应变量的值进行打印。支持局部变量、upvalue以及全局的_ENV
中的变量。
Lua中如何实现类似gdb的断点调试--01最小实现
说到Lua代码调试,最常用的方法应该就是加一堆print进行打印。print大法虽好,但其缺点也是显而易见的。比如效率低下,需要修改原有函数内部代码,在每个需要的地方添加print语句,运行一次只能获取一次信息,下次换个地方又得重新添加print语句。而且有时候,事先并不知道该去哪打印、或者打印什么内容,需要通过运行中获取的信息才能确定。
当print大法无法满足我们的需求时,就需要类似断点调试这样更高级的调试功能。本文将从零开始编写一个Lua调试器,实现类似gdb的断点调试功能。
本文代码已开源至Github,欢迎watch/star😘。
一篇文章搞懂密码学基础及SSL/TLS协议
SSL协议是现代网络通信中重要的一环,它提供了传输层上的数据安全。为了方便大家的理解,本文将先从加密学的基础知识入手,然后展开对SSL协议原理、流程以及一些重要的特性的详解,最后会扩展介绍一下国密SSL协议的差异、安全性以及TLS 1.3的关键新特性。
限于篇幅以及个人知识水平,本文不会涉及过于细节的内容。特别地,本文将不涉及算法的具体原理,也不涉及实际代码的实现。而是试图以图表等直观的方式来了解基本的原理以及流程。
自己动手实现Lua--实现TAILCALL指令
最近在看《自己动手实现Lua—虚拟机、编译器和标准库》。这是本挺不错的书,通过学习此书能够对Lua语言有比较深刻的理解,此外还可以对如何自己实现一门脚本语言有直观的认识。对于想学习Lua的同学,安利一下这本书。
废话不多说,书中留了一个作业,让读者自己实现TAILCALL
指令,实现尾调用的优化。本文就算是交作业吧。
OpenResty Lua钩子调用完整流程
前面一篇文章介绍了Openresty Lua协程调度机制,主要关心的是核心调度函数run_thread()
内部发生的事情,而对于外部的事情我们并没有涉及。本篇作为其姊妹篇,准备补上剩余的部分。本篇将通过一个例子,完整介绍OpenResty中Lua钩子的调用流程,包括初始化阶段的工作、新连接进来时如何进入钩子、I/O等待时如何出去、事件触发时如何恢复、钩子正常执行结束时的操作、钩子内出错的情况。本文同样是基于stream-lua
模块的代码。
Openresty Lua协程调度机制
写在前面
OpenResty(后面简称:OR)是一个基于Nginx和Lua的高性能Web平台,它内部集成大量的Lua API以及第三方模块,可以利用它快速搭建支持高并发、极具动态性和扩展性的Web应用、Web服务或动态网关。
OR最大的特点就是,将Lua协程与Nginx事件驱动模型及非阻塞I/O结合起来。使用户可以在handler中使用 同步但是依然是非阻塞 的方式编写其应用代码,而无需关心底层的协程调度以及与Nginx事件驱动模型的交互。
本文将先从总体上介绍OR的协程调度机制,然后结合源码以及Lua栈的情况来详细了解各个部分是如何实现的,包括其异常保护、协程初始化、协程的恢复和执行、协程的挂起、协程的执行结束、协程出错的情况。
本文主要关注调度函数内部的逻辑,如果想了解外部的调用流程。可以参看Openresty Lua钩子调用完整流程