前言 实验室的代码异化项目,我来接手,但是有一说一,这个项目很复杂
头文件部分 consumer.h 主要就是定义了六中错误类型
include 这里面include了几个AST相关的东西:
#include <clang/AST/ASTConsumer.h> #include <clang/AST/ASTContext.h> #include <clang/ASTMatchers/ASTMatchFinder.h> #include <clang/Rewrite/Core/Rewriter.h> #include <cstdint> #include <string> #include <utility>
<clang/AST/ASTConsumer.h>:该头文件提供了 ASTConsumer 类,它是一个抽象基类,定义了消耗 Clang 解析器生成的 AST(抽象语法树)的类的接口。
<clang/AST/ASTContext.h>:该头文件提供了 ASTContext 类,表示 Clang 解析器生成的 AST 的上下文。
<clang/ASTMatchers/ASTMatchFinder.h>:该头文件提供了 AST 匹配器和匹配器查找器,用于在 AST 中查找特定模式的节点。
<clang/Rewrite/Core/Rewriter.h>:该头文件提供了 Rewriter 类,用于重写源代码,通常用于生成修改后的代码。
enum枚举部分 enum { // 语义错误 OPERATOR_SEMANTIC = 0,//操作符错误 BOUND_SEMANTIC,//边界错误 VARUSE_SEMANTIC,//变量使用错误 // 语法错误 UNDEFVAR_SYNTAX,//未定义变量错误 FORSEMI_SYNTAX,//for循环逗号错误 PAREN_SYNTAX,//括号错误 ALL_CONSUMER };
父类:CustomASTConsumer 所有错误类型的父类,他本身也继承了clang::ASTConsumer
class CustomASTConsumer : public clang::ASTConsumer { protected: // 用于查找 AST 中特定模式的 MatchFinder clang::ast_matchers::MatchFinder finder; // 存储 MatchCallback 的唯一指针的容器 std::vector<std::unique_ptr<clang::ast_matchers::MatchFinder::MatchCallback>> callbacks; public: // 纯虚函数,用于获取自定义 AST 消费者的名称 virtual std::string name() const = 0; };
callbacks 是一个容器,其中的每个元素都是一个指向 clang::ast_matchers::MatchFinder::MatchCallback 类型对象的 std::unique_ptr 智能指针。
OperatorSemantic class OperatorSemantic : public CustomASTConsumer { public: explicit OperatorSemantic( std::vector<std::function<void(clang::Rewriter &)>> &all); void HandleTranslationUnit(clang::ASTContext &Context) override; std::string name() const override; };
explicit OperatorSemantic( std::vector<std::function<void(clang::Rewriter &)>> &all);
构造函数,接受一个vector,vector当中存储的是返回值为void,参数类型为Rewriter引用类型的函数
void HandleTranslationUnit(clang::ASTContext &Context) override;
处理翻译单元的AST
其余错误类型格式都是一样的 ,看一个就好了
ConsumerFactory class ASTConsumerFactory { public: static std::unique_ptr<clang::ASTConsumer> getASTConsumer(std::vector<std::function<void(clang::Rewriter &)>> &all, int type = ALL_CONSUMER); static bool isValidType(int type) { return type < ALL_CONSUMER && type >= OPERATOR_SEMANTIC; } };
用于获取 clang::ASTConsumer 的对象。它接受一个vector all 作为参数,该vector中的每个元素都是一个函数对象,这些函数对象接受一个 clang::Rewriter 的引用,并且返回 void。该函数还接受一个可选参数 type,默认值为 ALL_CONSUMER
util.h 就是用来生成随机数和打乱一个vector的
// 声明一个函数模板,用于生成指定长度的随机字符串 std::string randomStr(int n); // 声明一个函数模板,用于随机打乱 vector 元素的顺序 template<typename T> void shuffleVector(std::vector<T> &v) { // 初始化随机种子 std::random_device randDevice; std::mt19937 generate(randDevice()); // 随机排序 shuffle(v.begin(), v.end(), generate); }
在rand.cpp里会用到
exp.h 自定义了两种exp
#include <exception> // 自定义异常类 InvalidConsumerExp,继承自 std::exception class InvalidConsumerExp : public std::exception { public: // 重写基类中的虚函数 what(),返回异常信息的 C 风格字符串 const char *what() const noexcept override { return "Invalid Consumer Exception"; } }; // 自定义异常类 NoMoreConsumerExp,继承自 std::exception class NoMoreConsumerExp : public std::exception { public: // 重写基类中的虚函数 what(),返回异常信息的 C 风格字符串 const char *what() const noexcept override { return "No More Consumer Exception"; } };
syntax包 主要是三种语法错误
ForSemiSyntax 生成错误的for语句,将for语句本来的分号替换为逗号
匿名namespace namespace { const string desc = "This bug is a syntax error where a comma is mistakenly used instead " "of a semicolon within a for loop"; const string id = "for"; class ForSemiCallback : public MatchFinder::MatchCallback { public: vector<function<void(Rewriter &)>> &all; explicit ForSemiCallback(vector<function<void(Rewriter &)>> &all) : all(all) {} void run(const MatchFinder::MatchResult &result) override { const ForStmt *bo = result.Nodes.getNodeAs<ForStmt>(id);//获取循环语句的节点 SourceManager *sm = result.SourceManager; if (bo == nullptr) return; // 获取初始子句的结束位置,这是第一个分号的位置 const Stmt *init = bo->getInit(); // 获取条件语句的结束位置, 这是第二个分号的位置 const Stmt *cond = bo->getCond(); if ((init || cond)&&sm) { function<void(Rewriter &)> f = [init, cond, sm](Rewriter &r) { SourceLocation loc; int i = randomNumber(0, 2); if (init != nullptr) loc = init->getEndLoc(); if (i == 0 && cond != nullptr) loc = cond->getEndLoc(); // 使用Rewriter替换分号为逗号 r.ReplaceText(loc, 1, ","); outs() << sm->getPresumedLineNumber(loc) << " " << sm->getPresumedColumnNumber(loc) << "\n" << desc << "\n"; }; all.push_back(f); } } }; } // namespace
定义了描述、id还有一个类
来看一下这个ForSemiCallback
包含一个vector引用类型的成员变量,vector<function<void(Rewriter &)>> &all;:这是一个引用类型的成员变量,用于存储函数对象的向量。并且在构造函数中初始化:
explicit ForSemiCallback(vector<function<void(Rewriter &)>> &all) : all(all) {}
run方法
定位两个分号的位置,随机将一个分号改成逗号
构造函数 所有错误类型的构造函数都差不多,就分析这个了
ForSemiSyntax::ForSemiSyntax(vector<function<void(Rewriter &)>> &all) { this->callbacks.push_back( unique_ptr<MatchFinder::MatchCallback>(new ForSemiCallback(all)));//把自定义的callback函数放到容器当中 // 匹配所有for语句并且将他们的id设置为我们自定义的id,方便后续找到 StatementMatcher m = forStmt(isExpansionInMainFile()).bind(id); finder.addMatcher(m, this->callbacks[0].get());//对于所有for语句,都绑定ForSemiCallback,使得所有的for都能异化 }
还有两个大家都一样的方法:
void ParenSyntax::HandleTranslationUnit(clang::ASTContext &context) { finder.matchAST(context); } string ParenSyntax::name() const { return "ParenSyntax"; }
以后就不分析了
ParenSyntax 只看run方法了
void run(const MatchFinder::MatchResult &result) override { const Stmt *bo = result.Nodes.getNodeAs<Stmt>(id); const SourceManager *sm = result.SourceManager; if (bo && sm) {//有效才执行 function<void(Rewriter &)> f = [bo, sm](Rewriter &r) { // 左括号位置 右括号位置 SourceLocation lParenLoc, rParenLoc; if (const CallExpr *callExpr = dyn_cast<CallExpr>(bo)) {//如果bo是一个函数调用表达式的话 rParenLoc = callExpr->getRParenLoc();//那么就获取右括号的位置 // 获取左括号的位置 if (callExpr->getNumArgs() == 0) { // 如果没有参数 lParenLoc = rParenLoc.getLocWithOffset(-1);//左括号偏移量为-1 } else { // 有参数,使用第一个参数位置 lParenLoc = callExpr->getArg(0)->getBeginLoc().getLocWithOffset(-1); // 向左搜寻,以确保找到左括号的位置(考虑有空格和其他字符的情况) while (sm->getCharacterData(lParenLoc)[-1] != '(') { lParenLoc = lParenLoc.getLocWithOffset(-1); } } } else if (const ParenExpr *parenExpr = dyn_cast<ParenExpr>(bo)) {//如果bo是一个括号表达式的话 lParenLoc = parenExpr->getLParen();//直接获取 rParenLoc = parenExpr->getRParen(); } else { return; } char c = '('; SourceLocation loc = lParenLoc; //随机删除左右括号 int i = randomNumber(0, 2); if (i == 0) { c = ')'; loc = rParenLoc; } r.RemoveText(loc, 1); outs() << sm->getPresumedLineNumber(loc) << " " << sm->getPresumedColumnNumber(loc) << " " << c << "\n" << desc << "\n"; }; all.push_back(f);//被掉用户后就销毁,需要手动重新push } }
UndefVarSyntax 未定义变量错误,使用了未定义的变量, 现实情况可能是打错了变量名. 随机选择一个变量使用, 将它改成一个没有出现过的变量名.
void run(const MatchFinder::MatchResult &result) override { const DeclRefExpr *bo = result.Nodes.getNodeAs<DeclRefExpr>(id);//获得为id的变量的节点 const SourceManager *sm = result.SourceManager;//获得资源管理器 if (bo && sm) { function<void(Rewriter &)> f = [bo, sm](Rewriter &r) { const NamedDecl *nd = dyn_cast<NamedDecl>(bo->getDecl());//获得声明并转为名称的指针 const string oldn = nd->getName().str();//old名称 const string newn = oldn + randomStr(oldn.size());//新变量名 r.ReplaceText(bo->getSourceRange(), newn);//替换变量名 SourceLocation loc = bo->getBeginLoc(); outs() << sm->getPresumedLineNumber(loc) << " " << sm->getPresumedColumnNumber(loc) << " " << oldn << " " << newn << "\n" << desc << "\n"; }; all.push_back(f); } }
semantic包 语义错误
OperatorSemantic 操作符错误,随机把正确的操作符替换为错误的操作符
首先定义了所有的二院操作符,以二维数组的形式
vector<vector<string>> binaryOperators = { {"+", "-", "*", "/", "%"}, // Arithmetic {"<", "<=", ">", ">=", "==", "!="}, // Comparison {"&&", "||"}, // Logical {"&", "|", "^"}, // Bitwise {"=", "+=", "-=", "*=", "/=", "%="} // Assignment };
nextStr函数
从二维数组中选取一个不同于cur的二元操作符
string nxtStr(string &cur) { string nxt = cur; for (auto &line : binaryOperators) { if (find(line.begin(), line.end(), cur) == line.end()) continue; int s = line.size(); // if (s == 1) break; int rand = randomNumber(0, s); nxt = line[rand]; while (nxt == cur) { rand = randomNumber(0, s); nxt = line[rand]; } break; } return nxt; }
run函数
void run(const MatchFinder::MatchResult &result) override { const auto *bo = result.Nodes.getNodeAs<BinaryOperator>(id);//获得二元操作符的bo SourceManager *sm = result.SourceManager;//获得资源管理器 bool isInMain = sm->isInMainFile(bo->getOperatorLoc());//判断二院操作符的位置是不是在主文件当中 if (bo && isInMain) { string cur = bo->getOpcodeStr().str();//获得当前二元操作符 string nxt = nxtStr(cur);//新操作符 function<void(Rewriter &)> f = [bo, sm, cur, nxt](Rewriter &r) { const SourceLocation &loc = bo->getOperatorLoc(); // 设置 rewriter r.ReplaceText(loc, cur.size(), nxt);//替换 outs() << sm->getPresumedLineNumber(loc) << " " << sm->getPresumedColumnNumber(loc) << " " << cur << " " << nxt << "\n" << desc << "\n"; }; all.push_back(f); } }
BoundSemantic 边界错误: 对数值进行了不正确的增加或减少,从而导致变量值与预期不符. 挑选随机类型为数值类型的变量 a, 在它后边插入 +/- number 的操作.
这个就比语法错误复杂很多,而且包含了两个callback类
modifyBound函数 对数值进行增加或者减少
inline void modifyBound(const DeclRefExpr *bo, const SourceManager *sm, Rewriter &r) { // 注意 getEndLoc 可能返回代表范围结束的令牌的开始位置 SourceLocation loc = bo->getEndLoc(); // 返回紧随给定令牌之后的第一个字符的位置 loc = Lexer::getLocForEndOfToken(loc, 0, *sm, r.getLangOpts()); // 修改边界, 范围过大反而不真实 int num = randomNumber(1, 6); int op = randomNumber(0, 2); string num_str = to_string(num); string op_str = ((op == 0) ? " +" : " -"); r.InsertTextAfter(loc, op_str + " " + num_str); outs() << sm->getPresumedLineNumber(loc) << " " << sm->getPresumedColumnNumber(loc) << "\n" << desc << "\n"; }
举例:比如
er int x = 10; int y = x + 5; //执行 SourceLocation loc = bo->getEndLoc(); loc = Lexer::getLocForEndOfToken(loc, 0, *sm, r.getLangOpts()); //结果就是: //5所在的位置
BoundCallbackComm 的run方法 第一个callback类
void run(const MatchFinder::MatchResult &result) override { const auto *bo = result.Nodes.getNodeAs<DeclRefExpr>(id0);//获取id为id0且为声明引用类型的节点,id0为“numvarialbe" const SourceManager *sm = result.SourceManager; if (bo && sm) { function<void(Rewriter &)> f = [bo, sm](Rewriter &r) { modifyBound(bo, sm, r);//然后全部替换为其他值 }; all.push_back(f); } }
辅助函数isLastEquals 判断这个等号是否是最后一个赋值
bool isLastEquals(const BinaryOperator *bin, const SourceManager *sm) { if (!bin || !sm) { return false; } // 等号的确切位置 SourceLocation loc = bin->getOperatorLoc(); // 将 loc 移动到等号的下一个 token 位置 loc = Lexer::getLocForEndOfToken(loc, 0, *sm, LangOptions()); Lexer lexer(sm->getSpellingLoc(loc), LangOptions(), sm->getBufferData(sm->getFileID(loc)).data(), sm->getCharacterData(loc), sm->getBufferData(sm->getFileID(loc)).end());//创建lexer对象,用于对从这个loc到文件末尾的分析 Token token; while (!lexer.LexFromRawLexer(token)) {//读取token,并且存在token当中 // 如果 token 是分号 return true if (token.is(tok::semi)) { return true; } // 找到另一个等号,不是最后一个赋值 if (token.is(tok::equal)) { return false; } } // 没有找到其他的等号,是最后一个赋值 return true; }
辅助函数rightDeclRefs 提取所有右值引用,放到result当中,result就是个vector的引用
void rightDeclRefs(vector<const DeclRefExpr *> &result, const Expr *e, const SourceManager *sm) { if (!e) return; #ifdef AMMON_DEBUG printExpr(e, sm); #endif // 是否是变量使用 const DeclRefExpr *dre = dyn_cast<DeclRefExpr>(e);//能转换成功就代表是变量引用 // dre 不是 DeclRefExpr if (dre == nullptr) { // 在子节点中递归寻找 for (const Stmt *child : e->children()) { const Expr *cur = dyn_cast<Expr>(child); if (cur == nullptr) continue; rightDeclRefs(result, cur, sm); } return; } // dre 是 DeclRefExpr,那么尝试转换为变量声明 const VarDecl *vd = dyn_cast<VarDecl>(dre->getDecl()); // dre 引用的可能不是变量声明 (而是函数声明等), 所以这里是确认 vd 是 // VarDecl? if (vd == nullptr) return; // vd 是 arithmetic? if (!vd->getType()->isArithmeticType()) return; result.push_back(dre); }
BoundCallbackAssi 的run方法 在赋值语句中捕获右侧操作数中所有的变量引用,并将其边界进行修改
比第一个callback函数更加严谨
void run(const MatchFinder::MatchResult &result) override { // 获取赋值表达式 = const BinaryOperator *assi = result.Nodes.getNodeAs<BinaryOperator>(id1);//id1为assignment,就是等号 const SourceManager *sm = result.SourceManager; if (assi && sm) { // 保证是直到下一个分号为止最后一个 = if (!isLastEquals(assi, sm)) return; // 获取赋值右侧的表达式并忽略隐式的类型转换 const Expr *rhs = assi->getRHS()->IgnoreImpCasts(); // 收集所有的符合要求的 DeclRefExpr vector<const DeclRefExpr *> valid; rightDeclRefs(valid, rhs, sm); for (const DeclRefExpr *bo : valid) { if (bo == nullptr) continue; function<void(Rewriter &)> f = [bo, sm](Rewriter &r) { modifyBound(bo, sm, r); }; all.push_back(f); } } }
构造函数 有点不一样,这里考虑了两种情况
BoundSemantic::BoundSemantic(vector<function<void(Rewriter &)>> &all) { this->callbacks.push_back( unique_ptr<MatchFinder::MatchCallback>(new BoundCallbackComm(all))); this->callbacks.push_back( unique_ptr<MatchFinder::MatchCallback>(new BoundCallbackAssi(all))); // 寻找所有的数值类型变量使用, 并且如果上下文中有某些操作符时跳过 // 如果有等号就忽略 StatementMatcher m0 = declRefExpr(isExpansionInMainFile(), to(anyOf(varDecl(hasType(realFloatingPointType())), varDecl(hasType(isInteger())))), unless(hasAncestor(unaryOperator( hasAnyOperatorName("++", "--", "->", ".")))), unless(hasAncestor(binaryOperator(hasOperatorName("="))))) .bind(id0); // 集中处理等号情况 StatementMatcher m1 = binaryOperator(isExpansionInMainFile(), hasOperatorName("="), unless(hasAncestor(unaryOperator( hasAnyOperatorName("++", "--", "->", ".")))), forEachDescendant(declRefExpr( to(anyOf(varDecl(hasType(realFloatingPointType())), varDecl(hasType(isInteger()))))))) .bind(id1); finder.addMatcher(m0, this->callbacks[0].get()); finder.addMatcher(m1, this->callbacks[1].get()); }
VarUseSemantic 变量使用错误: 某个该用变量 a 的地方用成了同一类型的变量 b. 随机挑选类型 T 变量 a, 并替换为已声明的类型 T 变量 b.
定义了两个id:
const string desc = "This bug is a semantic error where the variable <b> is mistakenly used in " "place of the intended variable <a> of the same type"; const string id = "numberOperand"; const string id0 = "numberOperandGet";
内部还包含了一个将bo和sm单独封装成了一个类
class ReplaceVar { public: const DeclRefExpr *bo; const SourceManager *sm; ReplaceVar(const DeclRefExpr *bo, const SourceManager *sm) : bo(bo), sm(sm) {} };
getRandomReplaceName 方法
// 随机替换变量, 返回新变量名,引用更改需要替换的bo与sm string getRandomReplaceName(const DeclRefExpr *decl, const DeclRefExpr *&changedDecl, const SourceManager *&changedSm) { // 随机排序 shuffleVector(replaceList); // 循环遍历找到一个满足条件的变量 for (auto rVar : replaceList) { const NamedDecl *rNamedDecl = dyn_cast<NamedDecl>(rVar.bo->getDecl()); const NamedDecl *namedDecl = dyn_cast<NamedDecl>(decl->getDecl()); // 不同名且同类型 if (decl==nullptr || rNamedDecl->getNameAsString()==namedDecl->getNameAsString()) { continue; } string rType = getDeclType(rVar.bo); string declType = getDeclType(decl); if (rType.empty() || declType.empty() || rType!=declType) { continue; } #ifdef AMMON_DEBUG INFO("**Print Type**"); string typeInfo = namedDecl->getNameAsString()+" "+declType+" "+rNamedDecl->getNameAsString()+" "+rType; INFO(typeInfo); #endif // 两个变量进行比较,用作用域更大的变量替换另一个 if (isLargerScope(rNamedDecl, namedDecl)) { // 用vector中的变量替换传入的变量 return rNamedDecl->getNameAsString(); } else if (isLargerScope(namedDecl, rNamedDecl)) { // 用传入的变量替换vector中的变量 changedDecl = rVar.bo; changedSm = rVar.sm; return namedDecl->getNameAsString(); } } return ""; }
辅助函数:isLargerScope
// srcDecl的作用域是否更大 static bool isLargerScope(const NamedDecl *srcDecl, const NamedDecl *cmpDecl) { const DeclContext *srcContext = srcDecl->getDeclContext(); const DeclContext *cmpContext = cmpDecl->getDeclContext(); while (cmpContext) { if (cmpContext==srcContext) { return true; } cmpContext = cmpContext->getParent(); } return false; }
辅助函数:getDeclType
// 获取变量类型 static string getDeclType(const DeclRefExpr *decl) { const ValueDecl *valuedecl = decl->getDecl(); QualType qualType = valuedecl->getType();//c++中,变量和函数都是值声明的一种 // 跳过typedef clang::Type const *underType = qualType.getTypePtr()->getUnqualifiedDesugaredType(); // 输出string LangOptions lo;//语言选项 PrintingPolicy pp(lo);//打印策略 string s; llvm::raw_string_ostream rso(s);//定义一个字符串和stream对象,用于将类型写入这个字符串 QualType::print(underType, Qualifiers(), rso, lo, llvm::Twine());//将类型信息打入rso中 return rso.str();//返回rso中的字符串 }
辅助函数outputvar
string outputVar() { string declInfo; for (auto &replaceVar : replaceList) { const NamedDecl *namedDecl = dyn_cast<NamedDecl>(replaceVar.bo->getDecl());//转换为命名声明 declInfo += " " + namedDecl->getNameAsString(); } return declInfo; }
VarGetCallback run方法
void run(const MatchFinder::MatchResult &result) override { const DeclRefExpr *bo = result.Nodes.getNodeAs<DeclRefExpr>(id); const SourceManager *sm = result.SourceManager; if (bo && sm) { replaceTool.putVar(bo, sm); #ifdef AMMON_DEBUG printInfo(result, "Get"); #endif } }
这个回调函数只是将匹配到的变量放到replaceList当中
VarUseCallback 真正进行替换
run方法
void run(const MatchFinder::MatchResult &result) override { const DeclRefExpr *bo = result.Nodes.getNodeAs<DeclRefExpr>(id); const SourceManager *sm = result.SourceManager; if (bo && sm) { #ifdef AMMON_DEBUG printInfo(result, "replace"); #endif const DeclRefExpr *changedDecl = bo;//替换的bo const SourceManager *changedSm = sm;//替换的sm const string newName = replaceTool.getRandomReplaceName(bo, changedDecl, changedSm);//调用获取新变量名 if (newName.empty()) { return; } function<void(Rewriter &)> f = [changedDecl, changedSm, newName](Rewriter &r) { const NamedDecl *changedNd = dyn_cast<NamedDecl>(changedDecl->getDecl()); const string oldName = changedNd->getNameAsString(); r.ReplaceText(changedDecl->getSourceRange(), newName);//替换掉 SourceLocation loc = changedDecl->getBeginLoc(); outs() << changedSm->getPresumedLineNumber(loc) << " " << changedSm->getPresumedColumnNumber(loc) << " " << oldName << " " << newName << "\n" << desc << "\n"; }; all.push_back(f); } }
构造函数 也是add两个
VarUseSemantic::VarUseSemantic(vector<function<void(Rewriter &)>> &all) { this->callbacks.push_back( unique_ptr<MatchFinder::MatchCallback>(new VarGetCallback(all))); this->callbacks.push_back( unique_ptr<MatchFinder::MatchCallback>(new VarUseCallback(all))); // 所有有声明的变量使用 StatementMatcher m = declRefExpr(isExpansionInMainFile(), hasDeclaration(varDecl(hasType(isInteger())))) .bind(id); finder.addMatcher(m, this->callbacks[0].get()); finder.addMatcher(m, this->callbacks[1].get()); }
main.cpp SilentDiagConsumer类 用于屏蔽源程序中的诊断信息,即警告和错误信息,不让其输出到控制台或其他目标。
重写了HandleDiagnostic方法,为空,也就是不做任何操作
// 用于将源程序本身的警告屏蔽掉 class SilentDiagConsumer : public clang::DiagnosticConsumer { public: void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) override {} };
AmmonFrontendAction类 继承了ASTFrontendAction 类,用于对Clang前端进行操作
成员变量 private: Rewriter rewriter;//rewriter // 这是不知道 rewriter 里边有 editBuffer 迭代器之前的设计... vector<function<void(Rewriter &)>> all;//存放函数的vector int typeOption;
EndSourceFileAction方法 重写的方法,在处理完源文件之后被调用
// 处理完文件后, 进行的操作 void EndSourceFileAction() override { auto mainFileId = rewriter.getSourceMgr().getMainFileID();//获取主文件的标识符,这个标识符用于识别源文件的位置 int num = all.size();//有多少个修改操作,也就是rewriter if (num == 0) { // 无效的 ASTConsumer throw InvalidConsumerExp(); } // 随机选择一个位置进行改写 int rand = randomNumber(0, num); // 在这里将修改存储在 rewriter 缓冲区中 all[rand](rewriter); // 将整个修改后的文件写入 llvm 输出 rewriter.getEditBuffer(mainFileId).write(outs()); }
CreateASTConsumer方法 也是重写的方法,就是创建ASTComsumer对象用的
unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci, StringRef file) override { // 屏蔽原来的错误和异常信息,用自己的SilentDiagConsumer ci.getDiagnostics().setClient(new SilentDiagConsumer(), true); rewriter.setSourceMgr(ci.getSourceManager(), ci.getLangOpts()); // 为每个文件创建一个 ASTConsumer,调用的是自己写的工厂类 return ASTConsumerFactory::getASTConsumer(all, typeOption); } };
AmmonFrontendActionFactory类 用于创建AmmonFrontedAction对象,貌似就是把他变成unique的?
class AmmonFrontendActionFactory : public FrontendActionFactory { private: int typeOption; public: AmmonFrontendActionFactory(int t) : typeOption(t) {} unique_ptr<FrontendAction> create() override { return make_unique<AmmonFrontendAction>(typeOption); } };
一个新的namespace namespace { // 命令行选项 cl::OptionCategory ammonCategory("Ammon options"); // -t <type> // magic number? int typeOptionInit = ALL_CONSUMER; cl::opt<int> typeOption("t", cl::desc("Specify mutation type"),//t是描述信息,表示变异类型 cl::value_desc("type"), cl::Optional,// cl::init(typeOptionInit), cl::cat(ammonCategory)); void runTool(ClangTool &tool) { // 预留处理其它参数的空间 if (ASTConsumerFactory::isValidType(typeOption)) {//选项是否合法 try { tool.run(newAmmon(typeOption).get());//运行 } catch (const InvalidConsumerExp &e) { } catch (...) { ERROR("Something Unknown Happened"); } return; } // 默认是循环尝试所有 ASTConsumer, 直到有一个可以用 while (true) { // 如果这次选择的 consumer 无效, 则多次选择 try { tool.run(newAmmon(typeOption).get()); } catch (const InvalidConsumerExp &e) { continue; // 继续循环就行了 } catch (const exception &e) { ERROR(e.what()); } catch (...) { ERROR("Something Unknown Happened"); } break; } } } // namespace
main函数 int main(int argc, const char **argv) { cl::HideUnrelatedOptions(ammonCategory);//隐藏与 ammonCategory 不相关的命令行选项 Expected<CommonOptionsParser> expectedParser = CommonOptionsParser::create(argc, argv, ammonCategory);//使用 CommonOptionsParser 类创建一个命令行选项解析器对象 if (!expectedParser) { return 1;//创建失败直接返回 } CommonOptionsParser &op = expectedParser.get();//获取解析器的引用 // 只考虑处理一个文件 ClangTool tool(op.getCompilations(), op.getSourcePathList()); outs() << "==== Start " << op.getSourcePathList()[0] << " ====\n"; runTool(tool); outs() << "==== End ====\n"; return 0; }