Skip to content

GaloisInc/galua

Repository files navigation

Galua

Galua is a Lua debugger implemented in Haskell. It is intended to work in place of the reference Lua implementation. Galua was funded by the Plan X program at DARPA I2O under contract FA8750-13-C-0203.

NOTE: Galua currently uses the reference compiler to turn Lua source into bytecode. This means that the luac executable is a runtime dependency of the debugger when loading Lua source files. This executable path is configurable with the LUAC environment variable.

Galua can operate in stand-alone mode or embedded mode with or without the debugger enabled

  • galua - stand-alone executable interpreter
  • galua-dbg - stand-alone executable interpreter with debugger
  • libgalua.a - embeddable library interpreter
  • libgalua-dbg.a - embeddable library interpreter with debugger

The Galua debugger opens an HTTP server on port 8000 by default. The Lua environment can be inspected by accessing this service with a web browser:

http://localhost:8000

While all modern web browsers are likely to work, Galua is actively tested using Chrome and Safari.

Debugger Layout

Source code tabs

The left side of the debugger has tabs for displaying source code. The first tab will be the currently executing function in the currently executing coroutine. Other tabs will correspond to functions being inspected. These other tabs allows breakpoints to be set or for code to be reviewed.

The back button opens a new source code tab corresponding to the parent function of a tab. The close button closes a source code tab.

Each line of source code can be clicked to reveal the Lua opcodes generated by that line. Operating at the level of opcodes allows the user to carefully step through evaluation of a single line of code.

Hovering over the line numbers reveals a menu for toggling breakpoints as well as jumping directly to an opcode in the current function. Care should be used when jumping because local variables might be uninitialized at that point in the code.

Thread tabs

Coroutines can be inspected via tabs available on the right side of the UI. By default there is only the main thread tab. These tabs allow the user to observe the current state of the callstack for that coroutine as well as the current state of each frame on the call stack. To inspect a coroutine in a thread tab, use the context menu for a thread value to open a new tab.

The Stack panel shows the current call stack frames. Each frame is labeled with the type and name of the function that created the frame. Clicking on a stack frame entry will focus the Variables pane to show the local variables and upvalues for that stack frame. It will also load the first source code tab to show the line of code that was executing in that frame.

When executing a C function the Lua stack for that C frame will be shown in the Variables pane. When a C function calls a Lua C API method, that method and the C arguments to that method will be shown as annotations on the call stack. The combination of seeing the C stack along with seeing the C API methods being called and their arguments can help to debug C functions that tie into Lua.

Monitor pane

The monitor pane allows a user to mark a program location as interesting. This location is treated as a path from the given stack frame through the given local variable slot following a path of table keys if necessary. This allows the user to easily monitor changes to global variables and upvalues even while inside nested function calls.

Console pane

The console pane captures text that is printing using the built-in print function. Each printed argument is represented by a separate box in the same line. Repeated lines are surpressed and the counter next to the line is incremented. The console pane only shows a limited history of the console output.

Inspecting Lua values

Hovering over a value brings up a context menu allowing various queries and operations to be performed on values. These include expanding tables, viewing source code for functions, changing values, setting monitors, changing representations, triggering run-time analysis of functions, and inspecting threads.

Breakpoint list

The list of breakpoints can opened with a button on the right side of the toolbar. From this menu breakpoints can be cleared or source code tabs showing the breakpoints can be opened.

Profiling list

Profiling data can be opened with a button on the right side of the toolbar. This menu shows which functions have been called since the debugger started, how many times they have been called, how much CPU time has been spent by each function individually, and how much time has been spend by each function cumulatively.

"Individual" time is the time spent directly inside a function without calling the time spent in function calls. "Cummulative" time counts the time spent in function calls as well.

Building Galua

Building galua requires at least: stack, git, make, gcc

Building galua can be started by executing the top level build script

$ ./build.sh

The resulting binaries and include files will be available at galua-c/inplace

Configuring Galua's Debugger

When running in debugger mode (using libgalua-dbg) Galua will load the config.txt file from the current directory. This file can set initial breakpoints and parameters of the embedded HTTP server.

Breakpoint on error

By default galua will break whenever an error is thrown. This is configurable with the break-on-error configuration setting. Valid settings are yes and no.

Example:

break-on-error: no

Breakpoints on load

Breakpoints are configured in the breakpoints section of config.txt. This section should be a list. Each element of the list starts with a lua chunk name. Lua prefixes filenames with @. Line numbers can follow the chunk name. The empty chunk name matches the first chunk loaded regardless of name. The 0 line number adds a breakpoint on the first instruction of a chunk, regardless of line number.

As shown below, the breakpoint ["",0] will cause the debugger to pause as soon as the first loaded chunk starts executing.

Example:

breakpoints:
  * ["", 0]                       -- Pause when 1st chunk runs
  * ["@sample.lua", 0]            -- Pause when `sample.lua` runs
  * ["@example.lua", 1, 10, 20]   -- Pause on lines 1, 10, and 20 of `example.lua`

Web interface configuration

The debugger interface web server is configurable via a config.txt file in the current directory. The http section of this file is specifically for HTTP configuration options.

  • The configuration file uses indentation to group sections.
  • Section names and configuration keys are suffixed by colons.
  • Strings are surrounded by double-quotes.
  • Decimal number arguments do not need quotes.
  • Options can be disabled by setting their value to the atom no.
  • Options without arguments can be enable by setting their value to the atom yes.

Example:

http:
  address:       "::"
  port:          9000
  no-access-log: yes

Settings

  • hostname (string) : local hostname
  • address (string) : address webserver should bind to
  • port (number) : port webserver should bind to
  • access-log (string) : filename of access log
  • error-log (string) : filename of error log
  • no-access-log (none) : disable access log
  • no-error-log (none) : disable error log

Analysis

Galua supports an analysis to compute the possible return values and error values that a particular function can throw given the current runtime context. By performing a static analysis of a function at runtime, we're able to consider the actual environment that a function would be running in. Because of the extreme flexibility offered by Lua in ways to load Lua bytecode, this means that it is possible to analyze functions without knowledge in the analysis of the module system or module loading system being used.