1. 程式人生 > >Clang Plugin 之 Debug

Clang Plugin 之 Debug

前面一篇文章 LLVM & Clang 入門 講了如何編寫一個 Clang 外掛,然後將外掛編譯成一個.dylib的動態連結庫。整合到 Xcode 中就可以看到效果(正確的結果)。

在得到正確結果的過程中,必不可少的一步就是Debug,沒有任何程式是一蹴而就的,除非你printf一個"Hello, World!",說不定你的world還寫成了word

在使用Plugin的模式下我們是不能打斷點進行 Debug 的,但是我們可以在程式碼中加日誌,然後在終端中執行命令看日誌進行 Debug。這種低效率(Low)的方式是你想要的嗎?顯然不是。

我們只需要把.dylib動態庫變成可執行檔案

就能打斷點 debug。LibTooling 或許是一個不錯的選擇。使用 LibTooling 的話,我們只需要改動很少部分的程式碼就可以。

LibTooling 簡介

LibTooling 是一個獨立的庫,它允許使用者很方便地搭建屬於你自己的編譯器前端工具,它基於 C++ 介面,提供給使用者強大全面的 AST 解析和控制能力,同時由於它與 Clang 的核心過於接近導致它的版本相容能力比 libclang 差得多,Clang 的變動很容易影響到 LibTooling。libTooling 還提供了完整的引數解析方案,可以很方便的構建一個獨立的命令列工具。

建立 LibTooling 專案及程式碼調整

為了方便,我們直接建立一個可執行的LibTooling專案,我們可以建立一個名為QTPluginTooling的專案。

  1. 建立過程跟 建立外掛 步驟差不多,前面 3 步都是一樣的,只需要把QTPlugin替換為QTPluginTooling就可以。

  2. 只是在第 4 步略有不同,QTPluginTooling目錄下的CMakeLists.txt的檔案內容為

    set(LLVM_LINK_COMPONENTS
        Support
    )
    
    add_clang_executable(QTPluginTooling
        QTPluginTooling.cpp
    )
    
    target_link_libraries(QTPluginTooling
        PRIVATE
        clangAST
        clangBasic
        clangDriver
        clangFormat
        clangLex
        clangParse
        clangSema
        clangFrontend
        clangTooling
        clangToolingCore
        clangRewrite
        clangRewriteFrontend
    )
    
    if
    (UNIX) set(CLANGXX__LING_OR_COPY create_symlink) else() set(CLANGXX_LINK_OR_COPY copy) endif() 複製程式碼

    tooling_cmakeLists

  3. llvm_xcode目錄下執行$ cmake -G Xcode ../llvm,重新生成一下Xcode專案。Tooling專案在 Xcode 的Clang executables目錄下可以找到。

  4. 將之前 Plugin 的程式碼複製過來,新增三個標頭檔案

    #include "clang/Tooling/CommonOptionsParser.h"
    #include "clang/Frontend/FrontendActions.h"
    #include "clang/Tooling/Tooling.h"
    複製程式碼
  5. 新增一個名稱空間

    using namespace clang::tooling;
    複製程式碼
  6. QTASTAction的繼承改為繼承至ASTFrontendAction

  7. FrontendPluginRegistry註冊外掛的方式註釋。更改為main()函式方式

    static llvm::cl::OptionCategory OptsCategory("QTPlugin");
    int main(int argc, const char **argv) {
        CommonOptionsParser op(argc, argv, OptsCategory);
        ClangTool Tool(op.getCompilations(), op.getSourcePathList());
        return Tool.run(newFrontendActionFactory<QTPlugin::QTASTAction>().get());
    }
    複製程式碼

最後整個檔案的內容可以在 QTPluginTooling.cpp 看到。

輸入源

如果這時候就run的話則會直接退出。這是因為沒有“輸入源”。我們可以在QTPluginToolingScheme加入。

/Users/laiyoung_/Desktop/Plugin/ViewController.m
--
-isysroot
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk
-isystem
-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/10.0.0/include
-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1
-I/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include
-F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks
複製程式碼

引數解釋

上面在--後面的引數,是傳遞給CICompilation DataBase的,而不是這個命令列工具本身的。比如我們的ViewController.m,因為有#import <UIKit/UIKit.h>這麼一條語句,以及繼承了UIViewController,那麼語法分析器(Sema)讀到這裡的時候就需要知道UIViewController的定義是從哪裡來的,換句話說就是它需要找到定義UIViewController的地方。怎麼找呢?通過指定的-I-F這些引數指定的目錄來尋找。--後面的引數,可以理解為如果你要編譯ViewController.m需要什麼引數,那麼這個後面就要傳遞什麼引數給我們的 QTPlugin,否則就會看到Console裡打出找不到xxx定義或者xxx.h檔案的錯誤。當然因為一般的編譯指令,會有-c引數指定原始檔,但是--後面並不需要,因為我們在--前面就指定了。--這種傳參的方式還有另外一種方法,使用-extra-arg="xxxx"的方式指定編譯引數,這樣就不需要--了。

-extra-arg="-Ixxxxxx"
-extra-arg="-Fxxxxxx"
-extra-arg="-isysroot xxxxxx"
xxxxxx表示的路徑
複製程式碼

最終效果:

參考文章

如有內容錯誤,歡迎 issue 指正。

Example

轉載請註明出處!