Skip to content

Debugging

Lars Urban edited this page Jun 11, 2020 · 20 revisions

Table of contents

  1. Prerequisite
  2. Command Line Interface
    1. Real World example
  3. Graphical User Interface
    1. Zerobrane Studio setup
      1. Plugin Installation
    2. Real World example

Sometimes we need a better way to Debug our FWL Scripts i describe 2 easy ways how this can be done.

Prerequisite

We have to start XPlane from a command-line
How to is well written here --> https://www.x-plane.com/kb/using-command-line-options/

Example for MacOS :

../X-Plane.app/Contents/MacOS/X-Plane --no_fbos

Command Line Interface (CLI)

In this example we use a easy and lightweight Solution from slembcke (Scott Lembcke) · GitHub .
Copy from this Project https://github.com/slembcke/debugger.lua only debugger.lua into FWL Modules Folder.

Make sure your Script Folder is empty or the Debugger starts , after FWL is loaded (Loading Screen XPLANE)

Adapt your own Script like this: (this Text was copied from the project)

local dbg = require("debugger")
-- Consider enabling auto_where to make stepping through
-- code easier to follow.
dbg.auto_where = 2

function foo()
    -- Calling dbg() will enter the debugger on the next executable line,
    -- right before it calls print().
    -- Once in the debugger, you will be able to step around and 
    -- inspect things.
    dbg()
    print("Woo!")

    -- Maybe you only want to start the debugger on a certain condition.
    -- If you pass a value to dbg(), it works like an assert statement.
    -- The debugger only triggers if it's nil or false.
    dbg(5 == 5) -- Will be ignored

    print("Fooooo!")
end

foo()

-- You can also wrap a chunk of code in a dbg.call() block.
-- Any error that occurs will cause the debugger to attach.
-- Then you can inspect the cause.
-- (NOTE: dbg.call() expects a function that takes no parameters)
dbg.call(function()
    -- Put some buggy code in here:
    local err1 = "foo" + 5
    local err2 = (nil).bar
end)

-- Lastly, you can override the standard Lua error() and 
-- assert() functions if you want:
-- These variations will enter the debugger instead of aborting 
-- the program.
-- dbg.call() is generally more useful though.
local assert = dbg.assert
local error = dbg.error

If we encounter now a "Bug" in our Script, we can see it in our Terminal:

debugger.lua: Loaded for LuaJIT 2.0.5
..Resources/plugins/FlyWithLua/Scripts/tutorial.lua:88 in chunk at    
..Resources/plugins/FlyWithLua/Scripts/tutorial.lua:0    
debugger.lua>

And now ? Type "h" and press Enter --> you can see all possible Shortcuts for Debugging

debugger.lua> h
  <return> => re-run last command
  c(ontinue) => continue execution
  s(tep) => step forward by one line (into functions)
  n(ext) => step forward by one line (skipping over functions)
  f(inish) => step forward until exiting the current function
  u(p) => move up the stack by one frame
  d(own) => move down the stack by one frame
  w(here) [line count] => print source code around the current line
  e(val) [statement] => execute the statement
  p(rint) [expression] => execute the expression and print the result
  t(race) => print the stack trace
  l(ocals) => print the function arguments, locals and upvalues.
  h(elp) => print this message
  q(uit) => halt execution
debugger.lua>

You can write "w" in your Terminal and press again Enter, you see then the starting point after your dbg() Statement

debugger.lua> w
  83        (c =  Continue execution)
  84    ]]
  85    end
  86
  87    dbg()
  88 => print(str1)
  89
  90    func1()
  91    print(str2)
  92
  93    func2()
debugger.lua>

Follow the whole Tutorial.lua from this project to warm up with this CLI Debugger. You can set statements, print variables ... everything what you need to debug your script.


Real World example

FLW can not tell us whats going on, we see nothing in detail in our XPLANE log nor information in FlyWithLua_Debug.txt ! We only see this on our Screen and the Script Name himself.

image-20200603201732168 and this image-20200603201833862

FlyWithLua_Debug.txt

[000948] FlyWithLua Debug Info: The Lua stack contains the following elements: [000949] caught (...) exception

XPLANE log.txt

FlyWithLua Info: Lua engine (re)started. LUA_RUN =19, SDK_VERSION = 301, XPLANE_VERSION = 11410, XPLANE_LANGUAGE = German and XPLANE_HOSTID = 1 FlyWithLua Info: HID access initialized. FlyWithLua Info: FlyWithLua.ini full path /Volumes/SSD2go PKT/X-Plane 11 stable/Resources/plugins/FlyWithLua/Internals/FlyWithLua.ini FlyWithLua Info: Discovered 10 HID devices. FlyWithLua Info: Load ini file. FlyWithLua Info: Searching for Lua script files FlyWithLua Info: Sorting Lua script files FlyWithLua Info: Start loading script file /Volumes/SSD2go PKT/X-Plane 11 stable/Resources/plugins/FlyWithLua/Scripts/xsil_autoupdater.lua FlyWithLua Error: CopyDataRefsToLua() failed, can't execute script file: /Volumes/SSD2go PKT/X-Plane 11 stable/Resources/plugins/FlyWithLua/Scripts/xsil_autoupdater.lua FlyWithLua Error: Unable to load script file /Volumes/SSD2go PKT/X-Plane 11 stable/Resources/plugins/FlyWithLua/Scripts/xsil_autoupdater.lua FlyWithLua Info: Loading time for all scripts is 0.028823 sec. FlyWithLua Debug Info: The Lua stack contains the following elements: caught (...) exception

FlyWithLua Error: Unable to load script file 
..Resources/plugins/FlyWithLua/Scripts/xsil_autoupdater.lua

Now that we know it is located in script --> xsil_autoupdater.lua , we add our Debug Stuff inside the script ! Remember ... we have no idea when the script crashed, we use always in this case the first lines, before we call any code !

--DEBUG START
local dbg = require("debugger")
local assert = dbg.assert
local error = dbg.error
dbg()
--DEBUG END

Reload all LUA script files --> Menü --> Plugin -->FlyWithLua

Have a look in your Terminal now ! Enter "w" --> and we see our break point

debugger.lua> w
  35    local assert = dbg.assert
  36    local error = dbg.error
  37    dbg()
  38    --DEBUG END
  39
  40 => local http_socket = require("socket.http")
  41    local ltn12 = require("ltn12")
  42    local lfs = require("lfs_ffi") -- https://github.com/sonoro1234/luafilesystem
  43    local jjjLib1 = require("jjjLib1")
  44    local graphics = require("graphics")
  45    local toast = require("toast")
debugger.lua>

Enter "n" for:

n(ext) => step forward by one line (skipping over functions)

until we get the line of code where the Script has an issue. HINT --> ( [return] - re-run last command ) Again our Green and Red Message inside X-Plane ... but we see also we jumped out in line 45

debugger.lua> n
..Resources/plugins/FlyWithLua/Scripts/xsil_autoupdater.lua:41 in chunk at ..Resources/plugins/FlyWithLua/Scripts/xsil_autoupdater.lua:0
debugger.lua>
..Resources/plugins/FlyWithLua/Scripts/xsil_autoupdater.lua:42 in chunk at ..Resources/plugins/FlyWithLua/Scripts/xsil_autoupdater.lua:0
debugger.lua>
..Resources/plugins/FlyWithLua/Scripts/xsil_autoupdater.lua:43 in chunk at ..Resources/plugins/FlyWithLua/Scripts/xsil_autoupdater.lua:0
debugger.lua>
..Resources/plugins/FlyWithLua/Scripts/xsil_autoupdater.lua:44 in chunk at ..Resources/plugins/FlyWithLua/Scripts/xsil_autoupdater.lua:0
debugger.lua>
..Resources/plugins/FlyWithLua/Scripts/xsil_autoupdater.lua:45 in chunk at ..Resources/plugins/FlyWithLua/Scripts/xsil_autoupdater.lua:0
debugger.lua>

After we know that our toast lib can`t be loaded we can capsulate this function with a dbg.call

--DEBUG START
local dbg = require("debugger")
local assert = dbg.assert
local error = dbg.error
dbg()
--DEBUG END

local http_socket = require("socket.http")
local ltn12 = require("ltn12")
local lfs = require("lfs_ffi") -- https://github.com/sonoro1234/luafilesystem
local jjjLib1 = require("jjjLib1")
local graphics = require("graphics")
--local toast = require("toast")

dbg.call(function()
    -- Put some buggy code in here:
    local toast = require("toast")
end)

We reload our script and starting again with "n(ext)" until we hit something like this

debugger.lua>
Debugger stopped on error in dbg.call(): "..plugins/FlyWithLua/Scripts/xsil_autoupdater.lua:49: 
module 'toast' not found:\
\9no field package.preload['toast']\
\9no file './toast.lua'\
\9no file '/usr/local/share/luajit-2.0.5/toast.lua'\
\9no file '/usr/local/share/lua/5.1/toast.lua'\
\9no file '/usr/local/share/lua/5.1/toast/init.lua'\
\9no file '..Resources/plugins/FlyWithLua/Internals/toast.lua'\
\9no file '..Resources/plugins/FlyWithLua/Internals/toast/init.lua'\
\9no file '..Resources/plugins/FlyWithLua/Modules/toast.lua'\
\9no file '..Resources/plugins/FlyWithLua/Modules/toast/init.lua'\
\9no file './toast.so'\
\9no file '/usr/local/lib/lua/5.1/toast.so'\
\9no file '/usr/local/lib/lua/5.1/loadall.so'\
\9no file '..Resources/plugins/FlyWithLua/Internals/toast_64.so'\
\9no file '..Resources/plugins/FlyWithLua/Modules/toast_64.so'"

Reason for this ? In my case forgot to copy my new Module in our FWL Module Folder ;-)

Ok now after this Module was copied ... again the same message ... caught (...) exception ... We don´t change our Debug lines, god thing is our debugger follow the code ! Again press "n" with dbg.call in our script

debugger.lua>
Debugger stopped on error in dbg.call(): "error loading module 'toast' from file '..Resources/plugins/FlyWithLua/Modules/toast.lua':\
\9....plugins/FlyWithLua/Modules/toast.lua:81: unexpected symbol near '='"

After this detail we can inspect our line of Code

image-20200603225620087

This was a pure Typo in this case inside a Module. --> correct was :

if toast[i] ~= nil then

Both topics were identified within 1 minute ! Without debugger much longer ;-)


Graphical User Interface (GUI)

There are many graphical tools to search for errors. I focused on ZeroBrane Studio for this part. The reason for this is quickly explained. It exists for every platform, we don't have to provide anything else in our operating system and it's open source !

After the software is installed we can start debugging immediately. We only need to call "MobDebug" in our script . This is also provided by Zerobrane.

ZeroBrane Studio supports remote debugging that allows to debug arbitrary Lua applications. The application may be running on the same or a different computer from the one running the IDE instance (the debugger is using a socket interface to interact with the application).

It can be found as an example on a MacOS at:

/Applications/ZeroBraneStudio.app/Contents/ZeroBraneStudio/lualibs/mobdebug

We have now two options:

  1. copy the script mobdebug.lua into our modules folder (should happen after every software update)

or

  1. we show our script/environment where it is
local ZBS = "/Applications/ZeroBraneStudio.app/Contents/ZeroBraneStudio"
package.path = package.path .. ";" .. ZBS .. "/lualibs/mobdebug/?.lua;"
local mobdebug = require("mobdebug")

with the last information in our script we can start the Remote debugging. This is a example start when the Game + Zerobrane is on the same PC.

mobdebug.start()

If you start Zerobrane from a other PC in your Network choose this (change ip / domain name to your Environment)

mobdebug.start("192.168.8.101")

Zerobrane Studio setup

You can extend Zerobrane Studio with useful plugins. Download from ZeroBranePackage the whole Project and follow the Installation. ARCHIVE AS ZIP

Plugin Installation

To install a plugin, copy its .lua file to ZBS/packages/ or HOME/.zbstudio/packages/ folder (where ZBS is the path to ZeroBrane Studio location and HOME is the path specified by the HOME environment variable). The first location allows you to have per-instance plugins, while the second allows to have per-user plugins. The second option may also be preferrable for Mac OSX users as the ZBS/packages/ folder may be overwritten during an application upgrade.

Useful ZeroBrane Studio packages:

analyzeall.lua
autodelimitersurroundselection.lua
autoindent.lua
autostartdebug.lua
colourpicker.lua
documentmap.lua
highlightselected.lua
projectsettings.lua
referencepanel.lua
striptrailingwhitespace.lua
syntaxcheckontype.lua
todoall.lua

When you open Zerobrane Studio for the first time we need some settings that need to be made.

Choose a project folder --> as I work locally I choose the FlyWithLua folder inside plugins. Menu --> Project --> Project Directory --> choose

image-20200611142516179

Create Project specific Preferences --> Menu --> Edit --> Preferences --> Settings: Project Project specific means, automatic creation inside our project folder .zbstudio/user.lua

--[[--
  Use this file to specify **User** preferences.
  Review [examples](+/Applications/ZeroBraneStudio.app/Contents/ZeroBraneStudio/cfg/user-sample.lua) or check [online documentation](http://studio.zerobrane.com/documentation.html) for details.
--]]--
--https://studio.zerobrane.com/doc-general-preferences#debugger

-- to automatically open files requested during debugging
editor.autoactivate = true

--enable verbose output
--debugger.verbose=true

--[[--
specify how print results should be redirected in 
the application being debugged (v0.39+). Use 'c' for ‘copying’ 
(appears in the application output and the Output panel), 
'r' for ‘redirecting’ (only appears in the Output panel), 
or 'd' for ‘default’ (only appears in the application output). 
This is mostly useful for remote debugging to specify how the 
output should be redirected.
--]]--
debugger.redirect="c"

-- to force execution to continue immediately after starting debugging;
-- set to `false` to disable (the interpreter will stop on the first line
-- or when debugging starts); some interpreters may use `true` or
-- `false` by default, but can be still reconfigured with this setting.
debugger.runonstart = true

-- FlyWithLua.ini version 2.7.6 build 2018-10-24
-- Where to search for modules.

-- use this to evaluate your project folder , 
-- select the print function / right Mousebutton --> Evaluate in Console
--print(ide.filetree.projdir)
--print(ide:GetProject())

local ZBSProjDir = "/Volumes/SSD2go PKT/X-Plane 11 stable/Resources/plugins/FlyWithLua"
INTERNALS_DIRECTORY = ZBSProjDir .. "/Internals/"
MODULES_DIRECTORY = ZBSProjDir .. "/Modules/"

package.path = table.concat({
    package.path,
    INTERNALS_DIRECTORY .. "?.lua",
    INTERNALS_DIRECTORY .. "?/init.lua",
    MODULES_DIRECTORY .. "?.lua",
    MODULES_DIRECTORY .. "?/init.lua",
}, ";")
package.cpath = table.concat({
    package.cpath,
    INTERNALS_DIRECTORY .. "?.ext",
    MODULES_DIRECTORY .. "?.ext",
}, ";")

-- Produce a correct name pattern for binary modules for OS 
-- and architecture.
-- This resolves clash between OS X and Linux binary modules 
-- by requiring "lib" prefix for Linux ones.
local library_pattern = "?_64."

if SYSTEM == "IBM" then
    library_pattern = library_pattern .. "dll"
elseif SYSTEM == "APL" then
    library_pattern = library_pattern .. "so"
else
    library_pattern = "lib" .. library_pattern .. "so"
end

package.cpath = package.cpath:gsub("?.ext", library_pattern)

--ZBS Plugin syntaxcheckontype.lua
syntaxcheckontype = {marker =
  {ch = wxstc.wxSTC_MARK_CHARACTER+('>'):byte(),
   fg = {0, 0, 0}, bg = {192, 192, 255}}
}

Always Save your changes ! Restart Zerobrane and debugging can start.

Real World example

we have the same case as in our CLI Debug example, that means first of all we have to prepare our script. Open Zerobrane and edit your script.

--DEBUG START
local ZBS = "/Applications/ZeroBraneStudio.app/Contents/ZeroBraneStudio"
package.path = package.path .. ";" .. ZBS .. "/lualibs/mobdebug/?.lua;"
local mobdebug = require("mobdebug")
mobdebug.start()
--DEBUG END

Second part is to Set a Breakpoint , quit easy with a Mouse click after the Line Number image-20200611213028385

Reload all Scripts in FlyWithLua and you will see our Debuger has stopped for us image-20200611213855825

We can now navigate in our code image-20200611214454047

The Start is always the same with a unknow failure --> Step Over until we see something like this image-20200611215004794

Couldn't activate file '/Volumes/SSD2go PKT/X-Plane 11 stable/Resources/plugins/FlyWithLua/flywithlua_debug()' for debugging; continuing without it.

This is our Information for something goes wrong , but we have seen our Line of code ! We can now Inspect this line, we mark the require Statement and click on our rigth Mouse Button --> Evaluate in Console

image-20200611215643610

After we copy now the Module in our Modules Folder --> same again --> restart debug session Same Line with problems --> Evaluate in Console

local toast = require("toast")
error loading module 'toast' from file '/Volumes/SSD2go PKT/X-Plane 11 stable/Resources/plugins/FlyWithLua/Modules/toast.lua':
    ...11 stable/Resources/plugins/FlyWithLua/Modules/toast.lua:81: unexpected symbol near '='
    [C]: at 0x010fdbbbe0
    [C]: in function 'require'

We jump now to our Toast script and change our line 81

-- before
if toast[i] ~== nil then 
-- after
if toast[i] ~= nil then

And as always we can also catch with a xpcall and start a debugger session for example (function copied from debugger.lua and adapted)

-- Works like pcall(), but invokes the debugger on an error.
function dbg_call(f, ...)
    return xpcall(f, function(err)
    require("mobdebug").start()
    print( "ERROR: ", err )
        return err
    end, ...)
end


dbg_call(function()
    -- Put some buggy code in here:
    local toast = require("toast")
end)

Use the Stack and Watch Window for your analysis ! image-20200611223527193