20 changes: 12 additions & 8 deletions lldb/include/lldb/Interpreter/ScriptedProcessInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ class ScriptedProcessInterface : virtual public ScriptedInterface {
return {};
}

virtual StructuredData::DictionarySP GetCapabilities() { return {}; }

virtual Status Attach(const ProcessAttachInfo &attach_info) {
return Status("ScriptedProcess did not attach");
}

virtual Status Launch() { return Status("ScriptedProcess did not launch"); }

virtual Status Resume() { return Status("ScriptedProcess did not resume"); }
Expand All @@ -44,19 +50,17 @@ class ScriptedProcessInterface : virtual public ScriptedInterface {

virtual StructuredData::DictionarySP GetThreadsInfo() { return {}; }

virtual StructuredData::DictionarySP GetThreadWithID(lldb::tid_t tid) {
return {};
}

virtual StructuredData::DictionarySP GetRegistersForThread(lldb::tid_t tid) {
return {};
}

virtual lldb::DataExtractorSP
ReadMemoryAtAddress(lldb::addr_t address, size_t size, Status &error) {
return {};
}

virtual size_t WriteMemoryAtAddress(lldb::addr_t addr,
lldb::DataExtractorSP data_sp,
Status &error) {
return LLDB_INVALID_OFFSET;
};

virtual StructuredData::ArraySP GetLoadedImages() { return {}; }

virtual lldb::pid_t GetProcessID() { return LLDB_INVALID_PROCESS_ID; }
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/Target/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -1430,6 +1430,8 @@ class Target : public std::enable_shared_from_this<Target>,
return *m_frame_recognizer_manager_up;
}

void SaveScriptedLaunchInfo(lldb_private::ProcessInfo &process_info);

/// Add a signal for the target. This will get copied over to the process
/// if the signal exists on that target. Only the values with Yes and No are
/// set, Calculate values will be ignored.
Expand Down
12 changes: 12 additions & 0 deletions lldb/include/lldb/Utility/ProcessInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "lldb/Utility/Environment.h"
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/NameMatches.h"
#include "lldb/Utility/StructuredData.h"
#include <vector>

namespace lldb_private {
Expand Down Expand Up @@ -86,6 +87,16 @@ class ProcessInfo {
Environment &GetEnvironment() { return m_environment; }
const Environment &GetEnvironment() const { return m_environment; }

bool IsScriptedProcess() const;

lldb::ScriptedMetadataSP GetScriptedMetadata() const {
return m_scripted_metadata_sp;
}

void SetScriptedMetadata(lldb::ScriptedMetadataSP metadata_sp) {
m_scripted_metadata_sp = metadata_sp;
}

protected:
FileSpec m_executable;
std::string m_arg0; // argv[0] if supported. If empty, then use m_executable.
Expand All @@ -97,6 +108,7 @@ class ProcessInfo {
uint32_t m_gid = UINT32_MAX;
ArchSpec m_arch;
lldb::pid_t m_pid = LLDB_INVALID_PROCESS_ID;
lldb::ScriptedMetadataSP m_scripted_metadata_sp = nullptr;
};

// ProcessInstanceInfo
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/lldb-forward.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ class RichManglingContext;
class Scalar;
class ScriptInterpreter;
class ScriptInterpreterLocker;
class ScriptedMetadata;
class ScriptedPlatformInterface;
class ScriptedProcessInterface;
class ScriptedThreadInterface;
Expand Down Expand Up @@ -380,6 +381,7 @@ typedef std::shared_ptr<lldb_private::RecognizedStackFrame>
typedef std::shared_ptr<lldb_private::ScriptSummaryFormat>
ScriptSummaryFormatSP;
typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP;
typedef std::shared_ptr<lldb_private::ScriptedMetadata> ScriptedMetadataSP;
typedef std::unique_ptr<lldb_private::ScriptedPlatformInterface>
ScriptedPlatformInterfaceUP;
typedef std::unique_ptr<lldb_private::ScriptedProcessInterface>
Expand Down
72 changes: 72 additions & 0 deletions lldb/source/API/SBAttachInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include "Utils.h"
#include "lldb/API/SBFileSpec.h"
#include "lldb/API/SBListener.h"
#include "lldb/API/SBStructuredData.h"
#include "lldb/Interpreter/ScriptedMetadata.h"
#include "lldb/Target/Process.h"
#include "lldb/Utility/Instrumentation.h"

Expand Down Expand Up @@ -251,3 +253,73 @@ void SBAttachInfo::SetListener(SBListener &listener) {

m_opaque_sp->SetListener(listener.GetSP());
}

const char *SBAttachInfo::GetScriptedProcessClassName() const {
LLDB_INSTRUMENT_VA(this);

ScriptedMetadataSP metadata_sp = m_opaque_sp->GetScriptedMetadata();

if (!metadata_sp || !*metadata_sp)
return nullptr;

// 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(metadata_sp->GetClassName().data());
return class_name.AsCString();
}

void SBAttachInfo::SetScriptedProcessClassName(const char *class_name) {
LLDB_INSTRUMENT_VA(this, class_name);

ScriptedMetadataSP metadata_sp = m_opaque_sp->GetScriptedMetadata();

if (!metadata_sp)
metadata_sp = std::make_shared<ScriptedMetadata>(class_name, nullptr);
else
metadata_sp = std::make_shared<ScriptedMetadata>(class_name,
metadata_sp->GetArgsSP());

m_opaque_sp->SetScriptedMetadata(metadata_sp);
}

lldb::SBStructuredData SBAttachInfo::GetScriptedProcessDictionary() const {
LLDB_INSTRUMENT_VA(this);

ScriptedMetadataSP metadata_sp = m_opaque_sp->GetScriptedMetadata();

SBStructuredData data;
if (!metadata_sp)
return data;

lldb_private::StructuredData::DictionarySP dict_sp = metadata_sp->GetArgsSP();
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;

ScriptedMetadataSP metadata_sp = m_opaque_sp->GetScriptedMetadata();

if (!metadata_sp)
metadata_sp = std::make_shared<ScriptedMetadata>("", dict_sp);
else
metadata_sp = std::make_shared<ScriptedMetadata>(
metadata_sp->GetClassName(), dict_sp);

m_opaque_sp->SetScriptedMetadata(metadata_sp);
}
27 changes: 21 additions & 6 deletions lldb/source/API/SBLaunchInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "lldb/API/SBStructuredData.h"
#include "lldb/Core/StructuredDataImpl.h"
#include "lldb/Host/ProcessLaunchInfo.h"
#include "lldb/Interpreter/ScriptedMetadata.h"

using namespace lldb;
using namespace lldb_private;
Expand Down Expand Up @@ -331,25 +332,36 @@ bool SBLaunchInfo::GetDetachOnError() const {
const char *SBLaunchInfo::GetScriptedProcessClassName() const {
LLDB_INSTRUMENT_VA(this);

ScriptedMetadataSP metadata_sp = m_opaque_sp->GetScriptedMetadata();

if (!metadata_sp || !*metadata_sp)
return nullptr;

// 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());
ConstString class_name(metadata_sp->GetClassName().data());
return class_name.AsCString();
}

void SBLaunchInfo::SetScriptedProcessClassName(const char *class_name) {
LLDB_INSTRUMENT_VA(this, class_name);

m_opaque_sp->SetScriptedProcessClassName(class_name);
ScriptedMetadataSP metadata_sp = m_opaque_sp->GetScriptedMetadata();
StructuredData::DictionarySP dict_sp =
metadata_sp ? metadata_sp->GetArgsSP() : nullptr;
metadata_sp = std::make_shared<ScriptedMetadata>(class_name, dict_sp);
m_opaque_sp->SetScriptedMetadata(metadata_sp);
}

lldb::SBStructuredData SBLaunchInfo::GetScriptedProcessDictionary() const {
LLDB_INSTRUMENT_VA(this);

lldb_private::StructuredData::DictionarySP dict_sp =
m_opaque_sp->GetScriptedProcessDictionarySP();
ScriptedMetadataSP metadata_sp = m_opaque_sp->GetScriptedMetadata();

SBStructuredData data;
if (!metadata_sp)
return data;

lldb_private::StructuredData::DictionarySP dict_sp = metadata_sp->GetArgsSP();
data.m_impl_up->SetObjectSP(dict_sp);

return data;
Expand All @@ -370,5 +382,8 @@ void SBLaunchInfo::SetScriptedProcessDictionary(lldb::SBStructuredData dict) {
if (!dict_sp || dict_sp->GetType() == lldb::eStructuredDataTypeInvalid)
return;

m_opaque_sp->SetScriptedProcessDictionarySP(dict_sp);
ScriptedMetadataSP metadata_sp = m_opaque_sp->GetScriptedMetadata();
llvm::StringRef class_name = metadata_sp ? metadata_sp->GetClassName() : "";
metadata_sp = std::make_shared<ScriptedMetadata>(class_name, dict_sp);
m_opaque_sp->SetScriptedMetadata(metadata_sp);
}
3 changes: 2 additions & 1 deletion lldb/source/API/SBTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,8 @@ lldb::SBProcess SBTarget::Attach(SBAttachInfo &sb_attach_info, SBError &error) {

if (target_sp) {
ProcessAttachInfo &attach_info = sb_attach_info.ref();
if (attach_info.ProcessIDIsValid() && !attach_info.UserIDIsValid()) {
if (attach_info.ProcessIDIsValid() && !attach_info.UserIDIsValid() &&
!attach_info.IsScriptedProcess()) {
PlatformSP platform_sp = target_sp->GetPlatform();
// See if we can pre-verify if a process exists or not
if (platform_sp && platform_sp->IsConnected()) {
Expand Down
1 change: 1 addition & 0 deletions lldb/source/Commands/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ add_lldb_library(lldbCommands
CommandObjectWatchpoint.cpp
CommandObjectWatchpointCommand.cpp
CommandOptionArgumentTable.cpp
CommandOptionsProcessAttach.cpp
CommandOptionsProcessLaunch.cpp

LINK_LIBS
Expand Down
100 changes: 35 additions & 65 deletions lldb/source/Commands/CommandObjectPlatform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "CommandObjectPlatform.h"
#include "CommandOptionsProcessAttach.h"
#include "CommandOptionsProcessLaunch.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
Expand All @@ -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"
Expand Down Expand Up @@ -1144,8 +1147,11 @@ class CommandObjectPlatformProcessLaunch : public CommandObjectParsed {
: 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});
Expand Down Expand Up @@ -1180,6 +1186,14 @@ class CommandObjectPlatformProcessLaunch : public CommandObjectParsed {
m_options.launch_info.GetArchitecture() = exe_module->GetArchitecture();
}

if (!m_class_options.GetName().empty()) {
m_options.launch_info.SetProcessPluginName("ScriptedProcess");
ScriptedMetadataSP metadata_sp = std::make_shared<ScriptedMetadata>(
m_class_options.GetName(), m_class_options.GetStructuredData());
m_options.launch_info.SetScriptedMetadata(metadata_sp);
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
Expand Down Expand Up @@ -1223,6 +1237,7 @@ class CommandObjectPlatformProcessLaunch : public CommandObjectParsed {
}

CommandOptionsProcessLaunch m_options;
OptionGroupPythonClassWithDict m_class_options;
OptionGroupOptions m_all_options;
};

Expand Down Expand Up @@ -1572,78 +1587,31 @@ class CommandObjectPlatformProcessInfo : public CommandObjectParsed {

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;

bool DoExecute(Args &command, CommandReturnObject &result) override {
PlatformSP platform_sp(
GetDebugger().GetPlatformList().GetSelectedPlatform());
if (platform_sp) {

if (!m_class_options.GetName().empty()) {
m_options.attach_info.SetProcessPluginName("ScriptedProcess");
ScriptedMetadataSP metadata_sp = std::make_shared<ScriptedMetadata>(
m_class_options.GetName(), m_class_options.GetStructuredData());
m_options.attach_info.SetScriptedMetadata(metadata_sp);
}

Status err;
ProcessSP remote_process_sp = platform_sp->Attach(
m_options.attach_info, GetDebugger(), nullptr, err);
Expand All @@ -1659,10 +1627,12 @@ class CommandObjectPlatformProcessAttach : public CommandObjectParsed {
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 {
Expand Down
93 changes: 23 additions & 70 deletions lldb/source/Commands/CommandObjectProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -199,10 +201,9 @@ class CommandObjectProcessLaunch : public CommandObjectProcessLaunchOrAttach {

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());
ScriptedMetadataSP metadata_sp = std::make_shared<ScriptedMetadata>(
m_class_options.GetName(), m_class_options.GetStructuredData());
m_options.launch_info.SetScriptedMetadata(metadata_sp);
target->SetProcessLaunchInfo(m_options.launch_info);
}

Expand Down Expand Up @@ -304,77 +305,20 @@ class CommandObjectProcessLaunch : public CommandObjectProcessLaunchOrAttach {
#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 {
Expand Down Expand Up @@ -409,6 +353,13 @@ class CommandObjectProcessAttach : public CommandObjectProcessLaunchOrAttach {
}
}

if (!m_class_options.GetName().empty()) {
m_options.attach_info.SetProcessPluginName("ScriptedProcess");
ScriptedMetadataSP metadata_sp = std::make_shared<ScriptedMetadata>(
m_class_options.GetName(), m_class_options.GetStructuredData());
m_options.attach_info.SetScriptedMetadata(metadata_sp);
}

// 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.)
Expand Down Expand Up @@ -483,7 +434,9 @@ class CommandObjectProcessAttach : public CommandObjectProcessLaunchOrAttach {
return result.Succeeded();
}

CommandOptions m_options;
CommandOptionsProcessAttach m_options;
OptionGroupPythonClassWithDict m_class_options;
OptionGroupOptions m_all_options;
};

// CommandObjectProcessContinue
Expand Down
76 changes: 76 additions & 0 deletions lldb/source/Commands/CommandOptionsProcessAttach.cpp
Original file line number Diff line number Diff line change
@@ -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);
}
47 changes: 47 additions & 0 deletions lldb/source/Commands/CommandOptionsProcessAttach.h
Original file line number Diff line number Diff line change
@@ -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
8 changes: 2 additions & 6 deletions lldb/source/Host/common/ProcessLaunchInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,15 @@ using namespace lldb_private;
ProcessLaunchInfo::ProcessLaunchInfo()
: ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(0),
m_file_actions(), m_pty(new PseudoTerminal), m_monitor_callback(nullptr),
m_listener_sp(), m_hijack_listener_sp(), m_scripted_process_class_name(),
m_scripted_process_dictionary_sp() {}
m_listener_sp(), m_hijack_listener_sp() {}

ProcessLaunchInfo::ProcessLaunchInfo(const FileSpec &stdin_file_spec,
const FileSpec &stdout_file_spec,
const FileSpec &stderr_file_spec,
const FileSpec &working_directory,
uint32_t launch_flags)
: ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(launch_flags),
m_file_actions(), m_pty(new PseudoTerminal),
m_scripted_process_class_name(), m_scripted_process_dictionary_sp() {
m_file_actions(), m_pty(new PseudoTerminal) {
if (stdin_file_spec) {
FileAction file_action;
const bool read = true;
Expand Down Expand Up @@ -171,8 +169,6 @@ void ProcessLaunchInfo::Clear() {
m_resume_count = 0;
m_listener_sp.reset();
m_hijack_listener_sp.reset();
m_scripted_process_class_name.clear();
m_scripted_process_dictionary_sp.reset();
}

void ProcessLaunchInfo::NoOpMonitorCallback(lldb::pid_t pid, int signal,
Expand Down
11 changes: 11 additions & 0 deletions lldb/source/Interpreter/ScriptInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ ScriptInterpreter::GetDataExtractorFromSBData(const lldb::SBData &data) const {
return data.m_opaque_sp;
}

lldb::ProcessAttachInfoSP ScriptInterpreter::GetOpaqueTypeFromSBAttachInfo(
const lldb::SBAttachInfo &attach_info) const {
return attach_info.m_opaque_sp;
}

lldb::ProcessLaunchInfoSP ScriptInterpreter::GetOpaqueTypeFromSBLaunchInfo(
const lldb::SBLaunchInfo &launch_info) const {
return std::make_shared<ProcessLaunchInfo>(
*reinterpret_cast<ProcessLaunchInfo *>(launch_info.m_opaque_sp.get()));
}

Status
ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const {
if (error.m_opaque_up)
Expand Down
54 changes: 53 additions & 1 deletion lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,34 @@ Status ScriptedProcess::DoResume() {
return error;
}

Status ScriptedProcess::DoAttach(const ProcessAttachInfo &attach_info) {
Status error = GetInterface().Attach(attach_info);
SetPrivateState(eStateRunning);
SetPrivateState(eStateStopped);
if (error.Fail())
return error;
// NOTE: We need to set the PID before finishing to attach otherwise we will
// hit an assert when calling the attach completion handler.
DidLaunch();

return {};
}

Status
ScriptedProcess::DoAttachToProcessWithID(lldb::pid_t pid,
const ProcessAttachInfo &attach_info) {
return DoAttach(attach_info);
}

Status ScriptedProcess::DoAttachToProcessWithName(
const char *process_name, const ProcessAttachInfo &attach_info) {
return DoAttach(attach_info);
}

void ScriptedProcess::DidAttach(ArchSpec &process_arch) {
process_arch = GetArchitecture();
}

Status ScriptedProcess::DoStop() {
Log *log = GetLog(LLDBLog::Process);

Expand Down Expand Up @@ -227,7 +255,31 @@ size_t ScriptedProcess::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
return ScriptedInterface::ErrorWithMessage<size_t>(
LLVM_PRETTY_FUNCTION, "Failed to copy read memory to buffer.", error);

return size;
// FIXME: We should use the diagnostic system to report a warning if the
// `bytes_copied` is different from `size`.

return bytes_copied;
}

size_t ScriptedProcess::DoWriteMemory(lldb::addr_t vm_addr, const void *buf,
size_t size, Status &error) {
lldb::DataExtractorSP data_extractor_sp = std::make_shared<DataExtractor>(
buf, size, GetByteOrder(), GetAddressByteSize());

if (!data_extractor_sp || !data_extractor_sp->GetByteSize())
return 0;

size_t bytes_written =
GetInterface().WriteMemoryAtAddress(vm_addr, data_extractor_sp, error);

if (!bytes_written || bytes_written == LLDB_INVALID_OFFSET)
return ScriptedInterface::ErrorWithMessage<size_t>(
LLVM_PRETTY_FUNCTION, "Failed to copy write buffer to memory.", error);

// FIXME: We should use the diagnostic system to report a warning if the
// `bytes_written` is different from `size`.

return bytes_written;
}

ArchSpec ScriptedProcess::GetArchitecture() {
Expand Down
14 changes: 14 additions & 0 deletions lldb/source/Plugins/Process/scripted/ScriptedProcess.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ class ScriptedProcess : public Process {

Status DoResume() override;

Status DoAttachToProcessWithID(lldb::pid_t pid,
const ProcessAttachInfo &attach_info) override;

Status
DoAttachToProcessWithName(const char *process_name,
const ProcessAttachInfo &attach_info) override;

void DidAttach(ArchSpec &process_arch) override;

Status DoDestroy() override;

void RefreshStateAfterStop() override;
Expand All @@ -60,6 +69,9 @@ class ScriptedProcess : public Process {
size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
Status &error) override;

size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size,
Status &error) override;

ArchSpec GetArchitecture();

Status
Expand Down Expand Up @@ -90,6 +102,8 @@ class ScriptedProcess : public Process {
Status DoGetMemoryRegionInfo(lldb::addr_t load_addr,
MemoryRegionInfo &range_info) override;

Status DoAttach(const ProcessAttachInfo &attach_info);

private:
friend class ScriptedThread;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ PythonObject ToSWIGWrapper(lldb::ExecutionContextRefSP ctx_sp);
PythonObject ToSWIGWrapper(const TypeSummaryOptions &summary_options);
PythonObject ToSWIGWrapper(const SymbolContext &sym_ctx);

PythonObject ToSWIGWrapper(lldb::ProcessAttachInfoSP attach_info_sp);
PythonObject ToSWIGWrapper(lldb::ProcessLaunchInfoSP launch_info_sp);
PythonObject ToSWIGWrapper(lldb::DataExtractorSP data_extractor_sp);

PythonObject ToSWIGWrapper(std::unique_ptr<lldb::SBValue> value_sb);
PythonObject ToSWIGWrapper(std::unique_ptr<lldb::SBStream> stream_sb);
PythonObject ToSWIGWrapper(std::unique_ptr<lldb::SBStructuredData> data_sb);
Expand All @@ -88,6 +92,8 @@ python::ScopedPythonObject<lldb::SBEvent> ToSWIGWrapper(Event *event);
} // namespace python

void *LLDBSWIGPython_CastPyObjectToSBData(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBLaunchInfo(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBError(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBValue(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyObject *data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "lldb/Host/Config.h"
#include "lldb/Target/Process.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/Status.h"
#include "lldb/lldb-enumerations.h"
Expand Down Expand Up @@ -56,6 +57,24 @@ StructuredData::GenericSP ScriptedProcessPythonInterface::CreatePluginObject(
return m_object_instance_sp;
}

StructuredData::DictionarySP ScriptedProcessPythonInterface::GetCapabilities() {
Status error;
StructuredData::DictionarySP dict =
Dispatch<StructuredData::DictionarySP>("get_capabilities", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, error))
return {};

return dict;
}

Status
ScriptedProcessPythonInterface::Attach(const ProcessAttachInfo &attach_info) {
lldb::ProcessAttachInfoSP attach_info_sp =
std::make_shared<ProcessAttachInfo>(attach_info);
return GetStatusFromMethod("attach", attach_info_sp);
}

Status ScriptedProcessPythonInterface::Launch() {
return GetStatusFromMethod("launch");
}
Expand Down Expand Up @@ -103,25 +122,6 @@ StructuredData::DictionarySP ScriptedProcessPythonInterface::GetThreadsInfo() {
return dict;
}

StructuredData::DictionarySP
ScriptedProcessPythonInterface::GetThreadWithID(lldb::tid_t tid) {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_thread_with_id", error, tid);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error))
return {};

StructuredData::DictionarySP dict{obj->GetAsDictionary()};

return dict;
}

StructuredData::DictionarySP
ScriptedProcessPythonInterface::GetRegistersForThread(lldb::tid_t tid) {
// TODO: Implement
return {};
}

lldb::DataExtractorSP ScriptedProcessPythonInterface::ReadMemoryAtAddress(
lldb::addr_t address, size_t size, Status &error) {
Status py_error;
Expand All @@ -135,19 +135,29 @@ lldb::DataExtractorSP ScriptedProcessPythonInterface::ReadMemoryAtAddress(
return data_sp;
}

size_t ScriptedProcessPythonInterface::WriteMemoryAtAddress(
lldb::addr_t addr, lldb::DataExtractorSP data_sp, Status &error) {
Status py_error;
StructuredData::ObjectSP obj =
Dispatch("write_memory_at_address", py_error, addr, data_sp, error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error))
return LLDB_INVALID_OFFSET;

// If there was an error on the python call, surface it to the user.
if (py_error.Fail())
error = py_error;

return obj->GetIntegerValue(LLDB_INVALID_OFFSET);
}

StructuredData::ArraySP ScriptedProcessPythonInterface::GetLoadedImages() {
Status error;
StructuredData::ArraySP array =
Dispatch<StructuredData::ArraySP>("get_loaded_images", error);

if (!array || !array->IsValid() || error.Fail()) {
return ScriptedInterface::ErrorWithMessage<StructuredData::ArraySP>(
LLVM_PRETTY_FUNCTION,
llvm::Twine("Null or invalid object (" +
llvm::Twine(error.AsCString()) + llvm::Twine(")."))
.str(),
error);
}
if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, array, error))
return {};

return array;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ class ScriptedProcessPythonInterface : public ScriptedProcessInterface,
StructuredData::DictionarySP args_sp,
StructuredData::Generic *script_obj = nullptr) override;

StructuredData::DictionarySP GetCapabilities() override;

Status Attach(const ProcessAttachInfo &attach_info) override;

Status Launch() override;

Status Resume() override;
Expand All @@ -43,13 +47,12 @@ class ScriptedProcessPythonInterface : public ScriptedProcessInterface,

StructuredData::DictionarySP GetThreadsInfo() override;

StructuredData::DictionarySP GetThreadWithID(lldb::tid_t tid) override;

StructuredData::DictionarySP GetRegistersForThread(lldb::tid_t tid) override;

lldb::DataExtractorSP ReadMemoryAtAddress(lldb::addr_t address, size_t size,
Status &error) override;

size_t WriteMemoryAtAddress(lldb::addr_t addr, lldb::DataExtractorSP data_sp,
Status &error) override;

StructuredData::ArraySP GetLoadedImages() override;

lldb::pid_t GetProcessID() override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
// LLDB Python header must be included first
#include "lldb-python.h"

#include "SWIGPythonBridge.h"
#include "ScriptInterpreterPythonImpl.h"
#include "ScriptedPythonInterface.h"
#include <optional>
Expand Down Expand Up @@ -71,6 +70,36 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>(
return m_interpreter.GetDataExtractorFromSBData(*sb_data);
}

template <>
lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error) {
lldb::SBAttachInfo *sb_attach_info = reinterpret_cast<lldb::SBAttachInfo *>(
LLDBSWIGPython_CastPyObjectToSBAttachInfo(p.get()));

if (!sb_attach_info) {
error.SetErrorString(
"Couldn't cast lldb::SBAttachInfo to lldb::ProcessAttachInfoSP.");
return nullptr;
}

return m_interpreter.GetOpaqueTypeFromSBAttachInfo(*sb_attach_info);
}

template <>
lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error) {
lldb::SBLaunchInfo *sb_launch_info = reinterpret_cast<lldb::SBLaunchInfo *>(
LLDBSWIGPython_CastPyObjectToSBLaunchInfo(p.get()));

if (!sb_launch_info) {
error.SetErrorString(
"Couldn't cast lldb::SBLaunchInfo to lldb::ProcessLaunchInfoSP.");
return nullptr;
}

return m_interpreter.GetOpaqueTypeFromSBLaunchInfo(*sb_launch_info);
}

template <>
std::optional<MemoryRegionInfo>
ScriptedPythonInterface::ExtractValueFromPythonObject<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
return python::ToSWIGWrapper(arg);
}

python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) {
return python::ToSWIGWrapper(arg);
}

python::PythonObject Transform(lldb::ProcessLaunchInfoSP arg) {
return python::ToSWIGWrapper(arg);
}

python::PythonObject Transform(lldb::DataExtractorSP arg) {
return python::ToSWIGWrapper(arg);
}

template <typename T, typename U>
void ReverseTransform(T &original_arg, U transformed_arg, Status &error) {
// If U is not a PythonObject, don't touch it!
Expand Down Expand Up @@ -198,6 +210,14 @@ template <>
Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>(
python::PythonObject &p, Status &error);

template <>
lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error);

template <>
lldb::ProcessLaunchInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
lldb::ProcessLaunchInfoSP>(python::PythonObject &p, Status &error);

template <>
lldb::DataExtractorSP
ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>(
Expand Down
2 changes: 1 addition & 1 deletion lldb/source/Target/Memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,11 @@ size_t MemoryCache::Read(addr_t addr, void *dst, size_t dst_len,
return dst_len - bytes_left;

if (process_bytes_read != cache_line_byte_size) {
data_buffer_heap_up->SetByteSize(process_bytes_read);
if (process_bytes_read < data_buffer_heap_up->GetByteSize()) {
dst_len -= data_buffer_heap_up->GetByteSize() - process_bytes_read;
bytes_left = process_bytes_read;
}
data_buffer_heap_up->SetByteSize(process_bytes_read);
}
m_L2_cache[curr_addr] = DataBufferSP(data_buffer_heap_up.release());
// We have read data and put it into the cache, continue through the
Expand Down
28 changes: 14 additions & 14 deletions lldb/source/Target/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3079,6 +3079,17 @@ bool Target::SetSectionUnloaded(const lldb::SectionSP &section_sp,

void Target::ClearAllLoadedSections() { m_section_load_history.Clear(); }

void Target::SaveScriptedLaunchInfo(lldb_private::ProcessInfo &process_info) {
if (process_info.IsScriptedProcess()) {
// Only copy scripted process launch options.
ProcessLaunchInfo &default_launch_info = const_cast<ProcessLaunchInfo &>(
GetGlobalProperties().GetProcessLaunchInfo());
default_launch_info.SetProcessPluginName("ScriptedProcess");
default_launch_info.SetScriptedMetadata(process_info.GetScriptedMetadata());
SetProcessLaunchInfo(default_launch_info);
}
}

Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) {
m_stats.SetLaunchOrAttachTime();
Status error;
Expand Down Expand Up @@ -3108,19 +3119,7 @@ Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) {

launch_info.GetFlags().Set(eLaunchFlagDebug);

if (launch_info.IsScriptedProcess()) {
// Only copy scripted process launch options.
ProcessLaunchInfo &default_launch_info = const_cast<ProcessLaunchInfo &>(
GetGlobalProperties().GetProcessLaunchInfo());

default_launch_info.SetProcessPluginName("ScriptedProcess");
default_launch_info.SetScriptedProcessClassName(
launch_info.GetScriptedProcessClassName());
default_launch_info.SetScriptedProcessDictionarySP(
launch_info.GetScriptedProcessDictionarySP());

SetProcessLaunchInfo(launch_info);
}
SaveScriptedLaunchInfo(launch_info);

// Get the value of synchronous execution here. If you wait till after you
// have started to run, then you could have hit a breakpoint, whose command
Expand Down Expand Up @@ -3333,11 +3332,12 @@ Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) {

Status error;
if (state != eStateConnected && platform_sp != nullptr &&
platform_sp->CanDebugProcess()) {
platform_sp->CanDebugProcess() && !attach_info.IsScriptedProcess()) {
SetPlatform(platform_sp);
process_sp = platform_sp->Attach(attach_info, GetDebugger(), this, error);
} else {
if (state != eStateConnected) {
SaveScriptedLaunchInfo(attach_info);
const char *plugin_name = attach_info.GetProcessPluginName();
process_sp =
CreateProcess(attach_info.GetListenerForProcess(GetDebugger()),
Expand Down
6 changes: 6 additions & 0 deletions lldb/source/Utility/ProcessInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "lldb/Utility/ProcessInfo.h"

#include "lldb/Interpreter/ScriptedMetadata.h"
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/StreamString.h"
Expand Down Expand Up @@ -36,6 +37,7 @@ void ProcessInfo::Clear() {
m_gid = UINT32_MAX;
m_arch.Clear();
m_pid = LLDB_INVALID_PROCESS_ID;
m_scripted_metadata_sp.reset();
}

const char *ProcessInfo::GetName() const {
Expand Down Expand Up @@ -109,6 +111,10 @@ void ProcessInfo::SetArguments(const Args &args, bool first_arg_is_executable) {
}
}

bool ProcessInfo::IsScriptedProcess() const {
return m_scripted_metadata_sp && *m_scripted_metadata_sp;
}

void ProcessInstanceInfo::Dump(Stream &s, UserIDResolver &resolver) const {
if (m_pid != LLDB_INVALID_PROCESS_ID)
s.Printf(" pid = %" PRIu64 "\n", m_pid);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ def cleanup():
self.assertEqual(process.GetProcessID(), 666)
self.assertEqual(process.GetNumThreads(), 0)

impl = process.GetScriptedImplementation()
self.assertTrue(impl)
impl = process.GetScriptedImplementation()
self.assertTrue(impl)
impl = process.GetScriptedImplementation()
self.assertTrue(impl)
impl = process.GetScriptedImplementation()
self.assertTrue(impl)

addr = 0x500000000
buff = process.ReadMemory(addr, 4, error)
self.assertEqual(buff, None)
Expand Down Expand Up @@ -137,27 +146,42 @@ def cleanup():

target_1 = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
self.assertTrue(target_1, VALID_TARGET)

# We still need to specify a PID when attaching even for scripted processes
attach_info = lldb.SBAttachInfo(42)
attach_info.SetProcessPluginName("ScriptedProcess")
attach_info.SetScriptedProcessClassName("dummy_scripted_process.DummyScriptedProcess")

error = lldb.SBError()
process_1 = target_1.Launch(launch_info, error)
process_1 = target_1.Attach(attach_info, error)
self.assertTrue(process_1 and process_1.IsValid(), PROCESS_IS_VALID)
self.assertEqual(process_1.GetProcessID(), 42)
self.assertEqual(process_1.GetNumThreads(), 1)

# ... then try reading from target #1 process ...
addr = 0x500000000
message = "Hello, target 1"
buff = process_1.ReadCStringFromMemory(addr, len(message) + 1, error)
self.assertSuccess(error)
self.assertEqual(buff, message)

# ... now, reading again from target #0 process to make sure the call
# gets dispatched to the right target.
addr = 0x500000000
message = "Hello, target 0"
buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error)
self.assertSuccess(error)
self.assertEqual(buff, message)

# Let's write some memory.
message = "Hello, world!"
bytes_written = process_0.WriteMemoryAsCString(addr, message, error)
self.assertSuccess(error)
self.assertEqual(bytes_written, len(message) + 1)

# ... and check if that memory was saved properly.
buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error)
self.assertSuccess(error)
self.assertEqual(buff, message)

thread = process_0.GetSelectedThread()
self.assertTrue(thread, "Invalid thread.")
self.assertEqual(thread.GetThreadID(), 0x19)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,29 @@
from lldb.plugins.scripted_process import ScriptedThread

class DummyScriptedProcess(ScriptedProcess):
memory = None

def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredData):
super().__init__(exe_ctx, args)
self.threads[0] = DummyScriptedThread(self, None)

def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo:
return None

def get_thread_with_id(self, tid: int):
return {}

def get_registers_for_thread(self, tid: int):
return {}

def read_memory_at_address(self, addr: int, size: int, error: lldb.SBError) -> lldb.SBData:
self.memory = {}
addr = 0x500000000
debugger = self.target.GetDebugger()
index = debugger.GetIndexOfTarget(self.target)
self.memory[addr] = "Hello, target " + str(index)

def read_memory_at_address(self, addr: int, size: int, error: lldb.SBError) -> lldb.SBData:
data = lldb.SBData().CreateDataFromCString(
self.target.GetByteOrder(),
self.target.GetCodeByteSize(),
"Hello, target " + str(index))
self.memory[addr])

return data

def write_memory_at_address(self, addr, data, error):
self.memory[addr] = data.GetString(error, 0)
return len(self.memory[addr]) + 1

def get_loaded_images(self):
return self.loaded_images

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,6 @@ def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredDat
super().__init__(exe_ctx, args)
self.threads[0] = InvalidScriptedThread(self, None)

def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo:
return None

def get_thread_with_id(self, tid: int):
return {}

def get_registers_for_thread(self, tid: int):
return {}

def read_memory_at_address(self, addr: int, size: int, error: lldb.SBError) -> lldb.SBData:
error.SetErrorString("This is an invalid scripted process!")
return lldb.SBData()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,6 @@ def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegion
return None
return mem_region

def get_thread_with_id(self, tid: int):
return {}

def get_registers_for_thread(self, tid: int):
return {}

def read_memory_at_address(self, addr: int, size: int, error: lldb.SBError) -> lldb.SBData:
data = lldb.SBData()
bytes_read = self.corefile_process.ReadMemory(addr, size, error)
Expand Down
26 changes: 26 additions & 0 deletions lldb/test/API/python_api/process/TestProcessAPI.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,32 @@ def test_write_memory(self):
exe=False,
startstr=b'a')

# Get the SBValue for the global variable 'my_cstring'.
val = frame.FindValue("my_cstring", lldb.eValueTypeVariableGlobal)
self.DebugSBValue(val)

addr = val.AddressOf().GetValueAsUnsigned()

# Write an empty string to memory
bytes_written = process.WriteMemoryAsCString(addr, "", error)
self.assertEqual(bytes_written, 0)
if not error.Success():
self.fail("SBProcess.WriteMemoryAsCString() failed")

message = "Hello!"
bytes_written = process.WriteMemoryAsCString(addr, message, error)
self.assertEqual(bytes_written, len(message) + 1)
if not error.Success():
self.fail("SBProcess.WriteMemoryAsCString() failed")

cstring = process.ReadCStringFromMemory(
val.AddressOf().GetValueAsUnsigned(), 256, error)
if not error.Success():
self.fail("SBProcess.ReadCStringFromMemory() failed")

self.assertEqual(cstring, message)


def test_access_my_int(self):
"""Test access 'my_int' using Python SBProcess.GetByteOrder() and other APIs."""
self.build()
Expand Down
23 changes: 23 additions & 0 deletions lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@ void *lldb_private::LLDBSWIGPython_CastPyObjectToSBData(PyObject *data) {
return nullptr;
}

void *lldb_private::LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject *data) {
return nullptr;
}

void *lldb_private::LLDBSWIGPython_CastPyObjectToSBLaunchInfo(PyObject *data) {
return nullptr;
}

void *lldb_private::LLDBSWIGPython_CastPyObjectToSBError(PyObject *data) {
return nullptr;
}
Expand Down Expand Up @@ -268,3 +276,18 @@ bool lldb_private::LLDBSwigPythonStopHookCallHandleStop(
python::PythonObject lldb_private::python::ToSWIGWrapper(const Status &status) {
return python::PythonObject();
}

python::PythonObject
lldb_private::python::ToSWIGWrapper(lldb::ProcessAttachInfoSP) {
return python::PythonObject();
}

python::PythonObject
lldb_private::python::ToSWIGWrapper(lldb::ProcessLaunchInfoSP) {
return python::PythonObject();
}

python::PythonObject
lldb_private::python::ToSWIGWrapper(lldb::DataExtractorSP) {
return python::PythonObject();
}