跳转到内容

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 ) 中为你的选项添加简短的帮助文本。

华夏公益教科书