blob: 72afed902649aebdb8bcb92bc439660cd6faa65b [file] [log] [blame]
Jonas Devlieghere9e046f02018-11-13 19:18:16 +00001//===-- CommandObjectReproducer.cpp -----------------------------*- C++ -*-===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Jonas Devlieghere9e046f02018-11-13 19:18:16 +00006//
7//===----------------------------------------------------------------------===//
8
9#include "CommandObjectReproducer.h"
10
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000011#include "lldb/Host/OptionParser.h"
Jonas Devlieghere8fc8d3f2019-09-16 23:31:06 +000012#include "lldb/Utility/GDBRemote.h"
Jonas Devliegheref4f12012019-10-17 00:02:00 +000013#include "lldb/Utility/Reproducer.h"
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000014
Jonas Devliegheredf14b942018-11-15 01:05:40 +000015#include "lldb/Interpreter/CommandInterpreter.h"
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000016#include "lldb/Interpreter/CommandReturnObject.h"
17#include "lldb/Interpreter/OptionArgParser.h"
18#include "lldb/Interpreter/OptionGroupBoolean.h"
19
20using namespace lldb;
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000021using namespace llvm;
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000022using namespace lldb_private;
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000023using namespace lldb_private::repro;
24
25enum ReproducerProvider {
26 eReproducerProviderCommands,
27 eReproducerProviderFiles,
28 eReproducerProviderGDB,
29 eReproducerProviderVersion,
Jonas Devliegheref4f12012019-10-17 00:02:00 +000030 eReproducerProviderWorkingDirectory,
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000031 eReproducerProviderNone
32};
33
34static constexpr OptionEnumValueElement g_reproducer_provider_type[] = {
35 {
36 eReproducerProviderCommands,
37 "commands",
38 "Command Interpreter Commands",
39 },
40 {
41 eReproducerProviderFiles,
42 "files",
43 "Files",
44 },
45 {
46 eReproducerProviderGDB,
47 "gdb",
48 "GDB Remote Packets",
49 },
50 {
51 eReproducerProviderVersion,
52 "version",
53 "Version",
54 },
55 {
Jonas Devliegheref4f12012019-10-17 00:02:00 +000056 eReproducerProviderWorkingDirectory,
57 "cwd",
58 "Working Directory",
59 },
60 {
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000061 eReproducerProviderNone,
62 "none",
63 "None",
64 },
65};
66
67static constexpr OptionEnumValues ReproducerProviderType() {
68 return OptionEnumValues(g_reproducer_provider_type);
69}
70
71#define LLDB_OPTIONS_reproducer
72#include "CommandOptions.inc"
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000073
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000074class CommandObjectReproducerGenerate : public CommandObjectParsed {
75public:
76 CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
Jonas Devlieghere973d66e2019-05-03 00:10:31 +000077 : CommandObjectParsed(
78 interpreter, "reproducer generate",
79 "Generate reproducer on disk. When the debugger is in capture "
80 "mode, this command will output the reproducer to a directory on "
81 "disk. In replay mode this command in a no-op.",
82 nullptr) {}
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000083
84 ~CommandObjectReproducerGenerate() override = default;
85
86protected:
87 bool DoExecute(Args &command, CommandReturnObject &result) override {
88 if (!command.empty()) {
89 result.AppendErrorWithFormat("'%s' takes no arguments",
90 m_cmd_name.c_str());
91 return false;
92 }
93
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +000094 auto &r = Reproducer::Instance();
Jonas Devlieghere9e046f02018-11-13 19:18:16 +000095 if (auto generator = r.GetGenerator()) {
96 generator->Keep();
Jonas Devlieghere865cd092019-10-09 21:47:49 +000097 } else if (r.IsReplaying()) {
Jonas Devlieghere2dca6532019-02-27 17:47:06 +000098 // Make this operation a NOP in replay mode.
99 result.SetStatus(eReturnStatusSuccessFinishNoResult);
100 return result.Succeeded();
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000101 } else {
102 result.AppendErrorWithFormat("Unable to get the reproducer generator");
Jonas Devlieghere2dca6532019-02-27 17:47:06 +0000103 result.SetStatus(eReturnStatusFailed);
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000104 return false;
105 }
106
107 result.GetOutputStream()
108 << "Reproducer written to '" << r.GetReproducerPath() << "'\n";
Jonas Devlieghere1c5250a2019-04-02 18:23:16 +0000109 result.GetOutputStream()
110 << "Please have a look at the directory to assess if you're willing to "
111 "share the contained information.\n";
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000112
113 result.SetStatus(eReturnStatusSuccessFinishResult);
114 return result.Succeeded();
115 }
116};
117
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000118class CommandObjectReproducerStatus : public CommandObjectParsed {
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000119public:
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000120 CommandObjectReproducerStatus(CommandInterpreter &interpreter)
Jonas Devlieghere973d66e2019-05-03 00:10:31 +0000121 : CommandObjectParsed(
122 interpreter, "reproducer status",
123 "Show the current reproducer status. In capture mode the debugger "
124 "is collecting all the information it needs to create a "
125 "reproducer. In replay mode the reproducer is replaying a "
126 "reproducer. When the reproducers are off, no data is collected "
127 "and no reproducer can be generated.",
128 nullptr) {}
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000129
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000130 ~CommandObjectReproducerStatus() override = default;
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000131
132protected:
133 bool DoExecute(Args &command, CommandReturnObject &result) override {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000134 if (!command.empty()) {
135 result.AppendErrorWithFormat("'%s' takes no arguments",
136 m_cmd_name.c_str());
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000137 return false;
138 }
139
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000140 auto &r = Reproducer::Instance();
Jonas Devlieghere865cd092019-10-09 21:47:49 +0000141 if (r.IsCapturing()) {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000142 result.GetOutputStream() << "Reproducer is in capture mode.\n";
Jonas Devlieghere865cd092019-10-09 21:47:49 +0000143 } else if (r.IsReplaying()) {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000144 result.GetOutputStream() << "Reproducer is in replay mode.\n";
145 } else {
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000146 result.GetOutputStream() << "Reproducer is off.\n";
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000147 }
148
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000149 result.SetStatus(eReturnStatusSuccessFinishResult);
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000150 return result.Succeeded();
151 }
152};
153
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000154static void SetError(CommandReturnObject &result, Error err) {
155 result.GetErrorStream().Printf("error: %s\n",
156 toString(std::move(err)).c_str());
157 result.SetStatus(eReturnStatusFailed);
158}
159
160class CommandObjectReproducerDump : public CommandObjectParsed {
161public:
162 CommandObjectReproducerDump(CommandInterpreter &interpreter)
163 : CommandObjectParsed(interpreter, "reproducer dump",
164 "Dump the information contained in a reproducer.",
165 nullptr) {}
166
167 ~CommandObjectReproducerDump() override = default;
168
169 Options *GetOptions() override { return &m_options; }
170
171 class CommandOptions : public Options {
172 public:
173 CommandOptions() : Options(), file() {}
174
175 ~CommandOptions() override = default;
176
177 Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
178 ExecutionContext *execution_context) override {
179 Status error;
180 const int short_option = m_getopt_table[option_idx].val;
181
182 switch (short_option) {
183 case 'f':
184 file.SetFile(option_arg, FileSpec::Style::native);
185 FileSystem::Instance().Resolve(file);
186 break;
187 case 'p':
188 provider = (ReproducerProvider)OptionArgParser::ToOptionEnum(
189 option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
190 if (!error.Success())
191 error.SetErrorStringWithFormat("unrecognized value for provider '%s'",
192 option_arg.str().c_str());
193 break;
194 default:
195 llvm_unreachable("Unimplemented option");
196 }
197
198 return error;
199 }
200
201 void OptionParsingStarting(ExecutionContext *execution_context) override {
202 file.Clear();
203 provider = eReproducerProviderNone;
204 }
205
206 ArrayRef<OptionDefinition> GetDefinitions() override {
207 return makeArrayRef(g_reproducer_options);
208 }
209
210 FileSpec file;
211 ReproducerProvider provider = eReproducerProviderNone;
212 };
213
214protected:
215 bool DoExecute(Args &command, CommandReturnObject &result) override {
216 if (!command.empty()) {
217 result.AppendErrorWithFormat("'%s' takes no arguments",
218 m_cmd_name.c_str());
219 return false;
220 }
221
222 // If no reproducer path is specified, use the loader currently used for
223 // replay. Otherwise create a new loader just for dumping.
224 llvm::Optional<Loader> loader_storage;
225 Loader *loader = nullptr;
226 if (!m_options.file) {
227 loader = Reproducer::Instance().GetLoader();
228 if (loader == nullptr) {
229 result.SetError(
230 "Not specifying a reproducer is only support during replay.");
231 result.SetStatus(eReturnStatusSuccessFinishNoResult);
232 return false;
233 }
234 } else {
235 loader_storage.emplace(m_options.file);
236 loader = &(*loader_storage);
237 if (Error err = loader->LoadIndex()) {
238 SetError(result, std::move(err));
239 return false;
240 }
241 }
242
243 // If we get here we should have a valid loader.
244 assert(loader);
245
246 switch (m_options.provider) {
247 case eReproducerProviderFiles: {
248 FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>();
249
250 // Read the VFS mapping.
251 ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
252 vfs::getRealFileSystem()->getBufferForFile(vfs_mapping.GetPath());
253 if (!buffer) {
254 SetError(result, errorCodeToError(buffer.getError()));
255 return false;
256 }
257
258 // Initialize a VFS from the given mapping.
259 IntrusiveRefCntPtr<vfs::FileSystem> vfs = vfs::getVFSFromYAML(
260 std::move(buffer.get()), nullptr, vfs_mapping.GetPath());
261
262 // Dump the VFS to a buffer.
263 std::string str;
264 raw_string_ostream os(str);
265 static_cast<vfs::RedirectingFileSystem &>(*vfs).dump(os);
266 os.flush();
267
268 // Return the string.
269 result.AppendMessage(str);
270 result.SetStatus(eReturnStatusSuccessFinishResult);
271 return true;
272 }
273 case eReproducerProviderVersion: {
Jonas Devlieghereb2575da2019-10-17 00:01:57 +0000274 Expected<std::string> version = loader->LoadBuffer<VersionProvider>();
275 if (!version) {
276 SetError(result, version.takeError());
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000277 return false;
278 }
Jonas Devlieghereb2575da2019-10-17 00:01:57 +0000279 result.AppendMessage(*version);
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000280 result.SetStatus(eReturnStatusSuccessFinishResult);
281 return true;
282 }
Jonas Devliegheref4f12012019-10-17 00:02:00 +0000283 case eReproducerProviderWorkingDirectory: {
284 Expected<std::string> cwd =
285 loader->LoadBuffer<WorkingDirectoryProvider>();
286 if (!cwd) {
287 SetError(result, cwd.takeError());
288 return false;
289 }
290 result.AppendMessage(*cwd);
291 result.SetStatus(eReturnStatusSuccessFinishResult);
292 return true;
293 }
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000294 case eReproducerProviderCommands: {
295 // Create a new command loader.
296 std::unique_ptr<repro::CommandLoader> command_loader =
297 repro::CommandLoader::Create(loader);
298 if (!command_loader) {
299 SetError(result,
300 make_error<StringError>(llvm::inconvertibleErrorCode(),
301 "Unable to create command loader."));
302 return false;
303 }
304
305 // Iterate over the command files and dump them.
306 while (true) {
307 llvm::Optional<std::string> command_file =
308 command_loader->GetNextFile();
309 if (!command_file)
310 break;
311
312 auto command_buffer = llvm::MemoryBuffer::getFile(*command_file);
313 if (auto err = command_buffer.getError()) {
314 SetError(result, errorCodeToError(err));
315 return false;
316 }
317 result.AppendMessage((*command_buffer)->getBuffer());
318 }
319
320 result.SetStatus(eReturnStatusSuccessFinishResult);
321 return true;
322 }
323 case eReproducerProviderGDB: {
Jonas Devlieghere8fc8d3f2019-09-16 23:31:06 +0000324 FileSpec gdb_file = loader->GetFile<ProcessGDBRemoteProvider::Info>();
325 auto error_or_file = MemoryBuffer::getFile(gdb_file.GetPath());
326 if (auto err = error_or_file.getError()) {
327 SetError(result, errorCodeToError(err));
328 return false;
329 }
330
331 std::vector<GDBRemotePacket> packets;
332 yaml::Input yin((*error_or_file)->getBuffer());
333 yin >> packets;
334
335 if (auto err = yin.error()) {
336 SetError(result, errorCodeToError(err));
337 return false;
338 }
339
Jonas Devliegheref4f12012019-10-17 00:02:00 +0000340 for (GDBRemotePacket &packet : packets) {
Jonas Devlieghere8fc8d3f2019-09-16 23:31:06 +0000341 packet.Dump(result.GetOutputStream());
342 }
343
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000344 result.SetStatus(eReturnStatusSuccessFinishResult);
345 return true;
346 }
347 case eReproducerProviderNone:
348 result.SetError("No valid provider specified.");
349 return false;
350 }
351
352 result.SetStatus(eReturnStatusSuccessFinishNoResult);
353 return result.Succeeded();
354 }
355
356private:
357 CommandOptions m_options;
358};
359
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000360CommandObjectReproducer::CommandObjectReproducer(
361 CommandInterpreter &interpreter)
Jonas Devlieghere973d66e2019-05-03 00:10:31 +0000362 : CommandObjectMultiword(
363 interpreter, "reproducer",
Jonas Devlieghere130ec062019-07-30 18:06:38 +0000364 "Commands for manipulate the reproducer functionality.",
365 "reproducer <subcommand> [<subcommand-options>]") {
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000366 LoadSubCommand(
367 "generate",
368 CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
Jonas Devlieghere15eacd72018-12-03 17:28:29 +0000369 LoadSubCommand("status", CommandObjectSP(
370 new CommandObjectReproducerStatus(interpreter)));
Jonas Devlieghere97fc8eb2019-09-13 23:27:31 +0000371 LoadSubCommand("dump",
372 CommandObjectSP(new CommandObjectReproducerDump(interpreter)));
Jonas Devlieghere9e046f02018-11-13 19:18:16 +0000373}
374
375CommandObjectReproducer::~CommandObjectReproducer() = default;