跳轉到內容

ROSE 編譯器框架/操作指南

來自華夏公益教科書

關於如何作為 ROSE 開發人員執行常見任務的快速、簡短和重點突出的教程。

請為每個操作指南主題建立一個新的華夏公益教科書頁面。每個操作指南維基頁面不應包含任何一級 (=) 或二級 (==) 標題,以便在本書的印刷版中以正確的級別進行包含。

關於如何作為 ROSE 開發人員執行常見任務的快速、簡短和重點突出的教程。請為每個操作指南主題建立一個新的華夏公益教科書頁面。每個操作指南維基頁面不應包含任何一級 (=) 或二級 (==) 標題,以便在本書的印刷版中以正確的級別進行包含。

建立新頁面

[編輯 | 編輯原始碼]
==[[ROSE Compiler Framework/How to write a How-to|How to write a How-to]]==
{{:ROSE Compiler Framework/How to write a How-to}}
  • 將貼上文字中的三個位置重新命名為所需的頁面名稱,例如
==[[ROSE Compiler Framework/How to do XYZ|How to do XYZ]]==
{{:ROSE Compiler Framework/How to do XYZ}}
  • 單擊儲存頁面
  • 您將看到嘗試連結到尚未存在的如何執行 XYZ頁面的紅色文字
  • 單擊任何紅色文字,它將帶您到一個編輯視窗以新增新操作指南頁面的內容
  • 您現在可以新增新內容並儲存。
    • 同樣,每個操作指南維基頁面不應包含任何一級 (=) 或二級 (==) 標題,以便在本書的印刷版中以正確的級別進行包含。

插入圖片到維基頁面

[編輯 | 編輯原始碼]

內容規則

[編輯 | 編輯原始碼]
  • 操作指南頁面中只允許三級標題 (===) 和更高級別的標題。這是為了確保操作指南頁面能夠正確地包含在最終的單頁印刷版中。對此限制我們深感抱歉。
    • 同樣,請勿在操作指南頁面中使用一級 (=) 或二級 (==) 標題!
  • 使每個操作指南簡短且重點突出。預計讀者只需花費 30 分鐘或更短的時間來快速學習如何使用 ROSE 執行某項操作。
  • 建立新的操作指南頁面並儲存您的貢獻後。請轉到印刷版以確保其顯示正確。
  • 請說明操作指南主題是當前實踐還是建議的全新做法。這樣,我們就可以為程式碼審查提供明確的指南,說明哪些是強制性的,哪些是可選的。

開發大型、複雜的專案會帶來許多挑戰。為了緩解其中一些挑戰,我們採用了一些最佳實踐:增量式開發、程式碼審查和持續整合。

以下是一些關於如何將大型專案分解成更小的、易於管理的部分,以便逐步開發、程式碼審查和整合的提示。

  • 輸入:根據複雜性和難度定義不同的測試輸入集。首先處理更簡單的集合。
  • 輸出:定義通向最終輸出的中間結果。通常,結果AB需要生成C。因此,該專案可以根據中間結果具有多個階段。
  • 演算法:複雜的編譯器演算法通常只是更基本演算法的增強版本。首先實現基本演算法以獲得洞察力和經驗。然後,之後,您可以實現完整的版本。
  • 語言:對於處理多種語言的專案,一次專注於一種語言。
  • 平臺:限制支援的平臺範圍:Linux、Ubuntu、OS X(待辦事項:新增對 ROSE 支援平臺的引用)
  • 效能:首先從一個基本的工作實現開始。然後嘗試最佳化其效能和效率。
  • 範圍:您的翻譯器可以首先專注於函式範圍內的工作,然後擴充套件到同時處理整個原始檔,甚至多個檔案。
  • 骨架然後是肉:應該首先定義主要元件來建立專案。每個元件之後可以單獨豐富。
  • 註釋(手動與自動):執行一個編譯器任務通常需要來自許多其他正在開發的任務的結果。將原始碼註釋定義為兩個任務之間的介面可以以乾淨的方式解耦這些依賴關係。註釋可以首先手動插入。之後,註釋可以由完成的分析自動生成。
  • 可選與預設:引入一個標誌來開啟/關閉您的功能。在功能成熟時將其作為預設選項。

視覺化 ROSE AST 需要三件事

  • 示例輸入程式碼:您提供它
  • 從 AST 生成點檔案的點圖生成器:ROSE 提供點圖生成器
  • 用於開啟點圖的視覺化工具:ZGRViewer 和 Graphviz 被 ROSE 開發人員使用

如果您不想從頭開始安裝 ROSE+ZGRview + Graphvis,您可以直接使用ROSE 虛擬機器映像,其中包含您需要安裝和配置的所有內容,因此您只需視覺化示例程式碼即可。

示例輸入程式碼

[編輯 | 編輯原始碼]

請準備最簡單的輸入程式碼不要包含任何標頭檔案,這樣您就可以獲得足夠小的 AST 來消化。

點圖生成器

[編輯 | 編輯原始碼]

我們提供 ROSE_INSTALLATION_TREE/bin/dotGeneratorWholeASTGraph(複雜圖)和 dotGenerator(更簡單的版本)來生成輸入程式碼的詳細 AST 的點圖。

用於以點格式生成 AST 圖的工具。有兩種版本

  • dotGenerator:簡單的 AST 圖生成器,顯示基本節點和邊
  • dotGeneratorWholeASTGraph:整個 AST 圖,顯示更多詳細資訊。它提供過濾器選項來顯示/隱藏某些 AST 資訊。

命令列

 dotGeneratorWholeASTGraph  yourcode.c  // it is best to avoid including any header into your input code to have a small enough tree to visualize.

跳過內建函式

  • dotGeneratorWholeASTGraph -DSKIP_ROSE_BUILTIN_DECLARATIONS yourcode.c
dotGeneratorWholeASTGraph -rose:help
   -rose:help                     show this help message
   -rose:dotgraph:asmFileFormatFilter           [0|1]  Disable or enable asmFileFormat filter
   -rose:dotgraph:asmTypeFilter                 [0|1]  Disable or enable asmType filter
   -rose:dotgraph:binaryExecutableFormatFilter  [0|1]  Disable or enable binaryExecutableFormat filter
   -rose:dotgraph:commentAndDirectiveFilter     [0|1]  Disable or enable commentAndDirective filter
   -rose:dotgraph:ctorInitializerListFilter     [0|1]  Disable or enable ctorInitializerList filter
   -rose:dotgraph:defaultFilter                 [0|1]  Disable or enable default filter
   -rose:dotgraph:defaultColorFilter            [0|1]  Disable or enable defaultColor filter
   -rose:dotgraph:edgeFilter                    [0|1]  Disable or enable edge filter
   -rose:dotgraph:expressionFilter              [0|1]  Disable or enable expression filter
   -rose:dotgraph:fileInfoFilter                [0|1]  Disable or enable fileInfo filter
   -rose:dotgraph:frontendCompatibilityFilter   [0|1]  Disable or enable frontendCompatibility filter
   -rose:dotgraph:symbolFilter                  [0|1]  Disable or enable symbol filter
   -rose:dotgraph:emptySymbolTableFilter        [0|1]  Disable or enable emptySymbolTable filter
   -rose:dotgraph:typeFilter                    [0|1]  Disable or enable type filter
   -rose:dotgraph:variableDeclarationFilter     [0|1]  Disable or enable variableDeclaration filter
   -rose:dotgraph:variableDefinitionFilter      [0|1]  Disable or enable variableDefinitionFilter filter
   -rose:dotgraph:noFilter                      [0|1]  Disable or enable no filtering
Current filter flags' values are: 
         m_asmFileFormat = 0 
         m_asmType = 0 
         m_binaryExecutableFormat = 0 
         m_commentAndDirective = 1 
         m_ctorInitializer = 0 
         m_default = 1 
         m_defaultColor = 1 
         m_edge = 1 
         m_emptySymbolTable = 0 
         m_expression = 0 
         m_fileInfo = 1 
         m_frontendCompatibility = 0 
         m_symbol = 0 
         m_type = 0 
         m_variableDeclaration = 0 
         m_variableDefinition = 0 
         m_noFilter = 0 

點圖視覺化

[編輯 | 編輯原始碼]

要視覺化生成的點圖,您必須安裝

請注意,您必須配置 ZGRViewer 以使其具有其使用的某些命令的正確路徑。您可以從其配置/設定選單項中進行操作。或者直接修改文字配置檔案(.zgrviewer)。

下面顯示了一個示例配置(cat .zgrviewer)

<?xml version="1.0" encoding="UTF-8"?>
<zgrv:config xmlns:zgrv="http://zvtm.sourceforge.net/zgrviewer">
    <zgrv:directories>
        <zgrv:tmpDir value="true">/tmp</zgrv:tmpDir>
        <zgrv:graphDir>/home/liao6/svnrepos</zgrv:graphDir>
        <zgrv:dot>/home/liao6/opt/graphviz-2.18/bin/dot</zgrv:dot>
        <zgrv:neato>/home/liao6/opt/graphviz-2.18/bin/neato</zgrv:neato>
        <zgrv:circo>/home/liao6/opt/graphviz-2.18/bin/circo</zgrv:circo>
        <zgrv:twopi>/home/liao6/opt/graphviz-2.18/bin/twopi</zgrv:twopi>
        <zgrv:graphvizFontDir>/home/liao6/opt/graphviz-2.18/bin</zgrv:graphvizFontDir>
    </zgrv:directories>
    <zgrv:webBrowser autoDetect="true" options="" path=""/>
    <zgrv:proxy enable="false" host="" port="80"/>
    <zgrv:preferences antialiasing="false" cmdL_options=""
        highlightColor="-65536" magFactor="2.0" saveWindowLayout="false"
        sdZoom="false" sdZoomFactor="2" silent="true"/>
    <zgrv:plugins/>
    <zgrv:commandLines/>
</zgrv:config>

您還必須配置 run.sh 指令碼以使其具有正確的路徑

cat run.sh

#!/bin/sh

# If you want to be able to run ZGRViewer from any directory,
# set ZGRV_HOME to the absolute path of ZGRViewer's main directory
# e.g. ZGRV_HOME=/usr/local/zgrviewer

ZGRV_HOME=/home/liao6/opt/zgrviewer-0.8.1

java -jar $ZGRV_HOME/target/zgrviewer-0.8.1.jar "$@"

示例會話

[編輯 | 編輯原始碼]

一個完整的示例

# make sure the environment variables(PATH, LD_LIBRARY_PATH) for the installed rose are correctly set
which dotGeneratorWholeASTGraph
~/workspace/masterClean/build64/install/bin/dotGeneratorWholeASTGraph

# run the dot graph generator
dotGeneratorWholeASTGraph -c ttt.c

#see it
which run.sh
~/64home/opt/zgrviewer-0.8.2/run.sh

run.sh ttt.c_WholeAST.dot

示例輸出

[編輯 | 編輯原始碼]

我們將一些示例原始檔及其 AST 轉儲檔案放入:https://github.com/chunhualiao/rose-ast

[編輯 | 編輯原始碼]

SageInterface 函式

// You can call the following functions with gdb

   //! Pretty print AST horizontally, output to std output
   void SageInterface::printAST (SgNode* node); 


   //! Pretty print AST horizontally, output to a specified text file
   void SageInterface::printAST (SgNode* node, const char* filename); 

   //! Pretty print AST horizontally, output to a specified text file.
   void SageInterface::printAST2TextFile (SgNode* node, const char* filename, bool printTypes=true);

還提供了一個翻譯器(textASTGenerator),其原始碼位於 exampleTranslators/defaultTranslator 下。

  • make install-tools 將安裝此工具
  • textASTGenerator input.c 將生成整個 AST 的文字輸出

在 gdb 中的示例使用

[編輯 | 編輯原始碼]
  • 將 AST 的一部分列印到螢幕
  • 將 AST 的一部分列印到文字檔案
(gdb) up
#7  0x00007ffff418ab5d in Unparse_ExprStmt::unparseExprStmt (this=0x1a1bf950, stmt=0x7fffda63ce30, info=...) at ../../../sourcetree/src/backend/unparser/CxxCodeGeneration/unparseCxx_statements.C:9889

(gdb) p SageInterface::printAST(stmt)
└──@0x7fffda63ce30 SgExprStatement transformation 0:0
    └──@0x7fffd8488790 SgFunctionCallExp transformation 0:0
        ├──@0x7fffe6211910 SgMemberFunctionRefExp transformation 0:0
        └──@0x7fffd7f2c370 SgExprListExp transformation 0:0
            └──@0x7fffd8488720 SgFunctionCallExp transformation 0:0
                ├──@0x7fffe6211988 SgMemberFunctionRefExp transformation 0:0
                └──@0x7fffd7f2c3d8 SgExprListExp transformation 0:0
$2 = void


(gdb) up 10
#48 0x00007ffff40dce69 in Unparser::unparseFile (this=0x7fffffff8c60, file=0x7fffeb786010, info=..., unparseScope=0x0) at ../../../sourcetree/src/backend/unparser/unparser.C:945
(gdb) p SageInterface::printAST2TextFile(file,"test.txt")

textASTGenerator

[編輯 | 編輯原始碼]

示例命令列使用

textASTGenerator -c test_qualifiedName.cpp

cat test_qualifiedName.cpp.AST.txt

└──@0x7fe9f1916010 SgProject
    └──@0xb45730 SgFileList
        └──@0x7fe9f17be010 SgSourceFile
            ├──@0x7fe9fdf19120 SgGlobal test_qualifiedName.cpp 0:0
            │   ├──@0x7fe9f159a010 SgTypedefDeclaration rose_edg_required_macros_and_functions.h 0:0
            │   │   └── NULL
            │   ├──@0x7fe9f159a390 SgTypedefDeclaration rose_edg_required_macros_and_functions.h 0:0
            │   │   └── NULL
            │   ├──@0x7fe9f0f59010 SgFunctionDeclaration rose_edg_required_macros_and_functions.h 0:0 "::feclearexcept"
            │   │   ├──@0x7fe9f1391010 SgFunctionParameterList rose_edg_required_macros_and_functions.h 0:0
            │   │   │   └──@0x7fe9f1258010 SgInitializedName rose_edg_required_macros_and_functions.h 0:0 "::__excepts"
            │   │   │       └── NULL
            │   │   ├── NULL
            │   │   └── NULL
            │   ├──@0x7fe9f0f59540 SgFunctionDeclaration rose_edg_required_macros_and_functions.h 0:0 "::fegetexceptflag"
            │   │   ├──@0x7fe9f1391630 SgFunctionParameterList rose_edg_required_macros_and_functions.h 0:0
            │   │   │   ├──@0x7fe9f1258420 SgInitializedName rose_edg_required_macros_and_functions.h 0:0 "::__flagp"
            │   │   │   │   └── NULL
            │   │   │   └──@0x7fe9f1258628 SgInitializedName rose_edg_required_macros_and_functions.h 0:0 "::__excepts"
            │   │   │       └── NULL
            │   │   ├── NULL
            │   │   └── NULL

              ...

            │   └──@0x7fe9eff218c0 SgFunctionDeclaration test_qualifiedName.cpp 14:1 "::foo"
            │       ├──@0x7fe9ef5e0320 SgFunctionParameterList test_qualifiedName.cpp 14:1
            │       │   ├──@0x7fe9ef495278 SgInitializedName test_qualifiedName.cpp 14:13 "x"
            │       │   │   └── NULL
            │       │   └──@0x7fe9ef495480 SgInitializedName test_qualifiedName.cpp 14:20 "y"
            │       │       └── NULL
            │       ├── NULL
            │       └──@0x7fe9ee8f3010 SgFunctionDefinition test_qualifiedName.cpp 15:1
            │           └──@0x7fe9ee988010 SgBasicBlock test_qualifiedName.cpp 15:1
            │               ├──@0x7fe9eee1ba90 SgVariableDeclaration test_qualifiedName.cpp 16:3
            │               │   ├── NULL
            │               │   └──@0x7fe9ef495688 SgInitializedName test_qualifiedName.cpp 16:3 "z"
            │               │       └── NULL
            │               ├──@0x7fe9ee7ad010 SgExprStatement test_qualifiedName.cpp 17:3
            │               │   └──@0x7fe9ee7dc010 SgAssignOp test_qualifiedName.cpp 17:5
            │               │       ├──@0x7fe9ee8c0010 SgVarRefExp test_qualifiedName.cpp 17:3
            │               │       └──@0x7fe9ee813010 SgAddOp test_qualifiedName.cpp 17:9
            │               │           ├──@0x7fe9ee8c0078 SgVarRefExp test_qualifiedName.cpp 17:7
            │               │           └──@0x7fe9ee84a010 SgMultiplyOp test_qualifiedName.cpp 17:12
            │               │               ├──@0x7fe9ee8c00e0 SgVarRefExp test_qualifiedName.cpp 17:11
            │               │               └──@0x7fe9ee881010 SgIntVal test_qualifiedName.cpp 17:13
            │               └──@0x7fe9ee77e010 SgReturnStmt test_qualifiedName.cpp 18:3
            │                   └──@0x7fe9ee8c0148 SgVarRefExp test_qualifiedName.cpp 18:10
            ├── NULL
            ├── NULL
            └── NULL

在 HTML 中渲染 AST

[編輯 | 編輯原始碼]

倉庫errington1/ast-to-html包含一個工具,用於將 Rose 抽象語法“圖”渲染為可摺疊的 HTML,其中共享節點和迴圈由 HTML 連結表示。目前,它只能從命令列訪問。計劃是新增命令列選項來省略樹的一部分,並將該工具作為庫提供。目前,它在一定程度上任意省略了源自檔案rose_edg_required_macros_and_functions.h的樹的一部分。

命令

astToHTML file.C

將生成file.C.html,可以透過瀏覽器檢視

firefox file.C.html

翻譯器基本上將一個 AST 轉換為另一個版本的 AST。翻譯過程可能會新增、刪除或修改儲存在 AST 中的資訊。

基於 ROSE 的翻譯器通常包含以下步驟

  1. 搜尋要翻譯的 AST 節點。
  2. 對找到的 AST 節點執行翻譯操作。此操作可以是兩種主要變體之一
  • 更新現有的 AST 節點
  • 建立新的 AST 節點來替換原始節點。這通常比修補現有 AST 更乾淨的方法,並且受到 SageBuilder 和 SageInterface 函式的更好支援。
  • 深度複製現有的 AST 子樹以複製程式碼。表示式子樹不應共享。因此,需要深度複製它們才能獲得正確的 AST。
  • 可以選擇更新翻譯的其他相關資訊。

第一步

[編輯 | 編輯原始碼]

熟悉翻譯前後使用的抽象語法樹(AST)。這樣您就可以確定程式碼將處理哪些內容以及程式碼將生成哪些 AST。

最好的方法是準備最簡單的示例程式碼,並仔細檢查它們的整個點圖。

有關視覺化 AST 的更多詳細資訊,請訪問 如何視覺化 AST

設計注意事項

[編輯 | 編輯原始碼]

通常,最好

  • 將搜尋步驟與翻譯步驟分開,以便所有型別的翻譯都可以重用一次搜尋(遍歷)。
  • 在設計搜尋和翻譯的順序時,請注意翻譯是否會對搜尋產生負面影響。
    • 請避免使用先序遍歷,因為您最終可能會修改 AST 節點以供以後訪問,這類似於迭代器失效的效果。
    • 請使用後序遍歷,或使用與翻譯掛鉤的先序遍歷的逆序。

搜尋 AST 節點

[編輯 | 編輯原始碼]

有多種方法可以在 AST 中查詢要翻譯的內容。

AST 查詢

[編輯 | 編輯原始碼]
  • 透過 AST 查詢:節點查詢返回相同型別的一組 AST 節點。這通常足以進行簡單的翻譯。
Rose_STL_Container<SgNode*> ProgramHeaderStatementList = NodeQuery::querySubTree (project,V_SgProgramHeaderStatement);
for (Rose_STL_Container<SgNode*>::iterator i = ProgramHeaderStatementList.begin(); i != ProgramHeaderStatementList.end(); i++)
{
    SgProgramHeaderStatement* ProgramHeaderStatement = isSgProgramHeaderStatement(*i);
    ...
}


有關 AST 查詢的更多資訊,請參閱 ROSE 使用者手冊 pdf 中的“6 查詢庫”。

AST 遍歷

[編輯 | 編輯原始碼]
  • 透過 AST 遍歷:使用不同的順序(先序或後序)遍歷整個 AST。建議使用後序遍歷,以避免修改遍歷稍後將訪問的內容(類似於 C++ 中的迭代器失效問題)。
    • AST 遍歷提供 visit() 函式,用於掛鉤翻譯函式。可以使用 switch 語句來處理不同型別的 AST 節點。
class f2cTraversal : public AstSimpleProcessing
{
  public:
    virtual void visit(SgNode* n);
};

void f2cTraversal::visit(SgNode* n)
{
  switch(n->variantT())
  {
    case V_SgSourceFile:
      {
        SgFile* fileNode = isSgFile(n);
        translateFileName(fileNode);
      }
      break;
    case V_SgProgramHeaderStatement:
      {
        ...
      }
      break;
    default:
      break;
  }
}

有關 AST 遍歷的更多資訊,請參閱線上 ROSE 使用者手冊 pdf 中的“7 AST 遍歷”。

執行翻譯

[編輯 | 編輯原始碼]

在編寫翻譯器之前,請閱讀 ROSE 教程 pdf 文件中的第 32 章 AST 構造(http://rosecompiler.org/ROSE_Tutorial/ROSE-Tutorial.pdf)。它包含任何翻譯編寫者必不可少的資訊。

您想要執行的翻譯通常取決於您訪問的 AST 節點的型別。例如,您可以在名稱空間中定義一組翻譯函式

  • void translateForLoop(SgForLoop* n)
  • void translateFileName(SgFile* n)
  • void translateReturnStatement(SgReturnStmt* n),等等。

其他提示

更新樹
[編輯 | 編輯原始碼]
  • 您可能需要處理一些細節,例如刪除符號、更新父節點和符號表。
  • 注意使用 deepDelete() 和 deepCopy()。某些資訊可能無法正確更新。例如,deepDelete 可能不會更新您的符號表。

驗證正確性

[編輯 | 編輯原始碼]

您可以使用整個 AST 圖來驗證您的翻譯。

所有基於 ROSE 的翻譯器都應該在完成所有轉換後呼叫AstTests::runAllTests(project),以確保翻譯後的 AST 正確。

這比僅正確地反解析為可編譯程式碼有更高的標準。AST 通常會正確地經過反解析,但在健全性檢查中失敗。

更多資訊請訪問 健全性檢查

示例翻譯器

[編輯 | 編輯原始碼]

這裡列出了幾個示例翻譯器,它們可以發展成您想要的更復雜的翻譯器。

查詢編譯指示

[編輯 | 編輯原始碼]
/*
toy code
by Liao, 12/14/2007
*/
#include "rose.h"
#include <iostream>
using namespace std;

class visitorTraversal : public AstSimpleProcessing
{
  protected:
    virtual void visit(SgNode* n);
};

void visitorTraversal::visit(SgNode* node)
{
  if (node->variantT() == V_SgPragmaDeclaration) {
      cout << "pragma!" << endl;
  }
}

int main(int argc, char * argv[])
{
  SgProject *project = frontend (argc, argv);
  visitorTraversal myvisitor;
  myvisitor.traverseInputFiles(project,preorder);

  return backend(project);
}


這是一個執行編譯指示解析並將結果儲存到 AST 屬性中的示例專案。

https://github.com/rose-compiler/rose-develop/tree/master/projects/pragmaParsing

迴圈變換

[編輯 | 編輯原始碼]

SageInterface 名稱空間(http://rosecompiler.org/ROSE_HTML_Reference/namespaceSageInterface.html)包含許多翻譯函式,例如用於迴圈的函式。

例如,https://github.com/rose-compiler/rose/blob/master/src/frontend/SageIII/sageInterface/sageInterface.C 中定義了迴圈平鋪函式。

//     Tile the n-level (starting from 1) loop of a perfectly nested loop nest using tiling size s.
bool     loopTiling (SgForStatement *loopNest, size_t targetLevel, size_t tileSize)


提供了一個示例測試翻譯器來測試此函式

它有一個測試輸入檔案

如何構建翻譯器

[編輯 | 編輯原始碼]

請參閱 如何為翻譯器設定 makefile

在本 HOW-TO 中,介紹了生成跨語言翻譯器的步驟。這裡將以 Fortran 到 C 翻譯器為例。

更改原始檔資訊

[編輯 | 編輯原始碼]
  • 更改輸出檔名。字尾名需要使用以下函式更改。
void SgFile::set_unparse_output_filename (std::string unparse_output_filename ) 
  • 更改輸出語言型別。
void SgFile::set_outputLanguage(SgFile::outputLanguageOption_enum outputLanguage) 	
  • 將輸出設定為僅目標語言。
 We use set_C_only for the Fortran to C translation.  This process might be optional.
void SgFile::set_C_only(bool C_only)

識別與語言相關的 AST 節點

[編輯 | 編輯原始碼]
  • 例如,ROSE AST 使用不同的 AST 節點來表示 C 和 Fortran 中的迴圈。以下兩幅圖表示不同語言的相同迴圈。
C 使用 SgForStatement 表示 for 迴圈。
C SgForStatement
Fortran 使用 SgFortranDo 來實現 do 迴圈。
Fortran SgFortranDo

實現翻譯函式

[edit | edit source]
  • 使用 wholeAST 作為參考來實現翻譯函式。
  • 從原始 AST 節點複製所需資訊,生成新的 AST 節點。
  • 移除原始節點,並確保 AST 中的父子關係設定正確。

測試輸出程式碼

[edit | edit source]
  • 如果編譯器可用,則執行後端使用後端編譯器生成目的碼。
  • 如果目標語言沒有編譯器,請確保可以從測試用例生成輸出程式碼。建議對所有測試輸出執行編譯測試。

在本指南中,您將建立一個 makefile 來編譯和測試您自己的自定義 ROSE 翻譯器。

您可能需要先檢視“如何安裝 ROSE”:ROSE 編譯器框架/安裝

環境變數

[edit | edit source]

您必須設定好環境變數,以便您的翻譯器在執行期間找到 librose.so。

export LD_LIBRARY_PATH=${ROSE_INSTALL}/lib:${BOOST_INSTALL}/lib:$LD_LIBRARY_PATH

翻譯器程式碼

[edit | edit source]

以下是最簡單的 ROSE 翻譯器。

// ROSE translator example: identity translator.
//
// No AST manipulations, just a simple translation:
//
//    input_code > ROSE AST > output_code

#include <rose.h>

int main (int argc, char** argv)
{
    // Build the AST used by ROSE
    SgProject* project = frontend(argc, argv);

    // Run internal consistency tests on AST
    AstTests::runAllTests(project);

    // Insert your own manipulations of the AST here...

    // Generate source code from AST and invoke your
    // desired backend compiler
    return backend(project);
}

示例 1

[edit | edit source]

如果您有一個獨立於 ROSE 的專案(例如,您使用*已安裝*版本的 ROSE 編譯它),您可以選擇如何操作。

如果專案僅依賴於 ROSE 和 ROSE 的依賴項,則可以使用 ROSE 安裝說明結尾處描述的 Makefile http://rosecompiler.org/ROSE_HTML_Reference/installation.html

# Sample makefile for programs that use the ROSE library.
#
# ROSE has a number of configuration details that must be present when
# compiling and linking a user program with ROSE, and some of these 
# details are difficult to get right.  The most foolproof way to get
# these details into your own makefile is to use the "rose-config"
# tool. 
#
#
# This makefile assumes:
#   1. The ROSE library has been properly installed (refer to the
#      documentation for configuring, building, and installing ROSE).
#
#   2. The top of the installation directory is $(ROSE_INSTALLED). This
#      is the same directory you specified for the "--prefix" argument
#      of the "configure" script, or the "CMAKE_INSTALL_PREFIX" if using 
#      cmake. E.g., "/usr/local".
#
# The "rose-config" tool currently only works for ROSE configured with
# GNU auto tools (e.g., you ran "configure" when you built and
# installed ROSE). The "cmake" configuration is not currently
# supported by "rose-config" [September 2015].
##############################################################################

# Standard C++ compiler stuff (see rose-config --help)
CXX      = $(shell $(ROSE_INSTALLED)/bin/rose-config cxx)
CPPFLAGS = $(shell $(ROSE_INSTALLED)/bin/rose-config cppflags)
CXXFLAGS = $(shell $(ROSE_INSTALLED)/bin/rose-config cxxflags)
LDFLAGS  = $(shell $(ROSE_INSTALLED)/bin/rose-config ldflags)
LIBDIRS  = $(shell $(ROSE_INSTALLED)/bin/rose-config libdirs)

MOSTLYCLEANFILES =

##############################################################################
# Assuming your source code is "demo.C" to build an executable named "demo".

all: demo

demo.o: demo.C
   $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $^ 

demo: demo.o
   $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
   @echo "Remember to set:" 
   @echo "  LD_LIBRARY_PATH=$(LIBDIRS):$$LD_LIBRARY_PATH"

MOSTLYCLEANFILES += demo demo.o

##############################################################################
# Standard boilerplate

.PHONY: clean 
clean:
   rm -f $(MOSTLYCLEANFILES)
 

完整示例

[edit | edit source]

有一些專案示例演示了使用 ROSE 的標頭檔案/庫構建專案的不同方法。

它們位於:https://github.com/chunhualiao/rose-project-templates

一些使用 ROSE 的獨立專案的模板。獨立專案是指位於 ROSE 原始碼樹之外的專案。

  • template-project-v1 : 使用 Makefile 構建專案
  • template-project-v2 : 使用 Makefile 構建和執行 ROSE 外掛

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

準備工作

[edit | edit source]

首先,確保您的 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 基礎

[edit | edit source]

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 的構建系統構建的翻譯器

[edit | edit source]

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

如果翻譯器使用沒有使用 libtool 的 makefile 構建。您的翻譯器的除錯步驟只是使用 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 遍歷

[edit | edit source]

我們首先準備遍歷 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]

使用監視點

[編輯 | 編輯原始碼]

您可以使用監視點來停止執行,只要表示式的值發生變化,而不必預測可能發生這種情況的特定位置。(這有時被稱為資料斷點。)

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

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

  • 我們首先在我們可以訪問節點內部欄位的斷點處停止。這通常透過在 SgForStatement::post_construction_initialization() 處停止來完成。
  • 一旦在適當的斷點處在 gdb 中可以看到內部變數,我們就可以獲取內部變數的記憶體地址。這需要您瞭解內部變數的命名方式。您可以檢視物件的類宣告,或者根據慣例進行猜測。例如,大多數情況下,具有訪問函式(如 get_something())的函式在 ROSE AST 節點型別中都有一個名為 p_something 的對應內部變數。
  • 最後,我們必須監視記憶體地址的解引用值(watch *address)。監視記憶體地址(watch 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()返回的節點在哪裡被分配了一個空值。

我們最終找到了對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的賦值為空。

# 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);

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

在程式碼審查函式getEnclosingProcedure後,我們發現演算法存在缺陷。

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

(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.

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

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

大多數在 ROSE 庫之上分層的程式碼開發都是從 projects 目錄中的一個專案開始的。有些專案最終會在成熟後被重構到 ROSE 庫中。本章介紹如何將新專案新增到 ROSE。

方法 1:新增新專案的新方法

[編輯 | 編輯原始碼]

Robb Matzke 在 ROSE 中添加了一個新功能,這樣您就可以更輕鬆地將新專案新增到 ROSE/projects 中

  • 建立一個 $ROSE/projects/whatever 目錄。
  • 在該目錄中,建立一個 "rose.config" 檔案
  • 在該檔案中,新增一行 AC_CONFIG_FILES(projects/whatever/Makefile)

rose/config/support-projects.m4 將透過執行 ./build 更新。

您仍然需要有自己的 Makefile.am。一個最簡單的例子是

方法 2:必需檔案

[編輯 | 編輯原始碼]

ROSE 的專案封裝了完整的程式或使用 ROSE 庫的相關程式集。每個專案都作為 ROSE “專案”目錄的子目錄存在,並應包含檔案“README”、“config/support-rose.m4”、“Makefile.am”以及任何必要的原始檔、指令碼、測試等。

  • “README”應提供有關專案目的、演算法、設計、實現等的解釋。
  • “support-rose.m4”以允許專案成為可選元件的方式將專案整合到 ROSE 構建系統中(可以停用、重新命名、刪除或不包含在分發中,而無需更改任何 ROSE 配置檔案)。大多數舊專案缺少此檔案,因此與構建系統更緊密地耦合。
  • “Makefile.am”充當 ROSE 用來生成 Makefile 的 GNU automake 系統的輸入。
  • 每個專案還應包含所有必要的原始檔、文件和測試用例。

設定 support-rose.m4

[編輯 | 編輯原始碼]

“config/support-rose.m4”檔案將專案整合到 ROSE 配置和構建系統中。至少,它應包含對 autoconf AC_CONFIG_FILES 宏的呼叫,其中包含專案 Makefile(不含“.am”副檔名)和其 doxygen 配置檔案(不含“.in”副檔名)的列表。它還可能包含任何其他必要的 autoconf 檢查,這些檢查尚未由 ROSE 的主配置指令碼執行,包括根據專案先決條件的可用性啟用/停用專案的程式碼。

以下是一個示例

dnl List of all makefiles and autoconf-generated                          -*- autoconf -*-
dnl files for this project
AC_CONFIG_FILES([projects/DemoProject/Makefile
                 projects/DemoProject/gui/Makefile
                 projects/DemoProject/doxygen/doxygen.conf
                ])

dnl Even if this project is present in ROSE's "projects" directory, we might not have the
dnl prerequisites to compile this project.  Enable the project's makefiles by using the
dnl ROSE_ENABLE_projectname automake conditional.  Many prerequisites have probably already
dnl been tested by ROSE's main configure script, so we don't need to list them here again
dnl (although it usually doesn't hurt).
AC_MSG_CHECKING([whether DemoProject prerequisites are satisfied])
if test "$ac_cv_header_gcrypt_h" = "yes"; then
        AC_MSG_RESULT([yes])
        rose_enable_demo_project=yes
else
        AC_MSG_RESULT([no])
        rose_enable_demo_project=
fi
AM_CONDITIONAL([ROSE_ENABLE_DEMO_PROJECT], [test "$rose_enable_demo_project" = yes])

由於專案的所有配置都封裝在“support-rose.m4”檔案中,因此重新命名、停用或刪除專案非常簡單:可以透過重新命名其目錄來重新命名專案,可以透過重新命名/刪除“support-rose.m4”來停用專案,或者可以透過刪除其目錄來刪除專案。“build”和“configure”指令碼應在任何這些更改之後重新執行。

由於專案是 ROSE 的自封裝和可選部分,因此不需要將它們與 ROSE 一起分發。這使使用者能夠將他們自己的私有專案放到現有的 ROSE 原始碼樹中,而無需修改任何 ROSE 檔案,並且允許 ROSE 開發人員處理未公開分發的專案。任何不在 ROSE 主 Git 儲存庫中的專案目錄都不會分發(這包括不分發 Git 子模組,儘管子模組的佔位符空目錄將分發)。

設定 Makefile.am

[編輯 | 編輯原始碼]

每個專案至少應有一個 Makefile.am,每個 Makefile.am 都由 GNU automake 和 autoconf 處理以生成 Makefile。有關這些檔案應包含的內容的詳細資訊,請參閱 automake 文件。一些重要的變數和目標是

  • include $(top_srcdir)/config/Makefile.for.ROSE.includes.and.libs: 這將引入來自更高級別 Makefile 的定義,並且所有專案都需要它。它應位於 Makefile.am 的頂部附近。
  • SUBDIRS: 此變數應包含專案所有具有 Makefile 的子目錄的名稱。如果專案的唯一 Makefile 位於專案的頂層目錄中,則可以省略它。
  • INCLUDES: 這將包含編譯期間需要新增的標誌(例如-I$(top_srcdir)/projects/RTC/include)。您的標誌應放置在$(ROSE_INCLUDES)之前,以確保找到正確的檔案。這將引入 src 目錄中的所有必要標頭到您的專案。
  • lib_*: 這些變數/目標在您從專案建立庫時是必要的,該庫可以與其他專案或 src 目錄稍後連結。這是處理專案的推薦方式。
  • EXTRA_DIST: 這些不是列為構建最終物件(如原始檔和標標頭檔案)所需的,但仍必須位於 ROSE tarball 分發中的檔案。例如,這可能包括 README 或配置檔案。
  • check-local: 這是在呼叫make check 時將從更高級別 Makefile 呼叫的目標。
  • clean-local: 為您提供執行手動清理專案的步驟,例如,如果您手動建立了一些檔案(因此 Automake 不會自動清理它們)。

一個基本示例

[編輯 | 編輯原始碼]

許多專案從翻譯器、分析器或最佳化器開始,它們接收輸入程式碼並生成輸出。

將新專案目錄新增到 ROSE 的基本示例提交:https://github.com/rose-compiler/rose/commit/edf68927596960d96bb773efa25af5e090168f4a

請檢視差異,以便了解為新專案新增和更改哪些檔案。

本質上,基本專案應包含

  • 一個 README 檔案,解釋該專案是關於什麼、演算法、設計、實現等
  • 一個翻譯器充當專案的驅動程式
  • 根據需要新增的額外原始檔和標頭以包含專案的核心內容
  • 測試輸入檔案
  • Makefile.am 以
    • 編譯和構建您的翻譯器
    • 包含make check 規則,以便您的翻譯器將被呼叫以處理您的輸入檔案並生成預期結果

要將您的專案連線到 ROSE 的構建系統,您還需要

  • 在 projects/Makefile.am 中為您的專案目錄新增一個子目錄條目
  • 在 config/support-rose.m4 中為您的專案使用的每個新 Makefile(從每個 Makefile.am 生成)新增一行。

安裝專案目標

[編輯 | 編輯原始碼]

將專案的“內容”安裝到使用者指定目錄中的單獨目錄中--prefix位置。這樣做背後的原因是,我們不想汙染核心 ROSE 安裝空間。透過這樣做,我們可以降低 ROSE 安裝樹的複雜性和混亂程度,同時消除跨專案檔案衝突。它還使安裝樹保持模組化。

此示例使用字首進行安裝。它還維護語義版本控制

來自projects/RosePoly

  ## 1. Version your project properly (http://semver.org/)
  rosepoly_API_VERSION=0.1.0

  ## 2. Install to separate directory
  ##
  ##    Installation tree should resemble:
  ##
  ##    <--prefix>
  ##    |--bin      # ROSE/bin
  ##    |--include  # ROSE/include
  ##    |--lib      # ROSE/lib
  ##    |
  ##    |--<project>-<version>
  ##       |--bin      # <project>/bin
  ##       |--include  # <project>/include
  ##       |--lib      # <project>/lib
  ##
  exec_prefix=${prefix}/rosepoly-$(rosepoly_API_VERSION)

  ## Installation/include tree should resemble:
  ##   |--<project>-<version>
  ##      |--bin      # <project>/bin
  ##      |--include  # <project>/include
  ##         |--<project>
  ##      |--lib      # <project>/lib
  librosepoly_la_includedir = ${exec_prefix}/include/rosepoly

生成 Doxygen 文件

[編輯 | 編輯原始碼]

0. 安裝 Doxygen 工具

對於 Apple 的 Mac OS,使用 MacPorts

  $ port install doxygen

  # set path to MacPort's bin/
  # ...

使用 LLNL 機器之一

  $ export PATH=/nfs/apps/doxygen/latest/bin:$PATH


1. 建立 Doxygen 配置檔案

  $ doxygen -g

Configuration file `Doxyfile' created.

Now edit the configuration file and enter

  doxygen Doxyfile

to generate the documentation for your project


2. 自定義配置檔案 (Doxyfile)

...

# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
# documentation are documented, even if no documentation was available.
# Private class members and static file members will be hidden unless
# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES

EXTRACT_ALL            = YES

...

# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank the following patterns are tested:
# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
# *.f90 *.f *.for *.vhd *.vhdl

FILE_PATTERNS          = *.cpp *.hpp

# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
# If left blank NO is used.

RECURSIVE              = YES

...


3. 生成 Doxygen 文件

  # Invoke from your top-level directory
  $ doxygen Doxyfile


4. 檢視並驗證 HTML 文件

  $ firefox html/index.html &

5. 將目標新增到您的Makefile.am以生成文件

.PHONY: docs
docs:
    doxygen Doxyfile # TODO: should be $(DOXYGEN)

如果您試圖修復一個錯誤(您自己的錯誤或分配給您修復的錯誤)。以下是可以執行工作的步驟

重現錯誤

[編輯 | 編輯原始碼]

只有在您可以重現錯誤時才能修復它。這一步可能比聽起來更難。為了重現錯誤,您必須

  • 找到合適的輸入檔案
  • 找到合適的翻譯器:與 ROSE 一起提供的翻譯器很容易找到。但是,在要求使用者編寫的翻譯器時,請保持耐心和真誠。
  • 找到相似/相同的軟體和硬體環境:錯誤可能只在使用特定軟體配置的特定平臺上出現

此步驟的可能結果

  • 您可以可靠地重現錯誤。太棒了!進入下一步。
  • 您無法重現錯誤。錯誤報告無效,或者您必須繼續嘗試。
  • 您偶爾可以重現錯誤(隨機錯誤)。糟糕。這是一種比較困難的情況。

找出錯誤的原因

[編輯 | 編輯原始碼]

一旦你可以重現錯誤,你需要使用偵錯程式(如gdb)來確定錯誤的根本原因。

常見的步驟包括

  • 儘可能簡化輸入程式碼:除錯包含大量輸入的程式非常困難。始終嘗試準備最簡單的程式碼,只需觸發錯誤即可。
    • 通常,你需要使用二分搜尋方法來縮小輸入程式碼範圍:一次只使用一半的輸入進行嘗試。遞迴地將輸入檔案分成兩部分,直到無法再切割,同時仍然可以觸發錯誤。
  • 向前追蹤:對於翻譯器來說,它通常會接收輸入並在生成最終輸出之前生成中間結果。使用偵錯程式在程式碼的每個關鍵階段設定斷點,以檢查中間結果是否符合預期。
  • 向後追蹤:類似於前面的技術,只是你反向追蹤問題。

修復錯誤

[edit | edit source]

任何錯誤修復提交都應該包含

  • 迴歸測試:這樣檢查規則可以確保錯誤確實已修復,並且不會因為進一步的程式碼更改而導致錯誤再次出現。

通常,新增到 ROSE 的功能會附帶一組命令列選項。這些選項可以啟用和自定義功能。

例如,ROSE 中的 OpenMP 支援預設情況下是停用的。需要一個特殊的選項來啟用它。此外,支援可以簡單到解析 OpenMP 指令,也可以複雜到翻譯成多執行緒程式碼。

本 HOWTO 將快速介紹新增選項的關鍵步驟。

內部標誌

[edit | edit source]

選項需要儲存在某個地方。有幾種儲存選擇:

  • 作為 SgProject 的資料成員,如果選項適用於與 SgProject 關聯的所有檔案。
  • 作為 SgFile 的資料成員,如果選項適用於單個原始檔,或者
  • 你定義的名稱空間中的一個成員變數,如果選項用於某種轉換或分析。

如果選項可以像每個檔案一樣具體,建議新增一個新的資料成員到 SgFile 來儲存選項值。

例如,這裡有一個命令列選項用於啟用 UPC 語言支援

ROSE/src/ROSETTA/src/support.C // 為 SgFile 新增一個數據成員

    // Liao (6/6/2008): Support for UPC model of C , 6/19/2008: add support for static threads compilation
    File.setDataPrototype         ( "bool", "UPC_only", "= false",
                                    NO_CONSTRUCTOR_PARAMETER, BUILD_ACCESS_FUNCTIONS, NO_TRAVERSAL, NO_DELETE);

ROSETTA 處理此資訊以自動生成一個成員以及相應的成員訪問函式(set/get_member())。

處理選項

[edit | edit source]

命令列選項應在 src/frontend/SageIII/sage_support/cmdline.cpp 中處理。

檔案級選項由 void SgFile::processRoseCommandLineOptions ( vector<string> & argv ) 處理


處理 -rose:openmp 選項的示例程式碼

     set_openmp(false);
     ROSE_ASSERT (get_openmp() == false);
       ...
     if ( CommandlineProcessing::isOption(argv,"-rose:","(OpenMP|openmp)",true) == true )
        {
          if ( SgProject::get_verbose() >= 1 )
               printf ("OpenMP option specified \n");
          set_openmp(true);
         //side effect for enabling OpenMP, define the macro as required
           argv.push_back("-D_OPENMP");
        }


處理後,應刪除 ROSE 命令列選項,以避免混淆後端編譯器

SgFile::stripRoseCommandLineOptions ( vector<string>& argv ) 應包含用於剝離選項的程式碼。

使用選項

[edit | edit source]

在你的程式碼中,你可以使用自動生成的訪問函式來設定/檢索儲存的選項值。

例如

  if (sourceFile->get_openmp())
     //... do something here ....

記錄選項

[edit | edit source]

任何選項都應透過線上幫助輸出進行解釋。

請在 ./src/frontend/SageIII/sage_support/cmdline.cpp 的 void SgFile::usage ( int status ) 中為你的選項新增簡短的幫助文字。

華夏公益教科書