diff --git a/lldb/include/lldb/API/SBAttachInfo.h b/lldb/include/lldb/API/SBAttachInfo.h --- a/lldb/include/lldb/API/SBAttachInfo.h +++ b/lldb/include/lldb/API/SBAttachInfo.h @@ -164,6 +164,14 @@ /// allows a different listener to be used to listen for process events. void SetListener(SBListener &listener); + const char *GetScriptedProcessClassName() const; + + void SetScriptedProcessClassName(const char *class_name); + + lldb::SBStructuredData GetScriptedProcessDictionary() const; + + void SetScriptedProcessDictionary(lldb::SBStructuredData dict); + protected: friend class SBTarget; diff --git a/lldb/include/lldb/API/SBStructuredData.h b/lldb/include/lldb/API/SBStructuredData.h --- a/lldb/include/lldb/API/SBStructuredData.h +++ b/lldb/include/lldb/API/SBStructuredData.h @@ -92,6 +92,7 @@ size_t GetStringValue(char *dst, size_t dst_len) const; protected: + friend class SBAttachInfo; friend class SBLaunchInfo; friend class SBDebugger; friend class SBTarget; diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -193,6 +193,28 @@ lldb::ListenerSP GetListenerForProcess(Debugger &debugger); + bool IsScriptedProcess() const { + return !m_scripted_process_class_name.empty(); + } + + std::string GetScriptedProcessClassName() const { + return m_scripted_process_class_name; + } + + void SetScriptedProcessClassName(std::string name) { + m_scripted_process_class_name = name; + } + + lldb_private::StructuredData::DictionarySP + GetScriptedProcessDictionarySP() const { + return m_scripted_process_dictionary_sp; + } + + void SetScriptedProcessDictionarySP( + lldb_private::StructuredData::DictionarySP dictionary_sp) { + m_scripted_process_dictionary_sp = dictionary_sp; + } + protected: lldb::ListenerSP m_listener_sp; lldb::ListenerSP m_hijack_listener_sp; @@ -210,6 +232,11 @@ false; // Use an async attach where we start the attach and return // immediately (used by GUI programs with --waitfor so they can // call SBProcess::Stop() to cancel attach) + std::string m_scripted_process_class_name; // The name of the class that will + // manage a scripted process. + StructuredData::DictionarySP + m_scripted_process_dictionary_sp; // A dictionary that holds key/value + // pairs passed to the scripted process. }; // This class tracks the Modification state of the process. Things that can diff --git a/lldb/source/API/SBAttachInfo.cpp b/lldb/source/API/SBAttachInfo.cpp --- a/lldb/source/API/SBAttachInfo.cpp +++ b/lldb/source/API/SBAttachInfo.cpp @@ -10,6 +10,7 @@ #include "Utils.h" #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBListener.h" +#include "lldb/API/SBStructuredData.h" #include "lldb/Target/Process.h" #include "lldb/Utility/Instrumentation.h" @@ -251,3 +252,48 @@ m_opaque_sp->SetListener(listener.GetSP()); } + +const char *SBAttachInfo::GetScriptedProcessClassName() const { + LLDB_INSTRUMENT_VA(this); + + // Constify this string so that it is saved in the string pool. Otherwise it + // would be freed when this function goes out of scope. + ConstString class_name(m_opaque_sp->GetScriptedProcessClassName().c_str()); + return class_name.AsCString(); +} + +void SBAttachInfo::SetScriptedProcessClassName(const char *class_name) { + LLDB_INSTRUMENT_VA(this, class_name); + + m_opaque_sp->SetScriptedProcessClassName(class_name); +} + +lldb::SBStructuredData SBAttachInfo::GetScriptedProcessDictionary() const { + LLDB_INSTRUMENT_VA(this); + + lldb_private::StructuredData::DictionarySP dict_sp = + m_opaque_sp->GetScriptedProcessDictionarySP(); + + SBStructuredData data; + data.m_impl_up->SetObjectSP(dict_sp); + + return data; +} + +void SBAttachInfo::SetScriptedProcessDictionary(lldb::SBStructuredData dict) { + LLDB_INSTRUMENT_VA(this, dict); + if (!dict.IsValid() || !dict.m_impl_up) + return; + + StructuredData::ObjectSP obj_sp = dict.m_impl_up->GetObjectSP(); + + if (!obj_sp) + return; + + StructuredData::DictionarySP dict_sp = + std::make_shared(obj_sp); + if (!dict_sp || dict_sp->GetType() == lldb::eStructuredDataTypeInvalid) + return; + + m_opaque_sp->SetScriptedProcessDictionarySP(dict_sp); +} diff --git a/lldb/source/Commands/CMakeLists.txt b/lldb/source/Commands/CMakeLists.txt --- a/lldb/source/Commands/CMakeLists.txt +++ b/lldb/source/Commands/CMakeLists.txt @@ -40,6 +40,7 @@ CommandObjectWatchpoint.cpp CommandObjectWatchpointCommand.cpp CommandOptionArgumentTable.cpp + CommandOptionsProcessAttach.cpp CommandOptionsProcessLaunch.cpp LINK_LIBS diff --git a/lldb/source/Commands/CommandObjectPlatform.cpp b/lldb/source/Commands/CommandObjectPlatform.cpp --- a/lldb/source/Commands/CommandObjectPlatform.cpp +++ b/lldb/source/Commands/CommandObjectPlatform.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "CommandObjectPlatform.h" +#include "CommandOptionsProcessAttach.h" #include "CommandOptionsProcessLaunch.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" @@ -18,6 +19,8 @@ #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionGroupFile.h" #include "lldb/Interpreter/OptionGroupPlatform.h" +#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h" +#include "lldb/Interpreter/ScriptedMetadata.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" @@ -1144,8 +1147,11 @@ : CommandObjectParsed(interpreter, "platform process launch", "Launch a new process on a remote platform.", "platform process launch program", - eCommandRequiresTarget | eCommandTryTargetAPILock) { + eCommandRequiresTarget | eCommandTryTargetAPILock), + m_class_options("scripted process", true, 'C', 'k', 'v', 0) { m_all_options.Append(&m_options); + m_all_options.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2, + LLDB_OPT_SET_ALL); m_all_options.Finalize(); CommandArgumentData run_arg_arg{eArgTypeRunArgs, eArgRepeatStar}; m_arguments.push_back({run_arg_arg}); @@ -1180,6 +1186,15 @@ m_options.launch_info.GetArchitecture() = exe_module->GetArchitecture(); } + if (!m_class_options.GetName().empty()) { + m_options.launch_info.SetProcessPluginName("ScriptedProcess"); + m_options.launch_info.SetScriptedProcessClassName( + m_class_options.GetName()); + m_options.launch_info.SetScriptedProcessDictionarySP( + m_class_options.GetStructuredData()); + target->SetProcessLaunchInfo(m_options.launch_info); + } + if (argc > 0) { if (m_options.launch_info.GetExecutableFile()) { // We already have an executable file, so we will use this and all @@ -1223,6 +1238,7 @@ } CommandOptionsProcessLaunch m_options; + OptionGroupPythonClassWithDict m_class_options; OptionGroupOptions m_all_options; }; @@ -1572,71 +1588,16 @@ class CommandObjectPlatformProcessAttach : public CommandObjectParsed { public: - class CommandOptions : public Options { - public: - CommandOptions() { - // Keep default values of all options in one place: OptionParsingStarting - // () - OptionParsingStarting(nullptr); - } - - ~CommandOptions() override = default; - - Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, - ExecutionContext *execution_context) override { - Status error; - char short_option = (char)m_getopt_table[option_idx].val; - switch (short_option) { - case 'p': { - lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; - if (option_arg.getAsInteger(0, pid)) { - error.SetErrorStringWithFormat("invalid process ID '%s'", - option_arg.str().c_str()); - } else { - attach_info.SetProcessID(pid); - } - } break; - - case 'P': - attach_info.SetProcessPluginName(option_arg); - break; - - case 'n': - attach_info.GetExecutableFile().SetFile(option_arg, - FileSpec::Style::native); - break; - - case 'w': - attach_info.SetWaitForLaunch(true); - break; - - default: - llvm_unreachable("Unimplemented option"); - } - return error; - } - - void OptionParsingStarting(ExecutionContext *execution_context) override { - attach_info.Clear(); - } - - llvm::ArrayRef GetDefinitions() override { - return llvm::ArrayRef(g_platform_process_attach_options); - } - - // Options table: Required for subclasses of Options. - - static OptionDefinition g_option_table[]; - - // Instance variables to hold the values for command options. - - ProcessAttachInfo attach_info; - }; - CommandObjectPlatformProcessAttach(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "platform process attach", "Attach to a process.", - "platform process attach ") {} + "platform process attach "), + m_class_options("scripted process", true, 'C', 'k', 'v', 0) { + m_all_options.Append(&m_options); + m_all_options.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2, + LLDB_OPT_SET_ALL); + m_all_options.Finalize(); + } ~CommandObjectPlatformProcessAttach() override = default; @@ -1644,6 +1605,15 @@ PlatformSP platform_sp( GetDebugger().GetPlatformList().GetSelectedPlatform()); if (platform_sp) { + + if (!m_class_options.GetName().empty()) { + m_options.attach_info.SetProcessPluginName("ScriptedProcess"); + m_options.attach_info.SetScriptedProcessClassName( + m_class_options.GetName()); + m_options.attach_info.SetScriptedProcessDictionarySP( + m_class_options.GetStructuredData()); + } + Status err; ProcessSP remote_process_sp = platform_sp->Attach( m_options.attach_info, GetDebugger(), nullptr, err); @@ -1659,10 +1629,12 @@ return result.Succeeded(); } - Options *GetOptions() override { return &m_options; } + Options *GetOptions() override { return &m_all_options; } protected: - CommandOptions m_options; + CommandOptionsProcessAttach m_options; + OptionGroupPythonClassWithDict m_class_options; + OptionGroupOptions m_all_options; }; class CommandObjectPlatformProcess : public CommandObjectMultiword { diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -9,6 +9,7 @@ #include "CommandObjectProcess.h" #include "CommandObjectBreakpoint.h" #include "CommandObjectTrace.h" +#include "CommandOptionsProcessAttach.h" #include "CommandOptionsProcessLaunch.h" #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointIDList.h" @@ -24,6 +25,7 @@ #include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Interpreter/OptionGroupPythonClassWithDict.h" #include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/ScriptedMetadata.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/StopInfo.h" @@ -304,77 +306,20 @@ #pragma mark CommandObjectProcessAttach class CommandObjectProcessAttach : public CommandObjectProcessLaunchOrAttach { public: - class CommandOptions : public Options { - public: - CommandOptions() { - // Keep default values of all options in one place: OptionParsingStarting - // () - OptionParsingStarting(nullptr); - } - - ~CommandOptions() override = default; - - Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, - ExecutionContext *execution_context) override { - Status error; - const int short_option = m_getopt_table[option_idx].val; - switch (short_option) { - case 'c': - attach_info.SetContinueOnceAttached(true); - break; - - case 'p': { - lldb::pid_t pid; - if (option_arg.getAsInteger(0, pid)) { - error.SetErrorStringWithFormat("invalid process ID '%s'", - option_arg.str().c_str()); - } else { - attach_info.SetProcessID(pid); - } - } break; - - case 'P': - attach_info.SetProcessPluginName(option_arg); - break; - - case 'n': - attach_info.GetExecutableFile().SetFile(option_arg, - FileSpec::Style::native); - break; - - case 'w': - attach_info.SetWaitForLaunch(true); - break; - - case 'i': - attach_info.SetIgnoreExisting(false); - break; - - default: - llvm_unreachable("Unimplemented option"); - } - return error; - } - - void OptionParsingStarting(ExecutionContext *execution_context) override { - attach_info.Clear(); - } - - llvm::ArrayRef GetDefinitions() override { - return llvm::ArrayRef(g_process_attach_options); - } - - ProcessAttachInfo attach_info; - }; - CommandObjectProcessAttach(CommandInterpreter &interpreter) : CommandObjectProcessLaunchOrAttach( interpreter, "process attach", "Attach to a process.", - "process attach ", 0, "attach") {} + "process attach ", 0, "attach"), + m_class_options("scripted process", true, 'C', 'k', 'v', 0) { + m_all_options.Append(&m_options); + m_all_options.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2, + LLDB_OPT_SET_ALL); + m_all_options.Finalize(); + } ~CommandObjectProcessAttach() override = default; - Options *GetOptions() override { return &m_options; } + Options *GetOptions() override { return &m_all_options; } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { @@ -409,6 +354,14 @@ } } + if (!m_class_options.GetName().empty()) { + m_options.attach_info.SetProcessPluginName("ScriptedProcess"); + m_options.attach_info.SetScriptedProcessClassName( + m_class_options.GetName()); + m_options.attach_info.SetScriptedProcessDictionarySP( + m_class_options.GetStructuredData()); + } + // Record the old executable module, we want to issue a warning if the // process of attaching changed the current executable (like somebody said // "file foo" then attached to a PID whose executable was bar.) @@ -483,7 +436,9 @@ return result.Succeeded(); } - CommandOptions m_options; + CommandOptionsProcessAttach m_options; + OptionGroupPythonClassWithDict m_class_options; + OptionGroupOptions m_all_options; }; // CommandObjectProcessContinue diff --git a/lldb/source/Commands/CommandOptionsProcessAttach.h b/lldb/source/Commands/CommandOptionsProcessAttach.h new file mode 100644 --- /dev/null +++ b/lldb/source/Commands/CommandOptionsProcessAttach.h @@ -0,0 +1,47 @@ +//===-- CommandOptionsProcessAttach.h ---------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_COMMANDS_COMMANDOPTIONSPROCESSATTACH_H +#define LLDB_SOURCE_COMMANDS_COMMANDOPTIONSPROCESSATTACH_H + +#include "lldb/Interpreter/Options.h" +#include "lldb/Target/Process.h" + +namespace lldb_private { + +// CommandOptionsProcessAttach + +class CommandOptionsProcessAttach : public lldb_private::OptionGroup { +public: + CommandOptionsProcessAttach() { + // Keep default values of all options in one place: OptionParsingStarting + // () + OptionParsingStarting(nullptr); + } + + ~CommandOptionsProcessAttach() override = default; + + lldb_private::Status + SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + lldb_private::ExecutionContext *execution_context) override; + + void OptionParsingStarting( + lldb_private::ExecutionContext *execution_context) override { + attach_info.Clear(); + } + + llvm::ArrayRef GetDefinitions() override; + + // Instance variables to hold the values for command options. + + lldb_private::ProcessAttachInfo attach_info; +}; // CommandOptionsProcessAttach + +} // namespace lldb_private + +#endif // LLDB_SOURCE_COMMANDS_COMMANDOPTIONSPROCESSATTACH_H diff --git a/lldb/source/Commands/CommandOptionsProcessAttach.cpp b/lldb/source/Commands/CommandOptionsProcessAttach.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Commands/CommandOptionsProcessAttach.cpp @@ -0,0 +1,76 @@ +//===-- CommandOptionsProcessAttach.cpp -----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CommandOptionsProcessAttach.h" + +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandOptionArgumentTable.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Target.h" + +#include "llvm/ADT/ArrayRef.h" + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; + +#define LLDB_OPTIONS_process_attach +#include "CommandOptions.inc" + +Status CommandOptionsProcessAttach::SetOptionValue( + uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error; + const int short_option = g_process_attach_options[option_idx].short_option; + switch (short_option) { + case 'c': + attach_info.SetContinueOnceAttached(true); + break; + + case 'p': { + lldb::pid_t pid; + if (option_arg.getAsInteger(0, pid)) { + error.SetErrorStringWithFormat("invalid process ID '%s'", + option_arg.str().c_str()); + } else { + attach_info.SetProcessID(pid); + } + } break; + + case 'P': + attach_info.SetProcessPluginName(option_arg); + break; + + case 'n': + attach_info.GetExecutableFile().SetFile(option_arg, + FileSpec::Style::native); + break; + + case 'w': + attach_info.SetWaitForLaunch(true); + break; + + case 'i': + attach_info.SetIgnoreExisting(false); + break; + + default: + llvm_unreachable("Unimplemented option"); + } + return error; +} + +llvm::ArrayRef CommandOptionsProcessAttach::GetDefinitions() { + return llvm::makeArrayRef(g_process_attach_options); +}