[Lldb-commits] [lldb] 3014a1c - [lldb] Add scripted process launch/attach option to {, platform }process commands
Med Ismail Bennani via lldb-commits
lldb-commits at lists.llvm.org
Fri Mar 3 19:33:19 PST 2023
Author: Med Ismail Bennani
Date: 2023-03-03T19:33:02-08:00
New Revision: 3014a1c5a130daeb73f6d3b7280c5ceaeadc66a8
URL: https://github.com/llvm/llvm-project/commit/3014a1c5a130daeb73f6d3b7280c5ceaeadc66a8
DIFF: https://github.com/llvm/llvm-project/commit/3014a1c5a130daeb73f6d3b7280c5ceaeadc66a8.diff
LOG: [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 at gmail.com>
Added:
lldb/source/Commands/CommandOptionsProcessAttach.cpp
lldb/source/Commands/CommandOptionsProcessAttach.h
Modified:
lldb/include/lldb/API/SBAttachInfo.h
lldb/include/lldb/API/SBStructuredData.h
lldb/include/lldb/Target/Process.h
lldb/source/API/SBAttachInfo.cpp
lldb/source/Commands/CMakeLists.txt
lldb/source/Commands/CommandObjectPlatform.cpp
lldb/source/Commands/CommandObjectProcess.cpp
Removed:
################################################################################
diff --git a/lldb/include/lldb/API/SBAttachInfo.h b/lldb/include/lldb/API/SBAttachInfo.h
index 9b211d0f74bd..e296956c0a19 100644
--- a/lldb/include/lldb/API/SBAttachInfo.h
+++ b/lldb/include/lldb/API/SBAttachInfo.h
@@ -164,6 +164,14 @@ class LLDB_API SBAttachInfo {
/// allows a
diff erent 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
index 9cf55ad002e0..13dfca6b38c2 100644
--- a/lldb/include/lldb/API/SBStructuredData.h
+++ b/lldb/include/lldb/API/SBStructuredData.h
@@ -92,6 +92,7 @@ class SBStructuredData {
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
index 3ffacb52299b..ff2a62a09def 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -193,6 +193,28 @@ class ProcessAttachInfo : public ProcessInstanceInfo {
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 @@ class ProcessAttachInfo : public ProcessInstanceInfo {
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
index edb4f7104d41..cb66566270b2 100644
--- 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 @@ 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);
+}
diff --git a/lldb/source/Commands/CMakeLists.txt b/lldb/source/Commands/CMakeLists.txt
index bec6a9c1bc44..dc1aebc30de1 100644
--- a/lldb/source/Commands/CMakeLists.txt
+++ b/lldb/source/Commands/CMakeLists.txt
@@ -40,6 +40,7 @@ add_lldb_library(lldbCommands
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
index 1ab218fa6fb2..40e037afdd6d 100644
--- 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 @@ 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});
@@ -1180,6 +1186,15 @@ class CommandObjectPlatformProcessLaunch : public CommandObjectParsed {
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 @@ class CommandObjectPlatformProcessLaunch : public CommandObjectParsed {
}
CommandOptionsProcessLaunch m_options;
+ OptionGroupPythonClassWithDict m_class_options;
OptionGroupOptions m_all_options;
};
@@ -1572,71 +1588,16 @@ 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;
@@ -1644,6 +1605,15 @@ class CommandObjectPlatformProcessAttach : public CommandObjectParsed {
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 @@ 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 {
diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp
index a45371f824b1..ff9992a2aac6 100644
--- 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 @@ 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 {
@@ -409,6 +354,14 @@ class CommandObjectProcessAttach : public CommandObjectProcessLaunchOrAttach {
}
}
+ 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 @@ class CommandObjectProcessAttach : public CommandObjectProcessLaunchOrAttach {
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.cpp b/lldb/source/Commands/CommandOptionsProcessAttach.cpp
new file mode 100644
index 000000000000..f9bd92938fa1
--- /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<OptionDefinition> CommandOptionsProcessAttach::GetDefinitions() {
+ return llvm::makeArrayRef(g_process_attach_options);
+}
diff --git a/lldb/source/Commands/CommandOptionsProcessAttach.h b/lldb/source/Commands/CommandOptionsProcessAttach.h
new file mode 100644
index 000000000000..02daf1aa769a
--- /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<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
More information about the lldb-commits
mailing list