diff --git a/lldb/source/Core/IOHandlerCursesGUI.cpp b/lldb/source/Core/IOHandlerCursesGUI.cpp --- a/lldb/source/Core/IOHandlerCursesGUI.cpp +++ b/lldb/source/Core/IOHandlerCursesGUI.cpp @@ -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" @@ -56,6 +57,7 @@ #include "lldb/Utility/State.h" #endif +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringRef.h" #ifdef _WIN32 @@ -1279,13 +1281,15 @@ class FileFieldDelegate : public TextFieldDelegate { public: FileFieldDelegate(const char *label, const char *content, - bool need_to_exist = true) - : TextFieldDelegate(label, content), m_need_to_exist(need_to_exist) {} + bool need_to_exist = true, bool required = true) + : TextFieldDelegate(label, content), m_need_to_exist(need_to_exist), + m_required(required) {} - // Set appropriate error messages if the file doesn't exists or is, in fact, a - // directory. void FieldDelegateExitCallback() override { - FileSpec file(GetPath()); + if (m_content.empty() && !m_required) + return; + + FileSpec file = GetFileSpec(); if (m_need_to_exist && !FileSystem::Instance().Exists(file)) { SetError("File doesn't exist!"); return; @@ -1296,23 +1300,33 @@ } } - // Returns the path of the file. const std::string &GetPath() { return m_content; } + bool IsSpecified() { return !m_content.empty(); } + + FileSpec GetFileSpec() { + FileSpec file_spec(m_content); + FileSystem::Instance().Resolve(file_spec); + return file_spec; + } + protected: bool m_need_to_exist; + bool m_required; }; class DirectoryFieldDelegate : public TextFieldDelegate { public: DirectoryFieldDelegate(const char *label, const char *content, - bool need_to_exist = true) - : TextFieldDelegate(label, content), m_need_to_exist(need_to_exist) {} + bool need_to_exist = true, bool required = true) + : TextFieldDelegate(label, content), m_need_to_exist(need_to_exist), + m_required(required) {} - // Set appropriate error messages if the directory doesn't exists or is, in - // fact, a file. void FieldDelegateExitCallback() override { - FileSpec file(GetPath()); + if (m_content.empty() && !m_required) + return; + + FileSpec file = GetFileSpec(); if (m_need_to_exist && !FileSystem::Instance().Exists(file)) { SetError("Directory doesn't exist!"); return; @@ -1323,11 +1337,43 @@ } } - // Returns the path of the file. const std::string &GetPath() { return m_content; } + bool IsSpecified() { return !m_content.empty(); } + + FileSpec GetFileSpec() { + FileSpec file_spec(m_content); + FileSystem::Instance().Resolve(file_spec); + return file_spec; + } + protected: bool m_need_to_exist; + bool m_required; +}; + +class ArchFieldDelegate : public TextFieldDelegate { +public: + ArchFieldDelegate(const char *label, const char *content, + bool required = false) + : TextFieldDelegate(label, content), m_required(required) {} + + void FieldDelegateExitCallback() override { + if (m_content.empty() && !m_required) + return; + + if (GetArchSpec().IsValid()) + return; + + SetError("Not a valid arch!"); + } + + const std::string &GetArchString() { return m_content; } + + ArchSpec GetArchSpec() { return ArchSpec(GetArchString()); } + +protected: + bool m_required; }; class BooleanFieldDelegate : public FieldDelegate { @@ -1863,18 +1909,28 @@ } FileFieldDelegate *AddFileField(const char *label, const char *content, - bool need_to_exist = true) { + bool need_to_exist = true, + bool required = true) { FileFieldDelegate *delegate = - new FileFieldDelegate(label, content, need_to_exist); + new FileFieldDelegate(label, content, need_to_exist, required); m_fields.push_back(FieldDelegateUP(delegate)); return delegate; } DirectoryFieldDelegate *AddDirectoryField(const char *label, const char *content, - bool need_to_exist = true) { + bool need_to_exist = true, + bool required = true) { DirectoryFieldDelegate *delegate = - new DirectoryFieldDelegate(label, content, need_to_exist); + new DirectoryFieldDelegate(label, content, need_to_exist, required); + m_fields.push_back(FieldDelegateUP(delegate)); + return delegate; + } + + ArchFieldDelegate *AddArchField(const char *label, const char *content, + bool required = false) { + ArchFieldDelegate *delegate = + new ArchFieldDelegate(label, content, required); m_fields.push_back(FieldDelegateUP(delegate)); return delegate; } @@ -2507,6 +2563,174 @@ ChoicesFieldDelegate *m_plugin_field; }; +class TargetCreateFormDelegate : public FormDelegate { +public: + TargetCreateFormDelegate(Debugger &debugger) : m_debugger(debugger) { + m_file_field = AddFileField("File", ""); + m_core_file_field = AddFileField("Core File", "", true, false); + m_symbol_file_field = AddFileField("Symbol File", "", true, false); + m_show_advanced_field = AddBooleanField("Show advanced settings.", false); + m_arch_field = AddArchField("Architecture", ""); + std::vector load_depentents_options; + load_depentents_options.push_back( + std::string("Only if the target is an executable.")); + load_depentents_options.push_back(std::string("Yes.")); + load_depentents_options.push_back(std::string("No.")); + m_load_dependent_files_field = + AddChoicesField("Load Dependents", 3, load_depentents_options); + + AddAction("Create", [this](Window &window) { CreateTarget(window); }); + } + + std::string GetName() override { return "Create Target"; } + + void UpdateFieldsVisibility() override { + if (m_show_advanced_field->GetBoolean()) { + m_arch_field->FieldDelegateShow(); + m_load_dependent_files_field->FieldDelegateShow(); + } else { + m_arch_field->FieldDelegateHide(); + m_load_dependent_files_field->FieldDelegateHide(); + } + } + + LoadDependentFiles GetLoadDependentFiles() { + std::string choice = m_load_dependent_files_field->GetChoiceContent(); + if (choice == "No.") + return eLoadDependentsNo; + if (choice == "Yes.") + return eLoadDependentsYes; + return eLoadDependentsDefault; + } + + TargetSP GetTarget() { + FileSpec file_spec = m_file_field->GetFileSpec(); + + if (!FileSystem::Instance().Exists(file_spec)) { + SetError("File doesn't exist!"); + return nullptr; + } + + if (!FileSystem::Instance().Open(file_spec, + lldb_private::File::eOpenOptionRead)) { + SetError("Can't open file!"); + return nullptr; + } + + OptionGroupPlatform platform_options(false); + TargetSP target_sp; + Status status = m_debugger.GetTargetList().CreateTarget( + m_debugger, m_file_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; + } + + FileSpec symbol_file_spec = m_symbol_file_field->GetFileSpec(); + + if (!FileSystem::Instance().Exists(symbol_file_spec)) { + SetError("Symbol file doesn't exist!"); + return; + } + + if (!FileSystem::Instance().Open(symbol_file_spec, + lldb_private::File::eOpenOptionRead)) { + SetError("Can't open symbol file!"); + return; + } + + ModuleSP module_sp(target_sp->GetExecutableModule()); + if (!module_sp) + return; + + module_sp->SetSymbolFileFileSpec(symbol_file_spec); + } + + void SetCoreFile(TargetSP target_sp) { + if (!m_core_file_field->IsSpecified()) { + return; + } + + FileSpec core_file_spec = m_core_file_field->GetFileSpec(); + + if (!FileSystem::Instance().Exists(core_file_spec)) { + SetError("Core file doesn't exist!"); + return; + } + + if (!FileSystem::Instance().Open(core_file_spec, + lldb_private::File::eOpenOptionRead)) { + SetError("Can't open core file!"); + return; + } + + 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!"); + } + + Status status = process_sp->LoadCore(); + if (status.Fail()) { + SetError("Can't find plug-in for core file!"); + return; + } + } + + void RemoveTarget(TargetSP target_sp) { + m_debugger.GetTargetList().DeleteTarget(target_sp); + } + + void CreateTarget(Window &window) { + ClearError(); + + 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; + } + + window.GetParent()->RemoveSubWindow(&window); + } + +protected: + Debugger &m_debugger; + + FileFieldDelegate *m_file_field; + FileFieldDelegate *m_core_file_field; + FileFieldDelegate *m_symbol_file_field; + BooleanFieldDelegate *m_show_advanced_field; + ArchFieldDelegate *m_arch_field; + ChoicesFieldDelegate *m_load_dependent_files_field; +}; + class MenuDelegate { public: virtual ~MenuDelegate() = default; @@ -2934,15 +3158,15 @@ 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")); @@ -4718,6 +4942,18 @@ 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();