blob: cbc284233753c0b336799fbeebc80616495e4c72 [file] [log] [blame]
Jonas Devlieghereedff5f42019-02-06 04:33:14 +00001#include "clang/AST/AST.h"
2#include "clang/AST/ASTConsumer.h"
3#include "clang/AST/RecursiveASTVisitor.h"
Jonas Devlieghere1ca9dd82019-02-12 18:19:34 +00004#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
Jonas Devlieghereedff5f42019-02-06 04:33:14 +00005#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
19using namespace clang;
20using namespace clang::driver;
21using namespace clang::tooling;
22
23static 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
33static 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
56static 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
70static 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
89static 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
103static std::string GetRegisterConstructorMacro(StringRef Class,
104 StringRef Signature) {
105 std::string Macro;
106 llvm::raw_string_ostream OS(Macro);
107 OS << "LLDB_REGISTER_CONSTRUCTOR(" << Class << ", (" << Signature
108 << "));\n\n";
109 return OS.str();
110}
111
112static std::string GetRegisterMethodMacro(StringRef Result, StringRef Class,
113 StringRef Method, StringRef Signature,
114 bool Static, bool Const) {
115 std::string Macro;
116 llvm::raw_string_ostream OS(Macro);
117 OS << GetRegisterMethodMacroName(Static, Const);
118 OS << "(" << Result << ", " << Class << ", " << Method << ", (" << Signature
119 << "));\n";
120 return OS.str();
121}
122
123class SBVisitor : public RecursiveASTVisitor<SBVisitor> {
124public:
125 SBVisitor(Rewriter &R, ASTContext &Context)
126 : MyRewriter(R), Context(Context) {}
127
128 bool VisitCXXMethodDecl(CXXMethodDecl *Decl) {
129 // Not all decls should be registered. Please refer to that method's
130 // comment for details.
131 if (ShouldSkip(Decl))
132 return false;
133
134 // Print 'bool' instead of '_Bool'.
135 PrintingPolicy Policy(Context.getLangOpts());
136 Policy.Bool = true;
137
138 // Collect the functions parameter types and names.
139 std::vector<std::string> ParamTypes;
140 std::vector<std::string> ParamNames;
141 for (auto *P : Decl->parameters()) {
142 QualType T = P->getType();
143
144 // Currently we don't support functions that have function pointers as an
145 // argument.
146 if (T->isFunctionPointerType())
147 return false;
148
149 // Currently we don't support functions that have void pointers as an
150 // argument.
151 if (T->isVoidPointerType())
152 return false;
153
154 ParamTypes.push_back(T.getAsString(Policy));
155 ParamNames.push_back(P->getNameAsString());
156 }
157
158 // Convert the two lists to string for the macros.
159 std::string ParamTypesStr = llvm::join(ParamTypes, ", ");
160 std::string ParamNamesStr = llvm::join(ParamNames, ", ");
161
162 CXXRecordDecl *Record = Decl->getParent();
163 QualType ReturnType = Decl->getReturnType();
164
165 // Construct the macros.
166 std::string Macro;
167 if (isa<CXXConstructorDecl>(Decl)) {
168 llvm::outs() << GetRegisterConstructorMacro(Record->getNameAsString(),
169 ParamTypesStr);
170
171 Macro = GetRecordConstructorMacro(Record->getNameAsString(),
172 ParamTypesStr, ParamNamesStr);
173 } else {
174 llvm::outs() << GetRegisterMethodMacro(
175 ReturnType.getAsString(Policy), Record->getNameAsString(),
176 Decl->getNameAsString(), ParamTypesStr, Decl->isStatic(),
177 Decl->isConst());
178
179 Macro = GetRecordMethodMacro(
180 ReturnType.getAsString(Policy), Record->getNameAsString(),
181 Decl->getNameAsString(), ParamTypesStr, ParamNamesStr,
182 Decl->isStatic(), Decl->isConst());
183 }
184
185 // If this CXXMethodDecl already starts with a macro we're done.
186 Stmt *Body = Decl->getBody();
187 for (auto &C : Body->children()) {
188 if (C->getBeginLoc().isMacroID())
189 return false;
190 break;
191 }
192
193 // Insert the macro at the beginning of the function. We don't attempt to
194 // fix the formatting and instead rely on clang-format to fix it after the
195 // tool has run. This is also the reason that the macros end with two
196 // newlines, counting on clang-format to normalize this in case the macro
197 // got inserted before an existing newline.
198 SourceLocation InsertLoc = Lexer::getLocForEndOfToken(
199 Body->getBeginLoc(), 0, MyRewriter.getSourceMgr(),
200 MyRewriter.getLangOpts());
201 MyRewriter.InsertTextAfter(InsertLoc, Macro);
202
203 return true;
204 }
205
206private:
207 /// Determine whether we need to consider the given CXXMethodDecl.
208 ///
209 /// Currently we skip the following cases:
210 /// 1. Decls outside the main source file,
211 /// 2. Decls that are only present in the source file,
212 /// 3. Decls that are not definitions,
213 /// 4. Non-public decls,
214 /// 5. Destructors.
215 bool ShouldSkip(CXXMethodDecl *Decl) {
216 // Skip anything outside the main file.
217 if (!MyRewriter.getSourceMgr().isInMainFile(Decl->getBeginLoc()))
218 return true;
219
220 // Skip if the canonical decl in the current decl. It means that the method
221 // is declared in the implementation and is therefore not exposed as part
222 // of the API.
223 if (Decl == Decl->getCanonicalDecl())
224 return true;
225
226 // Skip decls that have no body, i.e. are just declarations.
227 Stmt *Body = Decl->getBody();
228 if (!Body)
229 return true;
230
231 // Skip non-public decls.
232 AccessSpecifier AS = Decl->getAccess();
233 if (AS != AccessSpecifier::AS_public)
234 return true;
235
236 // Skip destructors.
237 if (isa<CXXDestructorDecl>(Decl))
238 return true;
239
240 return false;
241 }
242
243 Rewriter &MyRewriter;
244 ASTContext &Context;
245};
246
247class SBConsumer : public ASTConsumer {
248public:
249 SBConsumer(Rewriter &R, ASTContext &Context) : Visitor(R, Context) {}
250
251 // Override the method that gets called for each parsed top-level
252 // declaration.
253 bool HandleTopLevelDecl(DeclGroupRef DR) override {
254 for (DeclGroupRef::iterator b = DR.begin(), e = DR.end(); b != e; ++b) {
255 Visitor.TraverseDecl(*b);
256 }
257 return true;
258 }
259
260private:
261 SBVisitor Visitor;
262};
263
264class SBAction : public ASTFrontendAction {
265public:
266 SBAction() = default;
267
268 void EndSourceFileAction() override { MyRewriter.overwriteChangedFiles(); }
269
270 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
271 StringRef file) override {
272 MyRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts());
273 return llvm::make_unique<SBConsumer>(MyRewriter, CI.getASTContext());
274 }
275
276private:
277 Rewriter MyRewriter;
278};
279
280int main(int argc, const char **argv) {
281 CommonOptionsParser OP(argc, argv, InstrCategory,
282 "Utility for generating the macros for LLDB's "
283 "instrumentation framework.");
Jonas Devlieghere1ca9dd82019-02-12 18:19:34 +0000284
285 auto PCHOpts = std::make_shared<PCHContainerOperations>();
286 PCHOpts->registerWriter(llvm::make_unique<ObjectFilePCHContainerWriter>());
287 PCHOpts->registerReader(llvm::make_unique<ObjectFilePCHContainerReader>());
288
289 ClangTool T(OP.getCompilations(), OP.getSourcePathList(), PCHOpts);
Jonas Devlieghereedff5f42019-02-06 04:33:14 +0000290 return T.run(newFrontendActionFactory<SBAction>().get());
291}