[LLDB][GUI] Add Create Target form

This patch adds a Create Target form for the LLDB GUI. Additionally, an
Arch Field was introduced to input an arch and the file and directory
fields now have a required property.

Reviewed By: clayborg

Differential Revision: https://reviews.llvm.org/D106192
This commit is contained in:
Omar Emara 2021-07-29 13:27:22 -07:00 committed by Greg Clayton
parent 5856632252
commit 18c25cd376
1 changed files with 198 additions and 6 deletions

View File

@ -36,6 +36,7 @@
#include "lldb/Interpreter/CommandCompletions.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/OptionGroupPlatform.h"
#if LLDB_ENABLE_CURSES
#include "lldb/Breakpoint/BreakpointLocation.h"
@ -2651,6 +2652,185 @@ protected:
ProcessPluginFieldDelegate *m_plugin_field;
};
class TargetCreateFormDelegate : public FormDelegate {
public:
TargetCreateFormDelegate(Debugger &debugger) : m_debugger(debugger) {
m_executable_field = AddFileField("Executable", "", /*need_to_exist=*/true,
/*required=*/true);
m_core_file_field = AddFileField("Core File", "", /*need_to_exist=*/true,
/*required=*/false);
m_symbol_file_field = AddFileField(
"Symbol File", "", /*need_to_exist=*/true, /*required=*/false);
m_show_advanced_field = AddBooleanField("Show advanced settings.", false);
m_remote_file_field = AddFileField(
"Remote File", "", /*need_to_exist=*/false, /*required=*/false);
m_arch_field = AddArchField("Architecture", "", /*required=*/false);
m_platform_field = AddPlatformPluginField(debugger);
m_load_dependent_files_field =
AddChoicesField("Load Dependents", 3, GetLoadDependentFilesChoices());
AddAction("Create", [this](Window &window) { CreateTarget(window); });
}
std::string GetName() override { return "Create Target"; }
void UpdateFieldsVisibility() override {
if (m_show_advanced_field->GetBoolean()) {
m_remote_file_field->FieldDelegateShow();
m_arch_field->FieldDelegateShow();
m_platform_field->FieldDelegateShow();
m_load_dependent_files_field->FieldDelegateShow();
} else {
m_remote_file_field->FieldDelegateHide();
m_arch_field->FieldDelegateHide();
m_platform_field->FieldDelegateHide();
m_load_dependent_files_field->FieldDelegateHide();
}
}
static constexpr const char *kLoadDependentFilesNo = "No";
static constexpr const char *kLoadDependentFilesYes = "Yes";
static constexpr const char *kLoadDependentFilesExecOnly = "Executable only";
std::vector<std::string> GetLoadDependentFilesChoices() {
std::vector<std::string> load_depentents_options;
load_depentents_options.push_back(kLoadDependentFilesExecOnly);
load_depentents_options.push_back(kLoadDependentFilesYes);
load_depentents_options.push_back(kLoadDependentFilesNo);
return load_depentents_options;
}
LoadDependentFiles GetLoadDependentFiles() {
std::string choice = m_load_dependent_files_field->GetChoiceContent();
if (choice == kLoadDependentFilesNo)
return eLoadDependentsNo;
if (choice == kLoadDependentFilesYes)
return eLoadDependentsYes;
return eLoadDependentsDefault;
}
OptionGroupPlatform GetPlatformOptions() {
OptionGroupPlatform platform_options(false);
platform_options.SetPlatformName(m_platform_field->GetPluginName().c_str());
return platform_options;
}
TargetSP GetTarget() {
OptionGroupPlatform platform_options = GetPlatformOptions();
TargetSP target_sp;
Status status = m_debugger.GetTargetList().CreateTarget(
m_debugger, m_executable_field->GetPath(),
m_arch_field->GetArchString(), GetLoadDependentFiles(),
&platform_options, target_sp);
if (status.Fail()) {
SetError(status.AsCString());
return nullptr;
}
m_debugger.GetTargetList().SetSelectedTarget(target_sp);
return target_sp;
}
void SetSymbolFile(TargetSP target_sp) {
if (!m_symbol_file_field->IsSpecified())
return;
ModuleSP module_sp(target_sp->GetExecutableModule());
if (!module_sp)
return;
module_sp->SetSymbolFileFileSpec(
m_symbol_file_field->GetResolvedFileSpec());
}
void SetCoreFile(TargetSP target_sp) {
if (!m_core_file_field->IsSpecified())
return;
FileSpec core_file_spec = m_core_file_field->GetResolvedFileSpec();
FileSpec core_file_directory_spec;
core_file_directory_spec.GetDirectory() = core_file_spec.GetDirectory();
target_sp->AppendExecutableSearchPaths(core_file_directory_spec);
ProcessSP process_sp(target_sp->CreateProcess(
m_debugger.GetListener(), llvm::StringRef(), &core_file_spec, false));
if (!process_sp) {
SetError("Unable to find process plug-in for core file!");
return;
}
Status status = process_sp->LoadCore();
if (status.Fail()) {
SetError("Can't find plug-in for core file!");
return;
}
}
void SetRemoteFile(TargetSP target_sp) {
if (!m_remote_file_field->IsSpecified())
return;
ModuleSP module_sp(target_sp->GetExecutableModule());
if (!module_sp)
return;
FileSpec remote_file_spec = m_remote_file_field->GetFileSpec();
module_sp->SetPlatformFileSpec(remote_file_spec);
}
void RemoveTarget(TargetSP target_sp) {
m_debugger.GetTargetList().DeleteTarget(target_sp);
}
void CreateTarget(Window &window) {
ClearError();
bool all_fields_are_valid = CheckFieldsValidity();
if (!all_fields_are_valid)
return;
TargetSP target_sp = GetTarget();
if (HasError())
return;
SetSymbolFile(target_sp);
if (HasError()) {
RemoveTarget(target_sp);
return;
}
SetCoreFile(target_sp);
if (HasError()) {
RemoveTarget(target_sp);
return;
}
SetRemoteFile(target_sp);
if (HasError()) {
RemoveTarget(target_sp);
return;
}
window.GetParent()->RemoveSubWindow(&window);
}
protected:
Debugger &m_debugger;
FileFieldDelegate *m_executable_field;
FileFieldDelegate *m_core_file_field;
FileFieldDelegate *m_symbol_file_field;
BooleanFieldDelegate *m_show_advanced_field;
FileFieldDelegate *m_remote_file_field;
ArchFieldDelegate *m_arch_field;
PlatformPluginFieldDelegate *m_platform_field;
ChoicesFieldDelegate *m_load_dependent_files_field;
};
class MenuDelegate {
public:
virtual ~MenuDelegate() = default;
@ -3078,15 +3258,15 @@ public:
bool done = false;
int delay_in_tenths_of_a_second = 1;
// Alas the threading model in curses is a bit lame so we need to resort to
// polling every 0.5 seconds. We could poll for stdin ourselves and then
// pass the keys down but then we need to translate all of the escape
// Alas the threading model in curses is a bit lame so we need to resort
// to polling every 0.5 seconds. We could poll for stdin ourselves and
// then pass the keys down but then we need to translate all of the escape
// sequences ourselves. So we resort to polling for input because we need
// to receive async process events while in this loop.
halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths
// of seconds seconds when calling
// Window::GetChar()
halfdelay(delay_in_tenths_of_a_second); // Poll using some number of
// tenths of seconds seconds when
// calling Window::GetChar()
ListenerSP listener_sp(
Listener::MakeListener("lldb.IOHandler.curses.Application"));
@ -4908,6 +5088,18 @@ public:
MenuActionResult MenuDelegateAction(Menu &menu) override {
switch (menu.GetIdentifier()) {
case eMenuID_TargetCreate: {
WindowSP main_window_sp = m_app.GetMainWindow();
FormDelegateSP form_delegate_sp =
FormDelegateSP(new TargetCreateFormDelegate(m_debugger));
Rect bounds = main_window_sp->GetCenteredRect(80, 19);
WindowSP form_window_sp = main_window_sp->CreateSubWindow(
form_delegate_sp->GetName().c_str(), bounds, true);
WindowDelegateSP window_delegate_sp =
WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
form_window_sp->SetDelegate(window_delegate_sp);
return MenuActionResult::Handled;
}
case eMenuID_ThreadStepIn: {
ExecutionContext exe_ctx =
m_debugger.GetCommandInterpreter().GetExecutionContext();