Listing Loaded Lua Modules in OpenResty or Nginx Processes
It’s a common requirement to inspect some persistent Lua data structures when troubleshooting online Lua applications, including those run by OpenResty or Nginx (in the case of Nginx, it’s still using OpenResty’s core component, the Lua Nginx modules). The Lua data structures might get embedded deep into some loaded Lua modules, referenced by some Lua functions via upvalues, etc. The traditional way of inspecting such Lua data is to expose special APIs that serialize and dump out the corresponding Lua data. But it’s much more flexible and efficient to examine the Lua data inside the running processes via dynamic tracing technologies.
OpenResty XRay provides a Lua-compatible language called YLua that supports writing such tools for inspecting any Lua data inside a running process using a 100% non-invasive way. This tutorial demonstrates a straightforward use case of dumping out the names of all loaded Lua modules in a running OpenResty and LuaJIT application. We’ll show more complex use cases with YLua in future tutorials.
System Environment
Here we use a Red Hat Enterprise Linux 8 system as an example. Any Linux distributions supported by OpenResty XRay should work equally fine, like Ubuntu, Debian, Fedora, Rocky, Alpine, etc.
We use an unmodified open-source OpenResty binary build as the target application. You can use any OpenResty or Nginx binaries, including those compiled by yourself. No special build options, plugins, or libraries are needed in your existing server installation or processes. It is the beauty of dynamic tracing technologies. It’s genuinely non-invasive.
We also have the OpenResty XRay’s Agent daemon
running on the same system and have the command-line utilities from the openresty-xray-cli
package installed and configured.
Names of Loaded Lua Modules
Using Standard Tools
The easiest way to dump the loaded Lua modules in a target process is to use the standard OpenResty XRay tool lj-loaded-modules
:
Use the ps
cmd to find the nginx worker process we want to analyze. Here it is 1046
.
$ ps aux | grep nginx
1 S root 1059 1 0 80 0 - 73941 - 15:03 ? 00:00:00 nginx: master process /usr/local/openresty/nginx/sbin/nginx
5 S nobody 1046 1059 0 80 0 - 82123 - 15:03 ? 00:00:00 nginx: worker process
Now let’s run the standard analyzer lj-dump-loaded-mods
with the orxray
command-line utility from the openresty-xray-cli
package. The -p
option specifies the target process ID (or PID) to be analyzed.
# 4096 is the target process's PID
$ orxray analyzer run lj-loaded-modules -p 4096
Start tracing...
jit: table
math: table
coroutine: table
debug: table
...
Go to https://x5vrki.xray.openresty.com/targets/68/history/751715 for charts.
Using Custom YLua Tools
The standard tool is implemented in the YLua language under the hood. Let’s see how we can recreate the tool with YLua ourselves. We know that loaded Lua modules are always cached in the package.loaded
in the LuaJIT VM (and the standard Lua 5.1 interpreter’s VM). The keys are the module names while the values are the module data. Now we can just create a new YLua source file with the name, say, my-dump-loaded-mods.ylua
:
probe process.begin
for k, v in pairs(package.loaded) do
print(k, ": ", type(v))
end
exit()
end
Here the probe
keyword defines a new probe handler for the probe-point process.begin
, triggered when the tool is attached to the target processes1. And then, we dump the Lua modules by iterating the package.loaded
Lua table in YLua, which is precisely the same syntax as Lua. After printing out the module names and their data types, we call exit()
to end the whole tracing process. And we can use the run-ylua
command-line utility from the openresty-xray-cli
package to run this YLua source file.
Inspecting an OpenResty or Nginx Lua Application
Use the ps
cmd to find the nginx worker process we want to analyze. Here it is 1046
.
$ ps aux | grep nginx
1 S root 1059 1 0 80 0 - 73941 - 15:03 ? 00:00:00 nginx: master process /usr/local/openresty/nginx/sbin/nginx
5 S nobody 1046 1059 0 80 0 - 82123 - 15:03 ? 00:00:00 nginx: worker process
Now let’s run the previous YLua source file with the run-ylua
command-line utility from the openresty-xray-cli
package. The -p
option specifies the target process ID (or PID) to be analyzed.
$ run-ylua -p 1046 my-dump-loaded-mods.ylua
Start tracing...
resty.core.uri: table
resty.core.exit: table
resty.core.base64: table
resty.core.request: table
resty.core.response: table
string: table
pgmoon: table
openresty_org.controller: table
ndk: table
table: table
resty.core.utils: table
resty.lrucache: table
table.new: function
debug: table
table.clear: function
...
package: table
math: table
resty.core.base: table
jit.opt: table
ngx: table
_G: table
resty.core.var: table
resty.core.worker: table
resty.core.regex: table
resty.core.shdict: table
resty.core.time: table
resty.core.hash: table
We omitted most of the output for brevity. We can check out the total number by piping the output to the wc -l
command:
$ run-ylua -p 1046 my-dump-loaded-mods.ylua | wc -l
Start tracing...
38
Most of the modules are of the table
type. Usually, a Lua module is represented by a Lua table, after all. A few of the modules are unique in that they are of the function
type. For example, table.new
is a function typed module implemented specifically by LuaJIT.
io
,os
,string
,table
,math
,debug
are standard Lua modules defined by the Lua 5.1 language.jit
,bit
,ffi
,jit.opt
,table.new
,table.clear
are standard LuaJIT modules.resty.*
,coroutine
,ndk
,ngx
are modules introduced by OpenResty.
Inspecting standalone LuaJIT applications
We can also inspect a standalone luajit
process without Nginx or OpenResty involved.
First, find the PID of the luajit
process as always:
$ ps aux | grep luajit
root 3371845 0.0 0.0 4428 2524 pts/3 S 22:17 0:00 luajit t.lua
And then, we use this PID, 3371845
, to run the run-ylua
command:
$ orxray analyzer run lj-loaded-modules -p 3371845
Start tracing...
jit: table
math: table
coroutine: table
debug: table
os: table
_G: table
package: table
string: table
jit.opt: table
bit: table
io: table
table: table
As we see, standalone luajit
programs usually load many fewer Lua modules by default.
We can check out the total number by piping the output to the wc -l
command:
$ orxray analyzer run lj-loaded-modules -p 3371845 | wc -l
Start tracing...
12
Running Directly in the Web Console
The user may choose to execute any of the tools covered in this tutorial directly
in the web console of OpenResty XRay. They
can even be triggered automatically upon interesting events like high CPU usage.
The command-line utilities from the openresty-xray-cli
are handy for demonstration
purposes. And they are also easy to automate and integrate into other systems
by the DevOps and SRE people.
Tracing Applications inside Containers
OpenResty XRay tools support tracing containerized applications transparently. Both Docker and Kubernetes (K8s) containers work transparently. Just as with normal application processes, the target containers do not need any applications or extra privileges. The OpenResty XRay Agent daemon should run outside the target containers (like in the host operating system directly or in its own privileged container).
Let’s see an example. We first check the container name or container ID with
the docker ps
command.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4465297209d9 openresty/openresty:1.19.3.1-2-alpine-fat "/usr/local/openrest…" 18 months ago Up 3 minutes angry_mclaren
Here the container name is angry_mclaren
. We can then find out the target
process’s PID in this container.
$ docker top angry_mclaren
UID PID PPID C STIME TTY TIME CMD
root 3373047 3373026 0 22:23 ? 00:00:00 nginx: master process /usr/local/openresty/bin/openresty -g daemon off;
nobody 3373101 3373047 0 22:23 ? 00:00:00 nginx: worker process
The PID for the openresty
worker process is 3373101
. We then run the OpenResty
XRay analyzer against this PID as usual.
$ orxray analyzer run lj-loaded-modules -p 3373101
Start tracing...
table: table
ngx: table
resty.core.var: table
table.new: function
resty.core.regex: table
resty.core.shdict: table
resty.core.time: table
...
table.clear: function
resty.core.worker: table
resty.lrucache: table
io: table
OpenResty XRay is also able to automatically detect long-running processes as “applications” of a particular type (like “OpenResty”, “Python”, etc.).
How The Tools are Implemented
All the tools are implemented in the Y language.
OpenResty XRay executes them with either the Stap+2 or eBPF3 backends
of OpenResty XRay, both of which use the 100%
non-invasive dynamic tracing
technologies based on the Linux kernel’s uprobes
and kprobes
facilities.
The YLua scripts are first compiled
down to the Y language and
then further down to the executable dynamic tracing
tools.
We don’t require any collaborations from the target applications and processes. No log data or metrics data is used or needed. We directly analyze the running processes' process space in a strictly read-only way. And we also never inject any byte-code or other executable code into the target processes. It is 100% clean and safe.
The Overhead of the Tools
The dynamic-tracing tools demonstrated in this tutorial are very efficient and suitable for online execution.
When the tools are not running and actively sampling, the overhead on the system and the target processes are strictly zero. We never inject any extra code or plugins into the target applications and processes; thus, there’s no inherent overhead.
During sampling, the overhead on average request latency and throughput is usually not measurable for the tools covered in this tutorial. The tools here only do a one-off inspection anyway.
About The Author
Yichun Zhang (Github handle: agentzh), is the original creator of the OpenResty® open-source project and the CEO of OpenResty Inc..
Yichun is one of the earliest advocates and leaders of “open-source technology”. He worked at many internationally renowned tech companies, such as Cloudflare, Yahoo!. He is a pioneer of “edge computing”, “dynamic tracing” and “machine coding”, with over 22 years of programming and 16 years of open source experience. Yichun is well-known in the open-source space as the project leader of OpenResty®, adopted by more than 40 million global website domains.
OpenResty Inc., the enterprise software start-up founded by Yichun in 2017, has customers from some of the biggest companies in the world. Its flagship product, OpenResty XRay, is a non-invasive profiling and troubleshooting tool that significantly enhances and utilizes dynamic tracing technology. And its OpenResty Edge product is a powerful distributed traffic management and private CDN software product.
As an avid open-source contributor, Yichun has contributed more than a million lines of code to numerous open-source projects, including Linux kernel, Nginx, LuaJIT, GDB, SystemTap, LLVM, Perl, etc. He has also authored more than 60 open-source software libraries.
-
Note that this is very different from attaching through the
ptrace()
system call as in GDB. Here is the dynamic tracing attachment, which is much safer and faster. ↩︎ -
Stap+ is OpenResty Inc’s greatly enhanced version of SystemTap. ↩︎
-
This is actually the greatly enhanced version of OpenResty Inc.’s eBPF implementation called ORBPF. ↩︎