A high-performance, lightweight code coverage tool for Lua (supports Lua 5.3+). Built with a C++ data collection engine and a Go-based AST parser to provide precise, low-overhead line and function coverage statistics.
- Features
- Architecture & Workflow
- Prerequisites
- Build and Installation
- Reproducible Build & Tests
- Usage Guide
- Analyzing & Visualizing Results
- Automated Coverage Service (CluaHelper)
- Related Projects
- License
- 🚀 High Performance & Low Overhead: The core data collection library is written in C++ with minimal Lua hook overhead, keeping the performance impact on host applications to an absolute minimum.
- 🔌 Flexible Integration: Can be imported directly within scripts using
require "libclua"or dynamically injected into running production/testing processes via hookso without requiring server restarts or code changes. - 🎯 Precise AST Analysis: The parser is written in Go and analyzes the Lua syntax tree (AST) to compute exact file-level and function-level line coverage metrics and execution counts.
- ⏸️ Pause & Resume: Supports
cl.pause()andcl.resume()to selectively exclude critical or non-target code segments during testing. - 📊 Rich Visual Reports: Exports to standard LCOV format, allowing seamless HTML report generation with
genhtml. - 🌐 Automated Coverage Service: Provides
clua_helperwith Client, Server, and Generator modes to automate injection, aggregation, and web hosting across multiple testing environments.
+-----------------------+
| Target Process (Lua) |
| +-----------------+ |
| | libclua.so | | == (write coverage) ==> test.cov (Binary Data)
| +-----------------+ | |
+-----------------------+ |
v
+-----------------+
| clua (CLI) |
+-----------------+
| |
(Terminal Stats) v v (Export LCOV)
Function & File test.info
Coverage Rates |
v (genhtml)
HTML Dashboard
- OS: Linux (x86_64)
- Lua: 5.3 or higher (development headers and libraries such as
lua.hrequired) - C++ Compiler: GCC / Clang (with C++11 support)
- CMake: 3.13 or higher
- Go: 1.19 or higher (must match
go.modand CI;go.sumis required for a reproducible module build) - LCOV (optional, for HTML reports): install via
apt install lcovoryum install lcov
From a clean checkout, the supported entry point is:
makeThis builds libclua.so, clua, and clua_helper in the current directory. The same steps can be run manually:
cmake -S . -B build
cmake --build build
cp build/libclua.so .This generates libclua.so. If lua.h is not on the default include path (for example it lives in /usr/include/lua5.4), pass -DLUA_INCLUDE_DIR=/usr/include/lua5.4 to cmake.
go build -mod=readonly -o clua clua.goThis compiles the clua command-line utility used to parse binary coverage files. -mod=readonly uses the pinned versions and checksums in go.mod / go.sum.
go build -mod=readonly -o clua_helper clua_helper.goThis compiles clua_helper, which provides client, server, and report generator services.
Go 1.19 is the required language version in go.mod, this README, and CI (go-version-file: go.mod). After changing Go module dependencies, run go mod tidy or make tidy and commit the updated go.sum. Do not disable module checksum verification.
An automated end-to-end test exercises the documented workflow: load libclua.so from test.lua, write a coverage file, parse it with clua, and export LCOV. The test asserts that the coverage file is produced, that parse results include the expected file and function coverage, and that the LCOV output contains valid TN / SF / DA / LF / LH / end_of_record records.
make test
# or, after `make`:
go test -mod=readonly -count=1 ./e2eLoad and start the coverage collector inside your Lua scripts:
-- 1. Load libclua.so
local cl = require "libclua"
-- 2. Start recording coverage
-- Parameter 1: Output coverage file path (default: "luacov.data")
-- Parameter 2: Periodic auto-save interval in seconds (0 = disabled, flushes only on cl.stop())
cl.start("test.cov", 5)
-- (Optional) Pause and resume coverage collection as needed
-- cl.pause()
-- cl.resume()
-- 3. Execute your workload
do_something()
-- 4. Stop recording and flush data to disk
cl.stop()Note: Ensure
LUA_CPATHincludes the directory containinglibclua.so, for example:LUA_CPATH="./?.so;;" lua test.lua.
Using hookso, you can inject coverage collection into a running process without stopping or recompiling it:
-
Obtain the
lua_State*pointer in the target process: For example, inspect the first argument oflua_settop(L)(assuming the process ID is$PID):./hookso arg $PID liblua.so lua_settop 1 # Example output: 123456
-
Inject and load
libclua.so:./hookso dlopen $PID ./libclua.so -
Call
start_covto begin profiling: Equivalent to invokingstart_cov(L, "./test.cov", 5)inside the process:./hookso call $PID libclua.so start_cov i=123456 s="./test.cov" i=5
-
Call
stop_covto stop profiling and flush data: Equivalent to invokingstop_cov(L):./hookso call $PID libclua.so stop_cov i=123456
Parse the generated coverage file with the clua utility:
./clua -i test.covExample output:
total points = 27, files = 1
coverage of /home/project/cLua/test.lua:
local cl = require "libclua"
cl.start("test.cov", 5)
1 function test1(i)
10 if i % 2 then
10 print("a "..i)
else
print("b "..i)
end
11 end
1 function test2(i)
40 if i > 30 then
19 print("c "..i)
else
21 print("d "..i)
end
41 end
1 function test3(i)
51 if i > 0 then
51 print("e "..i)
else
print("f "..i)
end
52 end
test4 = function(i)
local function test5(i)
12 print("g "..i)
15 end
15 for i = 0, 3 do
12 test5(i)
end
4 end
102 for i = 0, 100 do
101 if i < 10 then
10 test1(i)
91 elseif i < 50 then
40 test2(i)
else
51 test3(i)
end
end
4 for i = 0, 2 do
3 test4(i)
end
1 cl.stop()
/home/project/cLua/test.lua total coverage 78% 22/28
/home/project/cLua/test.lua function coverage [function test1(i)] 66% 2/3
/home/project/cLua/test.lua function coverage [function test2(i)] 100% 3/3
/home/project/cLua/test.lua function coverage [function test3(i)] 66% 2/3
/home/project/cLua/test.lua function coverage [test4 = function(i)] 75% 3/4
/home/project/cLua/test.lua function coverage [local function test5(i)] 100% 1/1
The number preceding each line indicates its execution count, while blank entries indicate unexecuted lines. Overall file coverage and per-function coverage summaries are displayed at the end.
-
Export coverage data to standard LCOV info format:
./clua -i test.cov -lcov test.info
-
Generate an HTML dashboard using LCOV's
genhtml:genhtml -o htmltest test.info
-
Click into individual source files for line-by-line coverage details:

Tip: Multiple
.infofiles can be merged together usinglcov(lcov -a run1.info -a run2.info -o merged.info) to combine coverage from multiple test runs.
clua_helper provides an automated coverage collection system supporting Client, Server, and Generator (Gen) modes. Run ./clua_helper -h to see all available flags.
Runs on the target host, discovers target processes, injects libclua.so, monitors source code changes, and reports coverage files periodically to the server:
./clua_helper -type client \
-bin target_binary_name \
-getluastate "command_to_get_lua_state, e.g.: liblua.so lua_settop 1" \
-path /path/to/source/code \
-server http://server_ip:8877Runs on the central server, receives coverage reports from clients, stores files locally, and serves the generated static HTML dashboard:
./clua_helper -type server -port 8877Runs on the central server to aggregate received coverage files, merge results with source code, and generate static HTML reports:
./clua_helper -type gen \
-covpath /path/to/server/saved/cov \
-path /path/to/local/source/code \
-clientpath /path/to/client/source/codeAfter generation, visit http://server_ip:8877/static/ in your browser to inspect the live dashboard:

This project is licensed under the MIT License.