xref: /freebsd/contrib/llvm-project/lldb/tools/lldb-instr/Instrument.cpp (revision e9e8876a4d6afc1ad5315faaa191b25121a813d7)
1 #include "clang/AST/AST.h"
2 #include "clang/AST/ASTConsumer.h"
3 #include "clang/AST/RecursiveASTVisitor.h"
4 #include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
5 #include "clang/Frontend/ASTConsumers.h"
6 #include "clang/Frontend/CompilerInstance.h"
7 #include "clang/Frontend/FrontendActions.h"
8 #include "clang/Rewrite/Core/Rewriter.h"
9 #include "clang/Tooling/CommonOptionsParser.h"
10 #include "clang/Tooling/Tooling.h"
11 
12 #include "llvm/ADT/StringExtras.h"
13 #include "llvm/ADT/StringRef.h"
14 #include "llvm/Support/raw_ostream.h"
15 
16 #include <sstream>
17 #include <string>
18 
19 using namespace clang;
20 using namespace clang::driver;
21 using namespace clang::tooling;
22 
23 static llvm::cl::OptionCategory InstrCategory("LLDB Instrumentation Generator");
24 
25 /// Get the macro name for recording method calls.
26 ///
27 /// LLDB_RECORD_METHOD
28 /// LLDB_RECORD_METHOD_CONST
29 /// LLDB_RECORD_METHOD_NO_ARGS
30 /// LLDB_RECORD_METHOD_CONST_NO_ARGS
31 /// LLDB_RECORD_STATIC_METHOD
32 /// LLDB_RECORD_STATIC_METHOD_NO_ARGS
33 static std::string GetRecordMethodMacroName(bool Static, bool Const,
34                                             bool NoArgs) {
35   std::string Macro;
36   llvm::raw_string_ostream OS(Macro);
37 
38   OS << "LLDB_RECORD";
39   if (Static)
40     OS << "_STATIC";
41   OS << "_METHOD";
42   if (Const)
43     OS << "_CONST";
44   if (NoArgs)
45     OS << "_NO_ARGS";
46 
47   return OS.str();
48 }
49 
50 /// Get the macro name for register methods.
51 ///
52 /// LLDB_REGISTER_CONSTRUCTOR
53 /// LLDB_REGISTER_METHOD
54 /// LLDB_REGISTER_METHOD_CONST
55 /// LLDB_REGISTER_STATIC_METHOD
56 static std::string GetRegisterMethodMacroName(bool Static, bool Const) {
57   std::string Macro;
58   llvm::raw_string_ostream OS(Macro);
59 
60   OS << "LLDB_REGISTER";
61   if (Static)
62     OS << "_STATIC";
63   OS << "_METHOD";
64   if (Const)
65     OS << "_CONST";
66 
67   return OS.str();
68 }
69 
70 static std::string GetRecordMethodMacro(StringRef Result, StringRef Class,
71                                         StringRef Method, StringRef Signature,
72                                         StringRef Values, bool Static,
73                                         bool Const) {
74   std::string Macro;
75   llvm::raw_string_ostream OS(Macro);
76 
77   OS << GetRecordMethodMacroName(Static, Const, Values.empty());
78   OS << "(" << Result << ", " << Class << ", " << Method;
79 
80   if (!Values.empty()) {
81     OS << ", (" << Signature << "), " << Values << ");\n\n";
82   } else {
83     OS << ");\n\n";
84   }
85 
86   return OS.str();
87 }
88 
89 static std::string GetRecordConstructorMacro(StringRef Class,
90                                              StringRef Signature,
91                                              StringRef Values) {
92   std::string Macro;
93   llvm::raw_string_ostream OS(Macro);
94   if (!Values.empty()) {
95     OS << "LLDB_RECORD_CONSTRUCTOR(" << Class << ", (" << Signature << "), "
96        << Values << ");\n\n";
97   } else {
98     OS << "LLDB_RECORD_CONSTRUCTOR_NO_ARGS(" << Class << ");\n\n";
99   }
100   return OS.str();
101 }
102 
103 static std::string GetRecordDummyMacro(StringRef Result, StringRef Class,
104                                        StringRef Method, StringRef Signature,
105                                        StringRef Values) {
106   assert(!Values.empty());
107   std::string Macro;
108   llvm::raw_string_ostream OS(Macro);
109 
110   OS << "LLDB_RECORD_DUMMY(" << Result << ", " << Class << ", " << Method;
111   OS << ", (" << Signature << "), " << Values << ");\n\n";
112 
113   return OS.str();
114 }
115 
116 static std::string GetRegisterConstructorMacro(StringRef Class,
117                                                StringRef Signature) {
118   std::string Macro;
119   llvm::raw_string_ostream OS(Macro);
120   OS << "LLDB_REGISTER_CONSTRUCTOR(" << Class << ", (" << Signature << "));\n";
121   return OS.str();
122 }
123 
124 static std::string GetRegisterMethodMacro(StringRef Result, StringRef Class,
125                                           StringRef Method, StringRef Signature,
126                                           bool Static, bool Const) {
127   std::string Macro;
128   llvm::raw_string_ostream OS(Macro);
129   OS << GetRegisterMethodMacroName(Static, Const);
130   OS << "(" << Result << ", " << Class << ", " << Method << ", (" << Signature
131      << "));\n";
132   return OS.str();
133 }
134 
135 class SBReturnVisitor : public RecursiveASTVisitor<SBReturnVisitor> {
136 public:
137   SBReturnVisitor(Rewriter &R) : MyRewriter(R) {}
138 
139   bool VisitReturnStmt(ReturnStmt *Stmt) {
140     Expr *E = Stmt->getRetValue();
141 
142     if (E->getBeginLoc().isMacroID())
143       return false;
144 
145     SourceRange R(E->getBeginLoc(), E->getEndLoc());
146 
147     StringRef WrittenExpr = Lexer::getSourceText(
148         CharSourceRange::getTokenRange(R), MyRewriter.getSourceMgr(),
149         MyRewriter.getLangOpts());
150 
151     std::string ReplacementText =
152         "LLDB_RECORD_RESULT(" + WrittenExpr.str() + ")";
153     MyRewriter.ReplaceText(R, ReplacementText);
154 
155     return true;
156   }
157 
158 private:
159   Rewriter &MyRewriter;
160 };
161 
162 class SBVisitor : public RecursiveASTVisitor<SBVisitor> {
163 public:
164   SBVisitor(Rewriter &R, ASTContext &Context)
165       : MyRewriter(R), Context(Context) {}
166 
167   bool VisitCXXMethodDecl(CXXMethodDecl *Decl) {
168     // Not all decls should be registered. Please refer to that method's
169     // comment for details.
170     if (ShouldSkip(Decl))
171       return false;
172 
173     // Skip CXXMethodDecls that already starts with a macro. This should make
174     // it easier to rerun the tool to find missing macros.
175     Stmt *Body = Decl->getBody();
176     for (auto &C : Body->children()) {
177       if (C->getBeginLoc().isMacroID())
178         return false;
179       break;
180     }
181 
182     // Print 'bool' instead of '_Bool'.
183     PrintingPolicy Policy(Context.getLangOpts());
184     Policy.Bool = true;
185 
186     // Unsupported signatures get a dummy macro.
187     bool ShouldInsertDummy = false;
188 
189     // Collect the functions parameter types and names.
190     std::vector<std::string> ParamTypes;
191     std::vector<std::string> ParamNames;
192     for (auto *P : Decl->parameters()) {
193       QualType T = P->getType();
194       ParamTypes.push_back(T.getAsString(Policy));
195       ParamNames.push_back(P->getNameAsString());
196 
197       // Currently we don't support functions that have function pointers as an
198       // argument, in which case we insert a dummy macro.
199       ShouldInsertDummy |= T->isFunctionPointerType();
200     }
201 
202     // Convert the two lists to string for the macros.
203     std::string ParamTypesStr = llvm::join(ParamTypes, ", ");
204     std::string ParamNamesStr = llvm::join(ParamNames, ", ");
205 
206     CXXRecordDecl *Record = Decl->getParent();
207     QualType ReturnType = Decl->getReturnType();
208 
209     // Construct the macros.
210     std::string Macro;
211     if (ShouldInsertDummy) {
212       // Don't insert a register call for dummy macros.
213       Macro = GetRecordDummyMacro(
214           ReturnType.getAsString(Policy), Record->getNameAsString(),
215           Decl->getNameAsString(), ParamTypesStr, ParamNamesStr);
216 
217     } else if (isa<CXXConstructorDecl>(Decl)) {
218       llvm::outs() << GetRegisterConstructorMacro(Record->getNameAsString(),
219                                                   ParamTypesStr);
220 
221       Macro = GetRecordConstructorMacro(Record->getNameAsString(),
222                                         ParamTypesStr, ParamNamesStr);
223     } else {
224       llvm::outs() << GetRegisterMethodMacro(
225           ReturnType.getAsString(Policy), Record->getNameAsString(),
226           Decl->getNameAsString(), ParamTypesStr, Decl->isStatic(),
227           Decl->isConst());
228 
229       Macro = GetRecordMethodMacro(
230           ReturnType.getAsString(Policy), Record->getNameAsString(),
231           Decl->getNameAsString(), ParamTypesStr, ParamNamesStr,
232           Decl->isStatic(), Decl->isConst());
233     }
234 
235     // Insert the macro at the beginning of the function. We don't attempt to
236     // fix the formatting and instead rely on clang-format to fix it after the
237     // tool has run. This is also the reason that the macros end with two
238     // newlines, counting on clang-format to normalize this in case the macro
239     // got inserted before an existing newline.
240     SourceLocation InsertLoc = Lexer::getLocForEndOfToken(
241         Body->getBeginLoc(), 0, MyRewriter.getSourceMgr(),
242         MyRewriter.getLangOpts());
243     MyRewriter.InsertTextAfter(InsertLoc, Macro);
244 
245     // If the function returns a class or struct, we need to wrap its return
246     // statement(s).
247     bool ShouldRecordResult = ReturnType->isStructureOrClassType() ||
248                               ReturnType->getPointeeCXXRecordDecl();
249     if (!ShouldInsertDummy && ShouldRecordResult) {
250       SBReturnVisitor Visitor(MyRewriter);
251       Visitor.TraverseDecl(Decl);
252     }
253 
254     return true;
255   }
256 
257 private:
258   /// Determine whether we need to consider the given CXXMethodDecl.
259   ///
260   /// Currently we skip the following cases:
261   ///  1. Decls outside the main source file,
262   ///  2. Decls that are only present in the source file,
263   ///  3. Decls that are not definitions,
264   ///  4. Non-public methods,
265   ///  5. Variadic methods.
266   ///  6. Destructors.
267   bool ShouldSkip(CXXMethodDecl *Decl) {
268     // Skip anything outside the main file.
269     if (!MyRewriter.getSourceMgr().isInMainFile(Decl->getBeginLoc()))
270       return true;
271 
272     // Skip if the canonical decl in the current decl. It means that the method
273     // is declared in the implementation and is therefore not exposed as part
274     // of the API.
275     if (Decl == Decl->getCanonicalDecl())
276       return true;
277 
278     // Skip decls that have no body, i.e. are just declarations.
279     Stmt *Body = Decl->getBody();
280     if (!Body)
281       return true;
282 
283     // Skip non-public methods.
284     AccessSpecifier AS = Decl->getAccess();
285     if (AS != AccessSpecifier::AS_public)
286       return true;
287 
288     // Skip variadic methods.
289     if (Decl->isVariadic())
290       return true;
291 
292     // Skip destructors.
293     if (isa<CXXDestructorDecl>(Decl))
294       return true;
295 
296     return false;
297   }
298 
299   Rewriter &MyRewriter;
300   ASTContext &Context;
301 };
302 
303 class SBConsumer : public ASTConsumer {
304 public:
305   SBConsumer(Rewriter &R, ASTContext &Context) : Visitor(R, Context) {}
306 
307   // Override the method that gets called for each parsed top-level
308   // declaration.
309   bool HandleTopLevelDecl(DeclGroupRef DR) override {
310     for (DeclGroupRef::iterator b = DR.begin(), e = DR.end(); b != e; ++b) {
311       Visitor.TraverseDecl(*b);
312     }
313     return true;
314   }
315 
316 private:
317   SBVisitor Visitor;
318 };
319 
320 class SBAction : public ASTFrontendAction {
321 public:
322   SBAction() = default;
323 
324   bool BeginSourceFileAction(CompilerInstance &CI) override {
325     llvm::outs() << "{\n";
326     return true;
327   }
328 
329   void EndSourceFileAction() override {
330     llvm::outs() << "}\n";
331     MyRewriter.overwriteChangedFiles();
332   }
333 
334   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
335                                                  StringRef File) override {
336     MyRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts());
337     return std::make_unique<SBConsumer>(MyRewriter, CI.getASTContext());
338   }
339 
340 private:
341   Rewriter MyRewriter;
342 };
343 
344 int main(int argc, const char **argv) {
345   auto ExpectedParser = CommonOptionsParser::create(
346       argc, argv, InstrCategory, llvm::cl::OneOrMore,
347       "Utility for generating the macros for LLDB's "
348       "instrumentation framework.");
349   if (!ExpectedParser) {
350     llvm::errs() << ExpectedParser.takeError();
351     return 1;
352   }
353   CommonOptionsParser &OP = ExpectedParser.get();
354 
355   auto PCHOpts = std::make_shared<PCHContainerOperations>();
356   PCHOpts->registerWriter(std::make_unique<ObjectFilePCHContainerWriter>());
357   PCHOpts->registerReader(std::make_unique<ObjectFilePCHContainerReader>());
358 
359   ClangTool T(OP.getCompilations(), OP.getSourcePathList(), PCHOpts);
360   return T.run(newFrontendActionFactory<SBAction>().get());
361 }
362