Scite Debug

lua-users home
wiki

scite-debug 簡介

scite-debug 完全用 SciTE Lua 編寫,與標準 5.1 相容。唯一絕對不可或缺的 C 程式碼是產生器,它會擷取互動式指令列除錯器,例如 GDB,讓 scite-debug 可以將指令寫入其中。輸出導向至指定的一般函式,使用適當的主執行緒技術;在 Windows 上,我將訊息傳遞至偵錯器執行緒的 wndproc,而在 GTK 上使用非同步輸出機制。在兩個平台上,我們以整行方式擷取偵錯器輸出,意即在除錯會發出提示訊息的互動式程式時,會有限制。然而,在 Windows 端,我們會為偵錯的程式要求一個獨立的控制台視窗,用以區分程式的輸入和輸出,並對 Windows 控制台應用程式除錯。

程式完全有可能沒有可除錯符號。遇到這種情況時,表示我們僅對除錯程式載入的共用函式庫感興趣。因此,我們通常會在這些函式庫設定中斷點,但 GDB 必須接受這種做法。如果 debug.target 開頭為 '[n]',則 GDB 會載入一個任意的小符號檔案 (由 stubby.so|.dll 提供)。此時,它很樂意將任何未解析的中斷點要求視為暫存。(請注意,您需要 GDB 的 vs 6 版本;如果您使用的是 MinGW,請下載較新的 GDB,如果您仍在執行 vs 5 的話。)

可使用 debug.environment 屬性為除錯目標設定環境變數。這個變數是一個以分號分隔的 VAR=VAL 配對清單。

一般的受控偵錯器會話需要設定中斷點,並開始執行程式,並使用 檢視 | 參數 指定的參數。在執行程式之前設定的中斷點會寫入組態檔案;在 GDB 的情況下,它稱為 'prompt.cmd'。這個檔案也是一次按照我們喜好設定偵錯器的機會。例如,對於 GDB,我們以換行設定提示,關閉輸出分頁等等。

在中斷點上停止需要一種相符的模式,這種模式可以相符偵錯器輸出並萃取檔案和列。GDB 有 Emacs 使用的特有模式 (-f),它會以這種格式在每個中斷處列印出完整路徑:(26)(26)<路徑>:<列>,其中 (26) 是十進制 26 表示的 ASCII 字元。這提供了一個很明顯的目標模式。其他偵錯器並不適合自動驅動,因此我們必須想辦法解決。

一旦進入「中斷」模式,我們當然可以直接設定和取消中斷點。

可以從 Output 視窗輸入任何偵錯器指令。例如,您可以使用「watch var」在 GDB 中設定變數監控。

一個常見的任務是評估表達式。Alt-I(檢查)將報告游標處的表達式的值。scite-debug夠智能,知道a.bp->c->x是完整的表達式,但如果選取一段表達式,它會採用之。提示工具評估使用相同的启发式和相同的偵錯器指令,但會將輸出重新導向到提示工具而不是 SciTE 輸出視窗。任何指令的輸出都可以重新導向到任意函式,這為更圖形的介面開啟了有趣的機會。(不過,這必須再等一會兒。)

對於 C/C++,scite-debug會注意到一個評估為指標的表達式,並會嘗試除去那個指標的引用。也就是說,如果 GDB 回報的值為$11 = (A *)0xFFF23EEscite-debug將會嘗試評估*$11並收集結果。

C++ 偵錯的另一個有用功能是某些標準模式的自動簡化,特別是std::string會提取 char 指標值。這些規則會遞迴應用,所以包含std::string值的結構將會以更人性化的形式呈現。請注意,這些簡化極度仰賴使用的精確實現!目前只支援 g++,所以模式相當穩定。這是適合使用者自訂的地方。

scite-debug通常知道如何詮釋堆疊追蹤;在需要的層級上按兩下,會將你帶到那個框架並將你放到對應的原始碼行。堆疊追蹤可以用Ctrl+Alt+S明確顯示,如果可能,當錯誤發生時會自動顯示。Alt+UAlt+D對應於向上的和向下的層級。

整合的 Lua 和 C/C++ 偵錯

這項有用(可能也是獨特的)功能是透過在 GDB 內的某個行程中執行 clidebug Lua 偵錯器來實作的。clidebug現在有一個「GDB 模式」,模仿 GDB 的指令集和輸出。因此不論我們是在 GDB 或clidebug中中斷,luagdb都會將我們帶到原始碼中的那個位置。

可能出現兩種情況;第一種情況是主程式本身就是 Lua(或其親戚),並理解 -e 和 -l 命令列選項,這是預設值。第二種情況是主程式是一個已內嵌 Lua 的程式。debug.target 變數必須顯示主程式和 Lua 指令碼名稱。一些範例可以明確說明這一點

# the host is Lua on the path, and has no debugging symbols.
debug.target=[n]:gdb;lua;mytest.lua

# the host is SciTE - the [h] indicates that it isn't Lua
debug.target=[n]:gdb;SciTE[h];/home/steve/{{scite-debug}}/extman.lua

# a debug version of Lua.
debug.target=:gdb;/home/steve/lua-5.1.3/src/lua;/home/steve/tests/testlfs.lua

如果主程式不是 Lua,你必須在要偵錯的指令碼中自己初始化 clidebug。在這個片段中,將路徑替換為適當的位置。

local path = ';/home/steve/{{scite-debug}}/lua_clidebugger/?.'
package.path = package.path .. path .. 'lua'
package.cpath = package.cpath .. path .. 'so'
WIN=false
GDB=true
require "debugger"
io.stdout:setvbuf("no")
pause('debug')

要偵錯 Windows 上的 GUI 程式,例如 SciTE,需要有特別的建置,至少當你想要偵錯 Lua 指令碼時需要。通常 GUI 程式(「子系統:Windows」)沒有標準輸入和輸出,但它們可以重新建置。以 SciTE 為例,我用「-lgdi32 -lcommctl32」取代「-mwindows」;產生的程式會附帶一個難看的黑色主控台,但這個主控台會被偵錯器隱藏。

(另一種只對有 GUI 的 Lua 應用程式除錯的方法為 remDebug,這也獲得 scite-debug 的支援。)

C/C++ 單步執行

luagdb 使我們能夠從 Lua 程式單步執行 C 的延伸模組。要執行此動作,必須在「呼叫」除錯事件中找出我們正在進入的特定 C 函式的位址。dbgl 這個小延伸會查詢 Lua 內部資訊並傳回這個位址。它還提供一個方便的函式,叫做 debug_break(),用來設定中斷點。(這個技巧需要一個對中斷點沒有問題的 GDB 版本。)clidebug會以特殊的字首印出這個位址,並呼叫 dbgl.debug_break(),這個函式會讓 GDB 運作。luagdb 會取出這個位址,並對這個位址發出 GDB 『訊息行數』查詢,以查看是否能找到適用於這個位址的任何除錯訊息。如果有,它會在這個函式的開始處設定一個暫時的中斷點。無論是否如此,它都會發出『繼續』命令,讓我們繼續執行。我們會快取這個結果,這樣就不必一直呼叫『訊息行數』,但是當程式從 Lua 單步執行 C/C++ 時,我們仍會設定暫時的中斷點。這個動作可確保我們總是能跨過 C 函式。

一旦進入 C/C++,只要繼續執行(Alt+R),您就能繼續在 Lua 中單步執行。

這些仍是兩個分開的除錯工作階段,所以有些限制。在執行的程式中設定中斷點可能會造成一些問題,至少在 Windows 中是這樣。沒有整合呼叫堆疊。

dbgl.c 必須找出 Lua C 函式的對應位址

static int c_addr (lua_State *L)
{
    char buff[40];
    CallInfo *ci;
    Closure* cl = NULL;
    for (ci = L->ci - 1; ci > L->base_ci; ci--) {
      if (! f_isLua(ci)) {  // C function!
        cl = clvalue(ci->func);	
      	break;
      }
    }
    if (cl == NULL) {
      lua_pushnil(L);
    } else {
      void *fun = cl->c.f;    
      sprintf(buff,"0x%X",fun);
      lua_pushstring(L,buff);
    }
    return 1;
}

這需要對 Lua 內部運作有一些認識,而且需要非公開 API 的一部份 lstate.h。(所以重新建構 dbgl.c 時,您需要提供包含絕對完整 Lua 標頭集的路徑。)我們會往下檢視閉包的堆疊,找出我們呼叫的 C 函式。


另請參閱:[專案頁面]
最近的變更 · 喜好設定
編輯 · 記錄
最後於 2008 年 3 月 11 日下午 12:40 GMT 編輯 (差異)