跳轉至內容

ROSE 編譯器框架/如何除錯翻譯器

來自 Wikibooks,開放世界中的開放書籍

很少有翻譯器在您完成編碼後就能正常工作。使用 gdb 除錯您的程式碼對於確保程式碼按預期工作至關重要。此頁面展示瞭如何除錯翻譯器的示例。

準備工作

[編輯 | 編輯原始碼]

首先,確保您的 ROSE 安裝和翻譯器使用 -g 選項構建,並且沒有啟用 GCC 最佳化。這將確保最佳地保留所有除錯資訊。

要使用除錯選項配置 ROSE 安裝,您可以將以下選項新增到正常的配置中。

 ../rose/configure—with-CXX_DEBUG=-g --with-C_OPTIMIZE=-O0—with-CXX_OPTIMIZE=-O0  ...

如果您已經構建了 ROSE 但忘記了使用的選項,您可以轉到 ROSE 的構建樹以仔細檢查是否使用了除錯選項。

cd buildDebug/
-bash-4.2$ head config.log

  $ ../sourcetree/configure --with-java=/path/to/java/jdk/1.8.0_131 --with-boost=/path/to/boost/1_60_0/gcc/4.9.3 --with-CXX_DEBUG=-g --with-C_OPTIMIZE=-O0 --with-CXX_OPTIMIZE=-O0 --enable-languages=c++,fortran

在除錯您自己的翻譯器之前,您可能需要仔細檢查 ROSE 的內建翻譯器 (rose-compiler) 是否能夠正確處理您的輸入程式碼。如果不是,您應該向 ROSE 團隊報告錯誤。

如果 rose-compiler 可以處理,但您的自定義翻譯器無法處理。問題可能是由您在翻譯器中引入的自定義引起的。

另一件事是儘可能減少您的輸入程式碼,以便它僅觸發您感興趣的錯誤。這將極大地簡化錯誤查詢過程。除錯處理數千行程式碼的翻譯器非常困難。

GDB 基礎

[編輯 | 編輯原始碼]

gdb 是一個偵錯程式。它為您提供了一個受控的執行環境,以便檢查您的程式是否按預期執行。

從本質上講,它允許您

  • 在受控的除錯環境中執行您的程式:使用 gdb—args <program> <args...>
    • libtool—mode=execute gdb—args <progra> <args...> 用於 libtool 構建的可執行檔案。
  • 在所需的執行點停止
    • 普通斷點(稱為斷點):使用 break <where>,<where> 可以是函式名、行號或檔案:行號。
    • 當給定變數的值發生變化時(稱為觀察點):使用 watch <where>
    • 段錯誤:這將自動發生,因此您可以檢查段錯誤是如何發生的。
    • 斷言失敗:這將自動發生,因此您可以除錯斷言失敗。
  • 檢查甚至更改變數、型別等內容,一旦您的程式在所需的執行點停止。
    • 檢查斷點處的呼叫棧:使用 backtrace 或簡寫 bt。frame <frame#> 轉到您感興趣的棧幀。
    • 檢視斷點附近相關的原始碼:使用 list [+|-|filename:linenumber|filename:function]
    • 檢查變數和表示式的值:使用 print <what>,<what> 可以是任何變數、表示式,甚至函式呼叫。
    • 檢查變數的型別:whatis variable_name
    • 將變數的內容更改為給定值:set <var_name>=<value>
    • 呼叫函式:使用 print function_name,這有助於為某些類物件呼叫一些轉儲函式。
  • 進一步控制執行
    • 一次一步地執行程式:您可以在當前幀中單步執行(next)、向下單步執行到幀中(step)或退出當前棧幀(finish)。
    • 繼續執行直到下一個斷點或觀察點:使用 continue 或簡寫 c
    • 立即從函式返回,傳遞給定值:return <expression>
  • 以及其他操作。

要快速瞭解,您可以檢視網上的速查表

來自 Rob,有一個基於 curses 的包裝器稱為“cgdb” [1]

  • 您將獲得一個拆分視窗:底部是 GDB 控制檯,頂部是語法高亮的原始碼,該程式碼會自動跟蹤您當前的位置並支援 PageUp/PageDn,這比 GDB 的“list”命令更容易使用。
  • 它需要安裝 ncurses-devl 和 readline-devel。

非 ROSE 構建系統構建的翻譯器

[編輯 | 編輯原始碼]

對於某些人來說,這也被稱為原始碼外構建。

如果翻譯器使用 makefile 構建而沒有使用 libtool。翻譯器的除錯步驟只是使用 gdb 的經典步驟。

  • 確保您的翻譯器使用 GNU 除錯選項 編譯-g以便在您的目的碼中存在除錯資訊。

以下是典型除錯會話的步驟

1. 設定斷點

2. 檢查執行路徑以確保程式按預期透過路徑。

3. 檢查本地資料以驗證其值。

# how to print out information about a AST node
#-------------------------------------
(gdb) print n
$1 = (SgNode *) 0xb7f12008

# Check the type of a node
#-------------------------------------
(gdb) print n->sage_class_name()
$2 = 0x578b3af "SgFile"

(gdb) print n->get_parent()
$7 = (SgNode *) 0x95e75b8

# Convert a node to its real node type then call its member functions
#---------------------------
(gdb) isSgFile(n)->getFileName ()

#-------------------------------------
# When displaying a pointer to an object, identify the actual (derived) type of the object 
# rather than the declared type, using the virtual function table. 
#-------------------------------------
(gdb) set print object on
(gdb) print astNode
$6 = (SgPragmaDeclaration *) 0xb7c68008

# unparse the AST from a node
# Only works for AST pieces with full scope information
# It will report error if scope information is not available at any ancestor level.
#-------------------------------------
(gdb) print n->unparseToString()

# print out Sg_File_Info 
#-------------------------------------
(gdb) print n->get_file_info()->display()

示例 1:除錯 AST 遍歷

[編輯 | 編輯原始碼]

我們首先準備遍歷 AST 以查詢迴圈的基於 ROSE 的示例分析器。將其重新命名為 demo.C

我們可以檢視示例分析器的原始碼:cat demo.C 從本質上講,我們可以看到以下內容

  4 #include "rose.h"
  5 
  6 class visitorTraversal : public AstSimpleProcessing
  7    {
  8      public:
  9           visitorTraversal();
 10           virtual void visit(SgNode* n);
 11           virtual void atTraversalEnd();
 12    };
 13 
 14 visitorTraversal::visitorTraversal()
 15    {
 16    }
 17 
 18 void visitorTraversal::visit(SgNode* n)
 19    {
 20      if (isSgForStatement(n) != NULL)
 21         {
 22           printf ("Found a for loop ... \n");
 23         }
 24    }
 25 
 26 void visitorTraversal::atTraversalEnd()
 27    {
 28      printf ("Traversal ends here. \n");
 29    }
 30 
 31 int
 32 main ( int argc, char* argv[] )
 33    {
 34   // Initialize and check compatibility. See Rose::initialize
 35      ROSE_INITIALIZE;
 36 
 37      if (SgProject::get_verbose() > 0)
 38           printf ("In visitorTraversal.C: main() \n");
 39 
 40      SgProject* project = frontend(argc,argv);
 41      ROSE_ASSERT (project != NULL);
 42 
 43   // Build the traversal object
 44      visitorTraversal exampleTraversal;
 45 
 46   // Call the traversal function (member function of AstSimpleProcessing)
 47   // starting at the project node of the AST, using a preorder traversal.
 48      exampleTraversal.traverseInputFiles(project,preorder);
 49 
 50      return 0;
 51    }

基於 ROSE 的工具首先初始化 ROSE(在第 35 行)。然後呼叫 frontend() 函式來解析輸入程式碼並生成以 SgProject 型別專案的根節點的 AST(在第 40 行)。

之後,在第 44 行聲明瞭一個遍歷物件。該物件用於使用先序遍歷遍歷專案的輸入檔案。

遍歷物件基於第 6 行的派生 visitorTraversal 類。此派生類具有成員函式來定義在構造期間(第 14 行)、訪問節點(第 18 行)和遍歷結束時(第 26 行)應該發生什麼。

現在獲取一個示例 makefile 以將原始檔構建為可執行檔案

makefile 應該不言自明。它在安裝路徑中使用 rose-config 來設定編譯器、編譯和連結標誌、庫路徑等各種環境變數。

獲取分析器的示例輸入程式碼

輸入程式碼在第 20 行和第 41 行有兩個 for 迴圈,如 連結 所示

準備用於指定 ROSE 安裝位置的環境變數。

  • export ROSE_HOME=/home/freecc/install/rose_install

構建分析器

  • make -f SampleMakefile

當前目錄下應該有一個名為 demo 的可執行檔案。

最後,執行 demo 分析器來處理示例輸入程式碼

  • ./demo -c inputCode_ExampleTraversals.C

分析器應該找到兩個 for 迴圈並報告遍歷的結束。

Found a for loop ...
Found a for loop ...
Traversal ends here.

除錯翻譯器

[編輯 | 編輯原始碼]

現在讓我們除錯這個簡單的翻譯器。

首先,使用 gdb -args 執行帶有選項的翻譯器

gdb -args ./demo -c inputCode_ExampleTraversals.C

// r means run: It is usually a good practice to run the program without setting breakpoints first to see if it can run normally
//     Or to reproduce an assertion error or seg fault
(gdb) r
Starting program: /home/liao6/workspace/rose/2019-10-31_14-16-05_-0700/myTranslator/./demo -c inputCode_ExampleTraversals.C
...
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Found a for loop ...
Found a for loop ...
Traversal ends here.
[Inferior 1 (process 44697) exited normally]
...
(gdb)

// This program has no errors. So we set a break point at line 22 of demo.C

(gdb) b demo.C:22
Breakpoint 1 at 0x40b0e2: file demo.C, line 22.

// We expect this breakpoint will be hit twice since the input code has only two loops. We try to verify this:
(gdb) r
Starting program: /home/liao6/workspace/rose/2019-10-31_14-16-05_-0700/myTranslator/./demo -c inputCode_ExampleTraversals.C
warning: File "/nfs/casc/overture/ROSE/opt/rhel7/x86_64/gcc/4.9.3/mpc/1.0/mpfr/3.1.2/gmp/5.1.2/lib64/libstdc++.so.6.0.20-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load:/usr/bin/mono-gdb.py".
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, visitorTraversal::visit (this=0x7fffffffb430, n=0x7fffe87db010) at demo.C:22
22                printf ("Found a for loop ... \n");

// Hit breakpoint 1 once, try to continue to see what will happen

(gdb) c
Continuing.
Found a for loop ...

Breakpoint 1, visitorTraversal::visit (this=0x7fffffffb430, n=0x7fffe87db138) at demo.C:22
22                printf ("Found a for loop ... \n");

// Hit breakpoint 1 for the second time, try to continue

(gdb) c
Continuing.
Found a for loop ...
Traversal ends here.
[Inferior 1 (process 46262) exited normally]

// The program terminates now , no more stop at breakpoint 1.

// ----------now we inspect the variable n at the breakpoint 1
// return the program and hit Breakpoint 1
(gdb) r

Breakpoint 1, visitorTraversal::visit (this=0x7fffffffb430, n=0x7fffe87db010) at demo.C:22
22                printf ("Found a for loop ... \n");

//print out the casted n : it is indeed a SgForStatement

(gdb) p isSgForStatement(n)
$1 = (SgForStatement *) 0x7fffe87db010

// Inspect the file info of this ForStatement, understanding where it is coming from in the source code.
 
(gdb) p isSgForStatement(n)->get_file_info()->display()
Inside of Sg_File_Info::display() of this pointer = 0x7fffe94d58b0
     isTransformation                      = false
     isCompilerGenerated                   = false
     isOutputInCodeGeneration              = false
     isShared                              = false
     isFrontendSpecific                    = false
     isSourcePositionUnavailableInFrontend = false
     isCommentOrDirective                  = false
     isToken                               = false
     isDefaultArgument                     = false
     isImplicitCast                        = false
     filename = /home/liao6/workspace/rose/2019-10-31_14-16-05_-0700/myTranslator/inputCode_ExampleTraversals.C
     line     = 20  column = 6
     physical_file_id       = 0 = /home/liao6/workspace/rose/2019-10-31_14-16-05_-0700/myTranslator/inputCode_ExampleTraversals.C
     physical_line          = 20
     source_sequence_number = 8726
$2 = void

檢查 post_construction_intialization()

[編輯 | 編輯原始碼]

在 post_construction_initialization() 處設定斷點對於檢查節點何時建立和/或節點在構造後是否設定了所需欄位很有用。例如,遍歷導致此函式呼叫的呼叫棧(在 gdb 中使用向上和向下命令)可以檢查節點是否設定了父節點或作用域指標。如果沒有,您可以新增此類操作來修復與空指標相關的錯誤。

// ----------- We want to inspect when the SgForStatement nodes are created in the execution
// set a breakpoint at the post_construciton_initialization() method of SgForStatement

(gdb) b SgForStatement::post_construction_initialization()
Breakpoint 2 at 0x7ffff3d6495f: file Cxx_Grammar.C, line 139566.

// Disable Breapoint 1 for now
(gdb) disable 1

(gdb) info breakpoints
Num     Type           Disp Enb Address            What
1       breakpoint     keep n   0x000000000040b0e2 in visitorTraversal::visit(SgNode*) at demo.C:22
        breakpoint already hit 1 time
2       breakpoint     keep y   0x00007ffff3d6495f in SgForStatement::post_construction_initialization() at Cxx_Grammar.C:139566

// run until the Breakpoint 2 is hit
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Breakpoint 2, SgForStatement::post_construction_initialization (this=0x7fffe87db010) at Cxx_Grammar.C:139566
139566       if (p_for_init_stmt == NULL) {

//  use backtrace to check the function call stacks leading to this stop of Breakpoint 2. 
//  You can clearly see the callchain from main() all the way to the breakpoint.

(gdb) bt
#0  SgForStatement::post_construction_initialization (this=0x7fffe87db010) at Cxx_Grammar.C:139566
#1  0x00007ffff54e55d8 in SgForStatement::SgForStatement (this=0x7fffe87db010, test=0x0, increment=0x0, loop_body=0x0)
    at Cxx_GrammarNewConstructors.C:5258
#2  0x00007ffff5bb04ce in EDG_ROSE_Translation::parse_statement (sse=..., existingBasicBlock=0x0)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:49637
#3  0x00007ffff5bbb5ea in EDG_ROSE_Translation::parse_statement_list (sse=..., orig_kind=iek_statement, orig_ptr=0x115f810)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:53079
#4  0x00007ffff5bb0221 in EDG_ROSE_Translation::parse_statement (sse=..., existingBasicBlock=0x7fffe8934010)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:49492
#5  0x00007ffff5c09217 in EDG_ROSE_Translation::parse_function_body<SgFunctionDeclaration> (sse_base=..., p=0x1151ad0, decl=0x7fffe9e21698)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:36262
#6  0x00007ffff5b844fa in EDG_ROSE_Translation::convert_routine (p=0x1151ad0, forceTemplateDeclaration=false, edg_template=0x0,
    optional_nondefiningTemplateDeclaration=0x0) at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:34343
#7  0x00007ffff5b703cf in EDG_ROSE_Translation::parse_routine (sse=..., forceTemplateDeclaration=false, edg_template=0x0, forceSecondaryDeclaration=false)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:29866
#8  0x00007ffff5be6f78 in EDG_ROSE_Translation::parse_global_or_namespace_scope_entity (sse=...)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:64638
#9  0x00007ffff5bea2df in EDG_ROSE_Translation::parse_global_scope (inputGlobalScope=0x7ffff7ec3120, sse=..., skip_ast_translation=false)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:65427
#10 0x00007ffff5bedbee in sage_back_end (sageFile=...) at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:66777
#11 0x00007ffff5beea8a in cfe_main (argc=44, argv=0x702f80, sageFile=...)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:66992
#12 0x00007ffff5beebe7 in edg_main (argc=44, argv=0x702f80, sageFile=...)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:67093
#13 0x00007ffff3c14629 in SgSourceFile::build_C_and_Cxx_AST (this=0x7fffeb45e010, argv=..., inputCommandLine=...)
    at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:5430
#14 0x00007ffff3c1587a in SgSourceFile::buildAST (this=0x7fffeb45e010, argv=..., inputCommandLine=...)
    at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:5983
#15 0x00007ffff3c0e5b7 in SgFile::callFrontEnd (this=0x7fffeb45e010) at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:3119
#16 0x00007ffff3c0b576 in SgSourceFile::callFrontEnd (this=0x7fffeb45e010)
    at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:2137
#17 0x00007ffff3c0a005 in SgFile::runFrontend (this=0x7fffeb45e010, nextErrorCode=@0x7fffffffaadc: 0)
    at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:1606
#18 0x00007ffff3c12924 in Rose::Frontend::RunSerial (project=0x7fffeb555010)
    at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:4613
#19 0x00007ffff3c12593 in Rose::Frontend::Run (project=0x7fffeb555010) at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:4506
#20 0x00007ffff3c0b84d in SgProject::RunFrontend (this=0x7fffeb555010) at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:2209
#21 0x00007ffff3c0bcb2 in SgProject::parse (this=0x7fffeb555010) at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:2334
#22 0x00007ffff3c0b0d4 in SgProject::parse (this=0x7fffeb555010, argv=...)
    at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:2028
#23 0x00007ffff3cbd2e9 in SgProject::SgProject (this=0x7fffeb555010, argv=..., frontendConstantFolding=false) at Cxx_Grammar.C:29114
#24 0x00007ffff645fd54 in frontend (argv=..., frontendConstantFolding=false) at ../../../sourcetree/src/roseSupport/utility_functions.C:628
#25 0x00007ffff645fc10 in frontend (argc=3, argv=0x7fffffffb578, frontendConstantFolding=false)
    at ../../../sourcetree/src/roseSupport/utility_functions.C:590
#26 0x000000000040b152 in main (argc=3, argv=0x7fffffffb578) at demo.C:40
(gdb)

// Again, Breakpoint 2 will be hit twice since we only have two for loops in the input code

(gdb) c
Continuing.

Breakpoint 2, SgForStatement::post_construction_initialization (this=0x7fffe87db138) at Cxx_Grammar.C:139566
139566       if (p_for_init_stmt == NULL) {
(gdb) c
Continuing.
Found a for loop ...
Found a for loop ...
Traversal ends here.
[Inferior 1 (process 47292) exited normally]

為斷點設定條件

[編輯 | 編輯原始碼]

在實際程式碼中,存在數百個相同類型別的物件(例如 SgForStatement)。其中許多來自標頭檔案,並將出現在 AST 中。我們應該只在匹配我們想要檢查的物件時停止。通常,我們可以使用物件的記憶體地址作為條件。

// Add a condition to Breakpoint 2: stop only when the this pointers is equal to a memory address
(gdb) cond 2 (unsigned long)this==(unsigned long)0x7fffe87db138

// run the program: now it will stop only when the condition for Breakpoint 2 is met, skipping all other hits to Breakpoint 2. 
(gdb) r
Starting program: /home/liao6/workspace/rose/2019-10-31_14-16-05_-0700/myTranslator/./demo -c inputCode_ExampleTraversals.C
..
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 2, SgForStatement::post_construction_initialization (this=0x7fffe87db138) at Cxx_Grammar.C:139566
139566       if (p_for_init_stmt == NULL) {

// continue the execution, after doing inspections you want. It should go to the normal termination, skipping other hits to Breakpoint 2. 
(gdb) c
Continuing.
Found a for loop ...
Found a for loop ...
Traversal ends here.
[Inferior 1 (process 47785) exited normally]

使用觀察點

[編輯 | 編輯原始碼]

您可以使用觀察點,在表示式的值發生變化時停止執行,而無需預測可能發生此情況的特定位置。(這有時稱為資料斷點。)

觀察點可以被視為特殊型別的斷點。當監視的記憶體位置的值發生變化時,它們將停止。當您想知道某個變數(或物件的欄位)何時被設定為某個值或其值被清除時,這尤其有用。例如,錯誤通常與節點某些欄位的某些空值有關。這些欄位可能在節點構造期間設定。但後來神秘地某個欄位變成了 NULL。如果不使用觀察點,要找到何時發生這種情況極其困難。

例如,我們希望監視與第二個迴圈的記憶體地址匹配的 SgForStatement 的父欄位的值變化。

  • 我們首先停在有一個斷點,在這裡我們可以訪問節點的內部欄位。這通常透過在 SgForStatement::post_construction_initialization() 處停止來完成。
  • 一旦內部變數在 gdb 的適當斷點處可見,我們就可以獲取內部變數的記憶體地址。這需要您瞭解內部變數的命名方式。您可以檢視物件的類宣告,或根據約定猜測。例如,大多數具有訪問函式(如 get_something())的東西在 ROSE AST 節點型別中都有一個名為 p_something 的相應內部變數。
  • 最後,我們必須監視記憶體地址的解引用值(監視 *address)。監視記憶體地址(監視 address)是為了監視一個常量值。它不會起作用。
(gdb) info breakpoints
Num     Type           Disp Enb Address            What
1       breakpoint     keep n   0x000000000040b0e2 in visitorTraversal::visit(SgNode*) at demo.C:22
2       breakpoint     keep y   0x00007ffff3d6495f in SgForStatement::post_construction_initialization() at Cxx_Grammar.C:139566
        stop only if (unsigned long)this==(unsigned long)0x7fffe87db138
        breakpoint already hit 1 time

(gdb) r
Starting program: /home/liao6/workspace/rose/2019-10-31_14-16-05_-0700/myTranslator/./demo -c inputCode_ExampleTraversals.C

[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 2, SgForStatement::post_construction_initialization (this=0x7fffe87db138) at Cxx_Grammar.C:139566
139566       if (p_for_init_stmt == NULL) {

// the data member storing parent pointer of an AST node is p_parent
// it is now have NULL value 
(gdb) p p_parent
$3 = (SgNode *) 0x0

// we obtain the memory address of p_parent
(gdb) p &p_parent
$4 = (SgNode **) 0x7fffe87db140

// watch value changes of this address
// Must deference the address with * , or it will won't work by saying "Cannot watch constant value"

(gdb) watch *0x7fffe87db140

// We can now watch the value changes to this memory address
// Let's restart the program from the beginning:

(gdb) r
Starting program: /home/liao6/workspace/rose/2019-10-31_14-16-05_-0700/myTranslator/./demo -c inputCode_ExampleTraversals.C
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Hardware watchpoint 2: *0x7fffe87db140

Old value = <unreadable>
New value = 0

SgNode::SgNode (this=0x7fffe87db138) at Cxx_Grammar.C:2128
2128         p_isModified = false;

// we check when the first time its value is changed: the constructor of ancestor node SgNode

(gdb) bt
#0  SgNode::SgNode (this=0x7fffe87db138) at Cxx_Grammar.C:2128
#1  0x00007ffff3d19f01 in SgLocatedNode::SgLocatedNode (this=0x7fffe87db138, startOfConstruct=0x0) at Cxx_Grammar.C:85278
#2  0x00007ffff3d59798 in SgStatement::SgStatement (this=0x7fffe87db138, startOfConstruct=0x0) at Cxx_Grammar.C:134029
#3  0x00007ffff3d59fcc in SgScopeStatement::SgScopeStatement (this=0x7fffe87db138, file_info=0x0) at Cxx_Grammar.C:134289
#4  0x00007ffff54e54e0 in SgForStatement::SgForStatement (this=0x7fffe87db138, test=0x0, increment=0x0, loop_body=0x0)
    at Cxx_GrammarNewConstructors.C:5230
#5  0x00007ffff5bb04ce in EDG_ROSE_Translation::parse_statement (sse=..., existingBasicBlock=0x0)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:49637
#6  0x00007ffff5bbb5ea in EDG_ROSE_Translation::parse_statement_list (sse=..., orig_kind=iek_statement, orig_ptr=0x1162200)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:53079
#7  0x00007ffff5bb0221 in EDG_ROSE_Translation::parse_statement (sse=..., existingBasicBlock=0x7fffe8934470)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:49492
#8  0x00007ffff5c09217 in EDG_ROSE_Translation::parse_function_body<SgFunctionDeclaration> (sse_base=..., p=0x1151fc0, decl=0x7fffe9e21e68)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:36262
#9  0x00007ffff5b844fa in EDG_ROSE_Translation::convert_routine (p=0x1151fc0, forceTemplateDeclaration=false, edg_template=0x0,
    optional_nondefiningTemplateDeclaration=0x0) at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:34343
#10 0x00007ffff5b703cf in EDG_ROSE_Translation::parse_routine (sse=..., forceTemplateDeclaration=false, edg_template=0x0, forceSecondaryDeclaration=false)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:29866
#11 0x00007ffff5be6f78 in EDG_ROSE_Translation::parse_global_or_namespace_scope_entity (sse=...)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:64638
#12 0x00007ffff5bea2df in EDG_ROSE_Translation::parse_global_scope (inputGlobalScope=0x7ffff7ec3120, sse=..., skip_ast_translation=false)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:65427
#13 0x00007ffff5bedbee in sage_back_end (sageFile=...) at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:66777
#14 0x00007ffff5beea8a in cfe_main (argc=44, argv=0x702f80, sageFile=...)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:66992
#15 0x00007ffff5beebe7 in edg_main (argc=44, argv=0x702f80, sageFile=...)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:67093
#16 0x00007ffff3c14629 in SgSourceFile::build_C_and_Cxx_AST (this=0x7fffeb45e010, argv=..., inputCommandLine=...)
    at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:5430
#17 0x00007ffff3c1587a in SgSourceFile::buildAST (this=0x7fffeb45e010, argv=..., inputCommandLine=...)
    at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:5983
#18 0x00007ffff3c0e5b7 in SgFile::callFrontEnd (this=0x7fffeb45e010) at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:3119
#19 0x00007ffff3c0b576 in SgSourceFile::callFrontEnd (this=0x7fffeb45e010)
    at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:2137
#20 0x00007ffff3c0a005 in SgFile::runFrontend (this=0x7fffeb45e010, nextErrorCode=@0x7fffffffaadc: 0)
    at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:1606
#21 0x00007ffff3c12924 in Rose::Frontend::RunSerial (project=0x7fffeb555010)
    at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:4613
#22 0x00007ffff3c12593 in Rose::Frontend::Run (project=0x7fffeb555010) at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:4506
#23 0x00007ffff3c0b84d in SgProject::RunFrontend (this=0x7fffeb555010) at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:2209
#24 0x00007ffff3c0bcb2 in SgProject::parse (this=0x7fffeb555010) at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:2334
#25 0x00007ffff3c0b0d4 in SgProject::parse (this=0x7fffeb555010, argv=...)
    at ../../../../sourcetree/src/frontend/SageIII/sage_support/sage_support.cpp:2028
#26 0x00007ffff3cbd2e9 in SgProject::SgProject (this=0x7fffeb555010, argv=..., frontendConstantFolding=false) at Cxx_Grammar.C:29114
#27 0x00007ffff645fd54 in frontend (argv=..., frontendConstantFolding=false) at ../../../sourcetree/src/roseSupport/utility_functions.C:628
#28 0x00007ffff645fc10 in frontend (argc=3, argv=0x7fffffffb578, frontendConstantFolding=false)
    at ../../../sourcetree/src/roseSupport/utility_functions.C:590
#29 0x000000000040b152 in main (argc=3, argv=0x7fffffffb578) at demo.C:40

// We continue the execution

(gdb) c
Continuing.
Hardware watchpoint 2: *0x7fffe87db140

Old value = 0
New value = -393001872
SgNode::set_parent (this=0x7fffe87db138, parent=0x7fffe8934470) at Cxx_Grammar.C:1684
1684         if ( ( variantT() == V_SgClassDeclaration ) && ( parent != NULL && parent->variantT() == V_SgFunctionParameterList ) )

//  Now we found that this p_parent field is set by calling set_parent(). We can inspect the call stack and other things of interests
(gdb) bt
#0  SgNode::set_parent (this=0x7fffe87db138, parent=0x7fffe8934470) at Cxx_Grammar.C:1684
#1  0x00007ffff5bb04ef in EDG_ROSE_Translation::parse_statement (sse=..., existingBasicBlock=0x0)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:49643
#2  0x00007ffff5bbb5ea in EDG_ROSE_Translation::parse_statement_list (sse=..., orig_kind=iek_statement, orig_ptr=0x1162200)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:53079
#3  0x00007ffff5bb0221 in EDG_ROSE_Translation::parse_statement (sse=..., existingBasicBlock=0x7fffe8934470)
    at ../../../../../../sourcetree/src/frontend/CxxFrontend/EDG/edgRose/edgRose.C:49492
.... // omitted

(gdb) c
Continuing.
Found a for loop ...
Found a for loop ...
Traversal ends here.
[Inferior 1 (process 54495) exited normally]

// No more value changes to the same memory address, as expected. 

與 ROSE 一起提供的翻譯器

[編輯 | 編輯原始碼]

這也被稱為樹內或源樹內構建。libtool 用於構建翻譯器。

ROSE 預設情況下開啟 -O2 和 -g,因此與 ROSE 一起提供的翻譯器應該已經提供了一些除錯資訊。但是某些變數可能會被最佳化掉。為了保留最大的除錯資訊,您可能需要重新配置/重新編譯 rose 以關閉最佳化。

../sourcetree/configure—with-CXX_DEBUG=-g --with-C_OPTIMIZE=-O0—with-CXX_OPTIMIZE=-O0  ...

ROSE 使用 libtool,因此構建樹中的可執行檔案不是真實的——它們只是圍繞實際可執行檔案的包裝器。您有兩個選擇

  • 在 .lib 目錄中找到真正的可執行檔案,然後除錯那裡的真正可執行檔案
  • 使用 libtool 命令列,如下所示
$ libtool --mode=execute gdb --args ./built_in_translator file1.c

如果可以在 .bashrc 中設定別名命令,請新增以下內容

alias debug='libtool --mode=execute gdb -args' 

然後所有除錯會話都可以像這樣簡單地進行

$ debug ./built_in_translator file1.c

其餘步驟與使用典型操作(如斷點、列印資料等)的常規 gdb 會話相同。

示例 2:修復 ROSE 中的真實錯誤

[編輯 | 編輯原始碼]

1. 複製報告的錯誤

$ make check
...
./testVirtualCFG \
    --edg:no_warnings -w -rose:verbose 0 --edg:restrict \
    -I$ROSE/tests/CompileTests/virtualCFG_tests/../Cxx_tests \
    -I$ROSE/sourcetree/tests/CompileTests/A++Code \
    -c $ROSE/sourcetree/tests/CompileTests/virtualCFG_tests/../Cxx_tests/test2001_01.C

...
lt-testVirtualCFG: $ROSE/src/frontend/SageIII/virtualCFG/virtualCFG.h:111:
    VirtualCFG::CFGEdge::CFGEdge(VirtualCFG::CFGNode, VirtualCFG::CFGNode):
    Assertion `src.getNode() != __null && tgt.getNode() != __null' failed.

啊,所以我們在virtualCFG.h標頭檔案中的第 111 行失敗了一個斷言

Assertion `src.getNode() != __null && tgt.getNode() != __null' failed

並且錯誤是由執行lt-testVirtualCFGlibtool 可執行翻譯器生成的,即實際的翻譯器名稱為testVirtualCFG(沒有lt-字首)。

2. 使用 Libtool 執行相同的翻譯器命令列以啟動 GDB 除錯會話

$ libtool --mode=execute gdb --args ./testVirtualCFG \
    --edg:no_warnings -w -rose:verbose 0 --edg:restrict \
    -I$ROSE/tests/CompileTests/virtualCFG_tests/../Cxx_tests \
    -I$ROSE/sourcetree/tests/CompileTests/A++Code \
    -c $ROSE/sourcetree/tests/CompileTests/virtualCFG_tests/../Cxx_tests/test2001_01.C

GNU gdb (GDB) Red Hat Enterprise Linux (7.0.1-42.el5_8.1)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from ${ROSE_BUILD_TREE}tests/CompileTests/virtualCFG_tests/.libs/lt-testVirtualCFG...done.
(gdb)

GDB 會話已啟動,我們提供了命令列提示以開始我們的除錯。

3. 讓我們執行程式,這將導致斷言失敗

(gdb) r
Starting program: \
    ${ROSE_BUILD_TREE}/tests/CompileTests/virtualCFG_tests/.libs/lt-testVirtualCFG \
    --edg:no_warnings -w -rose:verbose 0 --edg:restrict \
    -I${ROSE}/tests/CompileTests/virtualCFG_tests/../Cxx_tests \
    -I../../../../sourcetree/tests/CompileTests/A++Code
    -c   ${ROSE}/tests/CompileTests/virtualCFG_tests/../Cxx_tests/test2001_01.C
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x2aaaaaaab000
[Thread debugging using libthread_db enabled]
lt-testVirtualCFG: ${ROSE}/src/frontend/SageIII/virtualCFG/virtualCFG.h:111:

VirtualCFG::CFGEdge::CFGEdge(VirtualCFG::CFGNode, VirtualCFG::CFGNode): Assertion `src.getNode() != __null && tgt.getNode() != __null' failed.

Program received signal SIGABRT, Aborted.
0x0000003752230285 in raise () from /lib64/libc.so.6

好的,我們在 GDB 會話中複製了問題。

4. 讓我們檢查回溯以瞭解我們如何最終到達此失敗的斷言

(gdb) bt
#0  0x0000003752230285 in raise () from /lib64/libc.so.6
#1  0x0000003752231d30 in abort () from /lib64/libc.so.6
#2  0x0000003752229706 in __assert_fail () from /lib64/libc.so.6

#3  0x00002aaaad6437b2 in VirtualCFG::CFGEdge::CFGEdge (this=0x7fffffffb300, src=..., tgt=...)
     at ${ROSE}/../src/frontend/SageIII/virtualCFG/virtualCFG.h:111
#4  0x00002aaaad643b60 in makeEdge<VirtualCFG::CFGNode, VirtualCFG::CFGEdge> (from=..., to=..., result=...)
     at ${ROSE}/../src/frontend/SageIII/virtualCFG/memberFunctions.C:82
#5  0x00002aaaad62ef7d in SgReturnStmt::cfgOutEdges (this=0xbfaf10, idx=1)
     at ${ROSE}/../src/frontend/SageIII/virtualCFG/memberFunctions.C:1471
#6  0x00002aaaad647e69 in VirtualCFG::CFGNode::outEdges (this=0x7fffffffb530)
     at ${ROSE}/../src/frontend/SageIII/virtualCFG/virtualCFG.C:636
#7  0x000000000040bf7f in getReachableNodes (n=..., s=...) at ${ROSE}/tests/CompileTests/virtualCFG_tests/testVirtualCFG.C:13
...

5. 接下來,我們將向後(或向上)移動程式以到達斷言點

(gdb) up
#1  0x0000003752231d30 in abort () from /lib64/libc.so.6

(gdb) up
#2  0x0000003752229706 in __assert_fail () from /lib64/libc.so.6

(gdb) up
#3  0x00002aaaad6437b2 in VirtualCFG::CFGEdge::CFGEdge (this=0x7fffffffb300, src=..., tgt=...)
     at ${ROSE}/src/frontend/SageIII/virtualCFG/virtualCFG.h:111
111         CFGEdge(CFGNode src, CFGNode tgt): src(src), tgt(tgt) \
                   { assert(src.getNode() != NULL && tgt.getNode() != NULL); }

好的,所以斷言在CFGEdge:

CFGEdge(CFGNode src, CFGNode tgt): src(src), tgt(tgt) \
{
    assert(src.getNode() != NULL && tgt.getNode() != NULL);  # This is the failed assertion
}

的建構函式內部。不幸的是,我們無法一目瞭然地看出斷言中的兩個條件中的哪一個失敗了。

6. 找出斷言失敗的原因

讓我們檢查斷言中的兩個條件

(gdb) p src.getNode()
$1 = (SgNode *) 0xbfaf10

所以src.getNode()正在返回指向SgNode的非空指標。怎麼樣tgt.getNode()?

(gdb) p tgt.getNode()
$2 = (SgNode *) 0x0

啊,這就是罪魁禍首。所以出於某種原因,tgt.getNode()正在返回一個空SgNode指標(0x0).

從這裡,我們使用 GDBup命令在程式中回溯以找出tgt.getNode()返回的節點在哪裡被分配了 NULL 值。

我們最終找到了對SgReturnStmt::cfgOutEdges的呼叫,它返回一個名為enclosingFunc的變數。在原始碼中,當前沒有斷言來檢查enclosingFunc的值,這就是為什麼我們在程式的後面收到斷言的原因。作為旁註,最好儘快在原始碼中新增斷言,這樣在像這樣的時候,我們就不必花費不必要的時間進行回溯。

在為enclosingFunc新增斷言後,我們再次執行程式以到達這個新的斷言點

lt-testVirtualCFG: ${ROSE}sourcetree/src/frontend/SageIII/virtualCFG/memberFunctions.C:1473: \
    virtual std::vector<VirtualCFG::CFGEdge, std::allocator<VirtualCFG::CFGEdge> > \
    SgReturnStmt::cfgOutEdges(unsigned int): \

    Assertion `enclosingFunc != __null' failed.

好的,它失敗了,所以我們知道對enclosingFunc的賦值為 NULL。

# enclosingFunc is definitely NULL (0x0)
(gdb) p enclosingFunc
$1 = (SgFunctionDefinition *) 0x0

# What is the current context?
(gdb) p this
$2 = (SgReturnStmt * const) 0xbfaf10

好的,我們在一個SgReturnStmt物件內部。讓我們設定一個斷點,其中enclosingFunc正在被賦值給

Breakpoint 1, SgReturnStmt::cfgOutEdges (this=0xbfaf10, idx=1) at ${ROSE}/src/frontend/SageIII/virtualCFG/memberFunctions.C:1472
1472              SgFunctionDefinition* enclosingFunc = SageInterface::getEnclosingProcedure(this);

所以這就是我們正在檢查的行

SgFunctionDefinition* enclosingFunc = SageInterface::getEnclosingProcedure(this);

所以 NULL 值必須來自SageInterface::getEnclosingProcedure(this);.

在程式碼審查函式getEnclosingProcedure後,我們發現了演算法中的一個缺陷。

該函式嘗試返回一個SgNode,它是指定型別SgFunctionDefinition的封閉過程。但是,在檢查函式在return點處的狀態時,我們發現它錯誤地檢測到一個SgBasicBlack作為SgReturnStmt.

(gdb) p parent->class_name()
$12 = {static npos = 18446744073709551615,
   _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x7cd0e8 "SgBasicBlock"}}

的封閉過程。具體來說,最後一段0x7cd0e8 "SgBasicBlock".

但這是錯誤的,因為我們正在尋找SgFunctionDefinition,而不是SgBasicBlock.

經過進一步檢查,我們發現該函式只是返回它找到的第一個封閉節點,而不是第一個匹配使用者條件的封閉節點。

我們添加了必要的邏輯以使函式完整,對其進行了測試以驗證其正確性,然後解決了錯誤。

華夏公益教科書