[lldb] Add scripted process launch/attach option to {,platform }process commands

This patch does several things:

First, it refactors the `CommandObject{,Platform}ProcessObject` command
option class into a separate `CommandOptionsProcessAttach` option group.

This will make sure both the `platform process attach` and `process attach`
command options will always stay in sync without having with duplicate
them each time. But more importantly, making this class an `OptionGroup`
allows us to combine with a `OptionGroupPythonClassWithDict` to add
support for the scripted process managing class name and user-provided
dictionary options.

This patch also improves feature parity between `ProcessLaunchInfo` and
`ProcessAttachInfo` with regard to ScriptedProcesses, by exposing the
various getters and setters necessary to use them through the SBAPI.

This is foundation work for adding support to "attach" to a process from
the scripted platform.

Differential Revision: https://reviews.llvm.org/D139945

Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
This commit is contained in:
Med Ismail Bennani 2023-03-03 15:27:07 -08:00
parent 2d5348be25
commit 3014a1c5a1
9 changed files with 264 additions and 131 deletions

View File

@ -164,6 +164,14 @@ public:
/// 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;

View File

@ -92,6 +92,7 @@ public:
size_t GetStringValue(char *dst, size_t dst_len) const;
protected:
friend class SBAttachInfo;
friend class SBLaunchInfo;
friend class SBDebugger;
friend class SBTarget;

View File

@ -193,6 +193,28 @@ public:
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 @@ protected:
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

View File

@ -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 @@ void SBAttachInfo::SetListener(SBListener &listener) {
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<StructuredData::Dictionary>(obj_sp);
if (!dict_sp || dict_sp->GetType() == lldb::eStructuredDataTypeInvalid)
return;
m_opaque_sp->SetScriptedProcessDictionarySP(dict_sp);
}

View File

@ -40,6 +40,7 @@ add_lldb_library(lldbCommands
CommandObjectWatchpoint.cpp
CommandObjectWatchpointCommand.cpp
CommandOptionArgumentTable.cpp
CommandOptionsProcessAttach.cpp
CommandOptionsProcessLaunch.cpp
LINK_LIBS

View File

@ -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 @@ public:
: 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 @@ protected:
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 @@ protected:
}
CommandOptionsProcessLaunch m_options;
OptionGroupPythonClassWithDict m_class_options;
OptionGroupOptions m_all_options;
};
@ -1572,71 +1588,16 @@ protected:
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<OptionDefinition> 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 <cmd-options>") {}
"platform process attach <cmd-options>"),
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 @@ public:
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 @@ public:
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 {

View File

@ -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 @@ protected:
#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<OptionDefinition> 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 <cmd-options>", 0, "attach") {}
"process attach <cmd-options>", 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 @@ protected:
}
}
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 @@ protected:
return result.Succeeded();
}
CommandOptions m_options;
CommandOptionsProcessAttach m_options;
OptionGroupPythonClassWithDict m_class_options;
OptionGroupOptions m_all_options;
};
// CommandObjectProcessContinue

View File

@ -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<OptionDefinition> CommandOptionsProcessAttach::GetDefinitions() {
return llvm::makeArrayRef(g_process_attach_options);
}

View File

@ -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<lldb_private::OptionDefinition> 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