Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp
213799 views
1
//===-- lldb-rpc-gen.cpp ----------------------------------------*- C++ -*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "RPCCommon.h"
10
#include "RPCServerHeaderEmitter.h"
11
#include "RPCServerSourceEmitter.h"
12
13
#include "clang/AST/AST.h"
14
#include "clang/AST/ASTConsumer.h"
15
#include "clang/AST/ASTContext.h"
16
#include "clang/AST/RecursiveASTVisitor.h"
17
#include "clang/Basic/SourceManager.h"
18
#include "clang/CodeGen/ObjectFilePCHContainerWriter.h"
19
#include "clang/Frontend/CompilerInstance.h"
20
#include "clang/Frontend/FrontendAction.h"
21
#include "clang/Frontend/FrontendActions.h"
22
#include "clang/Serialization/ObjectFilePCHContainerReader.h"
23
#include "clang/Tooling/CommonOptionsParser.h"
24
#include "clang/Tooling/Tooling.h"
25
26
#include "llvm/ADT/StringRef.h"
27
#include "llvm/Support/CommandLine.h"
28
#include "llvm/Support/Path.h"
29
#include "llvm/Support/ToolOutputFile.h"
30
#include "llvm/Support/raw_ostream.h"
31
32
using namespace clang;
33
using namespace clang::driver;
34
using namespace clang::tooling;
35
36
static llvm::cl::OptionCategory RPCGenCategory("Tool for generating LLDBRPC");
37
38
static llvm::cl::opt<std::string>
39
OutputDir("output-dir",
40
llvm::cl::desc("Directory to output generated files to"),
41
llvm::cl::init(""), llvm::cl::cat(RPCGenCategory));
42
43
static std::string GetLibraryOutputDirectory() {
44
llvm::SmallString<128> Path(OutputDir.getValue());
45
llvm::sys::path::append(Path, "lib");
46
return std::string(Path);
47
}
48
49
static std::unique_ptr<llvm::ToolOutputFile>
50
CreateOutputFile(llvm::StringRef OutputDir, llvm::StringRef Filename) {
51
llvm::SmallString<128> Path(OutputDir);
52
llvm::sys::path::append(Path, Filename);
53
54
std::error_code EC;
55
auto OutputFile =
56
std::make_unique<llvm::ToolOutputFile>(Path, EC, llvm::sys::fs::OF_None);
57
if (EC) {
58
llvm::errs() << "Failed to create output file: " << Path << "!\n";
59
return nullptr;
60
}
61
return OutputFile;
62
}
63
64
struct GeneratedByproducts {
65
std::set<std::string> ClassNames;
66
std::set<std::string> MangledMethodNames;
67
std::set<std::string> SkippedMethodNames;
68
std::set<lldb_rpc_gen::Method> CallbackMethods;
69
};
70
71
enum SupportLevel {
72
eUnsupported,
73
eUnimplemented,
74
eImplemented,
75
};
76
77
class SBVisitor : public RecursiveASTVisitor<SBVisitor> {
78
public:
79
SBVisitor(GeneratedByproducts &Byproducts, SourceManager &Manager,
80
ASTContext &Context,
81
std::unique_ptr<llvm::ToolOutputFile> &&ServerMethodOutputFile,
82
std::unique_ptr<llvm::ToolOutputFile> &&ServerHeaderOutputFile)
83
: Byproducts(Byproducts), Manager(Manager), Context(Context),
84
ServerSourceEmitter(std::move(ServerMethodOutputFile)),
85
ServerHeaderEmitter(std::move(ServerHeaderOutputFile)) {}
86
87
~SBVisitor() {}
88
89
bool VisitCXXRecordDecl(CXXRecordDecl *RDecl) {
90
if (ShouldSkipRecord(RDecl))
91
return true;
92
93
const std::string ClassName = RDecl->getNameAsString();
94
Byproducts.ClassNames.insert(ClassName);
95
96
// Print 'bool' instead of '_Bool'.
97
PrintingPolicy Policy(Context.getLangOpts());
98
Policy.Bool = true;
99
100
for (CXXMethodDecl *MDecl : RDecl->methods()) {
101
const std::string MangledName =
102
lldb_rpc_gen::GetMangledName(Context, MDecl);
103
const bool IsDisallowed = lldb_rpc_gen::MethodIsDisallowed(MangledName);
104
const bool HasCallbackParameter =
105
lldb_rpc_gen::HasCallbackParameter(MDecl);
106
SupportLevel MethodSupportLevel = GetMethodSupportLevel(MDecl);
107
if (MethodSupportLevel == eImplemented && !IsDisallowed) {
108
const lldb_rpc_gen::Method Method(MDecl, Policy, Context);
109
ServerSourceEmitter.EmitMethod(Method);
110
ServerHeaderEmitter.EmitMethod(Method);
111
Byproducts.MangledMethodNames.insert(MangledName);
112
} else if (MethodSupportLevel == eUnimplemented)
113
Byproducts.SkippedMethodNames.insert(MangledName);
114
}
115
return true;
116
}
117
118
private:
119
/// Determines whether we should skip a RecordDecl.
120
/// Conditions for skipping:
121
/// - Anything not in the header itself
122
/// - Certain inconvenient classes
123
/// - Records without definitions (forward declarations)
124
bool ShouldSkipRecord(CXXRecordDecl *Decl) {
125
const Type *DeclType = Decl->getTypeForDecl();
126
QualType CanonicalType = DeclType->getCanonicalTypeInternal();
127
return !Manager.isInMainFile(Decl->getBeginLoc()) ||
128
!Decl->hasDefinition() || Decl->getDefinition() != Decl ||
129
lldb_rpc_gen::TypeIsDisallowedClass(CanonicalType);
130
}
131
132
/// Check the support level for a type
133
/// Known unsupported types:
134
/// - FILE * (We do not want to expose this primitive)
135
/// - Types that are internal to LLDB
136
SupportLevel GetTypeSupportLevel(QualType Type) {
137
const std::string TypeName = Type.getAsString();
138
if (TypeName == "FILE *" || lldb_rpc_gen::TypeIsFromLLDBPrivate(Type))
139
return eUnsupported;
140
141
if (lldb_rpc_gen::TypeIsDisallowedClass(Type))
142
return eUnsupported;
143
144
return eImplemented;
145
}
146
147
/// Determine the support level of a given method.
148
/// Known unsupported methods:
149
/// - Non-public methods (lldb-rpc is a client and can only see public
150
/// things)
151
/// - Copy assignment operators (the client side will handle this)
152
/// - Move assignment operators (the client side will handle this)
153
/// - Methods involving unsupported types.
154
/// Known unimplemented methods:
155
/// - No variadic functions, e.g. Printf
156
SupportLevel GetMethodSupportLevel(CXXMethodDecl *MDecl) {
157
AccessSpecifier AS = MDecl->getAccess();
158
if (AS != AccessSpecifier::AS_public)
159
return eUnsupported;
160
if (MDecl->isCopyAssignmentOperator())
161
return eUnsupported;
162
if (MDecl->isMoveAssignmentOperator())
163
return eUnsupported;
164
165
if (MDecl->isVariadic())
166
return eUnimplemented;
167
168
SupportLevel ReturnTypeLevel = GetTypeSupportLevel(MDecl->getReturnType());
169
if (ReturnTypeLevel != eImplemented)
170
return ReturnTypeLevel;
171
172
for (auto *ParamDecl : MDecl->parameters()) {
173
SupportLevel ParamTypeLevel = GetTypeSupportLevel(ParamDecl->getType());
174
if (ParamTypeLevel != eImplemented)
175
return ParamTypeLevel;
176
}
177
178
// FIXME: If a callback does not take a `void *baton` parameter, it is
179
// considered unsupported at this time. On the server-side, we hijack the
180
// baton argument in order to pass additional information to the server-side
181
// callback so we can correctly perform a reverse RPC call back to the
182
// client. Without this baton, we would need the server-side callback to
183
// have some side channel by which it obtained that information, and
184
// spending time designing that doesn't outweight the cost of doing it at
185
// the moment.
186
bool HasCallbackParameter = false;
187
bool HasBatonParameter = false;
188
auto End = MDecl->parameters().end();
189
for (auto Iter = MDecl->parameters().begin(); Iter != End; Iter++) {
190
if ((*Iter)->getType()->isFunctionPointerType()) {
191
HasCallbackParameter = true;
192
continue;
193
}
194
195
// FIXME: We assume that if we have a function pointer and a void pointer
196
// together in the same parameter list, that it is not followed by a
197
// length argument. If that changes, we will need to revisit this
198
// implementation.
199
if ((*Iter)->getType()->isVoidPointerType())
200
HasBatonParameter = true;
201
}
202
203
if (HasCallbackParameter && !HasBatonParameter)
204
return eUnimplemented;
205
206
return eImplemented;
207
}
208
209
GeneratedByproducts &Byproducts;
210
SourceManager &Manager;
211
ASTContext &Context;
212
lldb_rpc_gen::RPCServerSourceEmitter ServerSourceEmitter;
213
lldb_rpc_gen::RPCServerHeaderEmitter ServerHeaderEmitter;
214
};
215
216
class SBConsumer : public ASTConsumer {
217
public:
218
SBConsumer(GeneratedByproducts &Byproducts, SourceManager &Manager,
219
ASTContext &Context,
220
std::unique_ptr<llvm::ToolOutputFile> &&ServerMethodOutputFile,
221
std::unique_ptr<llvm::ToolOutputFile> &&ServerHeaderOutputFile)
222
: Visitor(Byproducts, Manager, Context, std::move(ServerMethodOutputFile),
223
std::move(ServerHeaderOutputFile)) {}
224
bool HandleTopLevelDecl(DeclGroupRef DR) override {
225
for (Decl *D : DR)
226
Visitor.TraverseDecl(D);
227
228
return true;
229
}
230
231
private:
232
SBVisitor Visitor;
233
};
234
235
class SBAction : public ASTFrontendAction {
236
public:
237
SBAction(GeneratedByproducts &Byproducts) : Byproducts(Byproducts) {}
238
239
std::unique_ptr<ASTConsumer>
240
CreateASTConsumer(CompilerInstance &CI, llvm::StringRef File) override {
241
llvm::StringRef FilenameNoExt =
242
llvm::sys::path::stem(llvm::sys::path::filename(File));
243
244
const std::string ServerMethodFilename =
245
"Server_" + FilenameNoExt.str() + ".cpp";
246
std::unique_ptr<llvm::ToolOutputFile> ServerMethodOutputFile =
247
CreateOutputFile(GetServerOutputDirectory(), ServerMethodFilename);
248
if (!ServerMethodOutputFile)
249
return nullptr;
250
251
const std::string ServerHeaderFilename =
252
"Server_" + FilenameNoExt.str() + ".h";
253
std::unique_ptr<llvm::ToolOutputFile> ServerHeaderOutputFile =
254
CreateOutputFile(GetServerOutputDirectory(), ServerHeaderFilename);
255
if (!ServerHeaderOutputFile)
256
return nullptr;
257
258
ServerMethodOutputFile->keep();
259
ServerHeaderOutputFile->keep();
260
return std::make_unique<SBConsumer>(
261
Byproducts, CI.getSourceManager(), CI.getASTContext(),
262
std::move(ServerMethodOutputFile), std::move(ServerHeaderOutputFile));
263
}
264
265
private:
266
GeneratedByproducts &Byproducts;
267
};
268
269
class SBActionFactory : public FrontendActionFactory {
270
public:
271
SBActionFactory(GeneratedByproducts &Byproducts) : Byproducts(Byproducts) {}
272
273
std::unique_ptr<FrontendAction> create() override {
274
return std::make_unique<SBAction>(Byproducts);
275
}
276
277
private:
278
GeneratedByproducts &Byproducts;
279
};
280
281
bool EmitAmalgamatedServerHeader(const std::vector<std::string> &Files) {
282
// Create the file
283
static constexpr llvm::StringLiteral AmalgamatedServerHeaderName = "SBAPI.h";
284
std::unique_ptr<llvm::ToolOutputFile> AmalgamatedServerHeader =
285
CreateOutputFile(GetServerOutputDirectory(), AmalgamatedServerHeaderName);
286
if (!AmalgamatedServerHeader)
287
return false;
288
289
// Write the header
290
AmalgamatedServerHeader->os()
291
<< "#ifndef GENERATED_LLDB_RPC_SERVER_SBAPI_H\n";
292
AmalgamatedServerHeader->os()
293
<< "#define GENERATED_LLDB_RPC_SERVER_SBAPI_H\n";
294
for (const auto &File : Files) {
295
llvm::StringRef FilenameNoExt =
296
llvm::sys::path::stem(llvm::sys::path::filename(File));
297
const std::string ServerHeaderFilename =
298
"Server_" + FilenameNoExt.str() + ".h";
299
300
AmalgamatedServerHeader->os()
301
<< "#include \"" + ServerHeaderFilename + "\"\n";
302
}
303
AmalgamatedServerHeader->os() << "#include \"SBAPIExtensions.h\"\n";
304
AmalgamatedServerHeader->os()
305
<< "#endif // GENERATED_LLDB_RPC_SERVER_SBAPI_H\n";
306
AmalgamatedServerHeader->keep();
307
return true;
308
}
309
310
bool EmitClassNamesFile(std::set<std::string> &ClassNames) {
311
static constexpr llvm::StringLiteral ClassNamesFileName = "SBClasses.def";
312
std::unique_ptr<llvm::ToolOutputFile> ClassNamesFile =
313
CreateOutputFile(OutputDir.getValue(), ClassNamesFileName);
314
if (!ClassNamesFile)
315
return false;
316
317
ClassNamesFile->os() << "#ifndef SBCLASS\n";
318
ClassNamesFile->os() << "#error \"SBClass must be defined\"\n";
319
ClassNamesFile->os() << "#endif\n";
320
321
for (const auto &ClassName : ClassNames) {
322
if (ClassName == "SBStream" || ClassName == "SBProgress")
323
ClassNamesFile->os() << "#if !defined(SBCLASS_EXCLUDE_NONCOPYABLE)\n";
324
else if (ClassName == "SBReproducer")
325
ClassNamesFile->os() << "#if !defined(SBCLASS_EXCLUDE_STATICONLY)\n";
326
327
ClassNamesFile->os() << "SBCLASS(" << ClassName << ")\n";
328
if (ClassName == "SBStream" || ClassName == "SBReproducer" ||
329
ClassName == "SBProgress")
330
ClassNamesFile->os() << "#endif\n";
331
}
332
ClassNamesFile->keep();
333
return true;
334
}
335
336
bool EmitMethodNamesFile(std::set<std::string> &MangledMethodNames) {
337
static constexpr llvm::StringLiteral MethodNamesFileName = "SBAPI.def";
338
std::unique_ptr<llvm::ToolOutputFile> MethodNamesFile =
339
CreateOutputFile(OutputDir.getValue(), MethodNamesFileName);
340
if (!MethodNamesFile)
341
return false;
342
343
MethodNamesFile->os() << "#ifndef GENERATE_SBAPI\n";
344
MethodNamesFile->os() << "#error \"GENERATE_SBAPI must be defined\"\n";
345
MethodNamesFile->os() << "#endif\n";
346
347
for (const auto &MangledName : MangledMethodNames) {
348
MethodNamesFile->os() << "GENERATE_SBAPI(" << MangledName << ")\n";
349
}
350
MethodNamesFile->keep();
351
return true;
352
}
353
354
bool EmitSkippedMethodsFile(std::set<std::string> &SkippedMethodNames) {
355
static constexpr llvm::StringLiteral FileName = "SkippedMethods.txt";
356
std::unique_ptr<llvm::ToolOutputFile> File =
357
CreateOutputFile(OutputDir.getValue(), FileName);
358
if (!File)
359
return false;
360
361
for (const auto &Skipped : SkippedMethodNames) {
362
File->os() << Skipped << "\n";
363
}
364
File->keep();
365
return true;
366
}
367
368
int main(int argc, const char *argv[]) {
369
auto ExpectedParser = CommonOptionsParser::create(
370
argc, argv, RPCGenCategory, llvm::cl::OneOrMore,
371
"Tool for generating LLDBRPC interfaces and implementations");
372
373
if (!ExpectedParser) {
374
llvm::errs() << ExpectedParser.takeError();
375
return 1;
376
}
377
378
if (OutputDir.empty()) {
379
llvm::errs() << "Please specify an output directory for the generated "
380
"files with --output-dir!\n";
381
return 1;
382
}
383
384
CommonOptionsParser &OP = ExpectedParser.get();
385
auto PCHOpts = std::make_shared<PCHContainerOperations>();
386
PCHOpts->registerWriter(std::make_unique<ObjectFilePCHContainerWriter>());
387
PCHOpts->registerReader(std::make_unique<ObjectFilePCHContainerReader>());
388
389
ClangTool T(OP.getCompilations(), OP.getSourcePathList(), PCHOpts);
390
391
if (!EmitAmalgamatedServerHeader(OP.getSourcePathList())) {
392
llvm::errs() << "Failed to create amalgamated server header\n";
393
return 1;
394
}
395
396
GeneratedByproducts Byproducts;
397
398
SBActionFactory Factory(Byproducts);
399
auto Result = T.run(&Factory);
400
if (!EmitClassNamesFile(Byproducts.ClassNames)) {
401
llvm::errs() << "Failed to create SB Class file\n";
402
return 1;
403
}
404
if (!EmitMethodNamesFile(Byproducts.MangledMethodNames)) {
405
llvm::errs() << "Failed to create Method Names file\n";
406
return 1;
407
}
408
if (!EmitSkippedMethodsFile(Byproducts.SkippedMethodNames)) {
409
llvm::errs() << "Failed to create Skipped Methods file\n";
410
return 1;
411
}
412
413
return Result;
414
}
415
416