OmarEmaraDev created this revision. OmarEmaraDev added a reviewer: clayborg. Herald added a reviewer: teemperor. OmarEmaraDev requested review of this revision. Herald added a project: LLDB. Herald added a subscriber: lldb-commits.
This patch adds a process launch form. Additionally, a LazyBoolean field was implemented and numerous utility methods were added to various fields to get the launch form working. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D107869 Files: lldb/source/Core/IOHandlerCursesGUI.cpp
Index: lldb/source/Core/IOHandlerCursesGUI.cpp =================================================================== --- lldb/source/Core/IOHandlerCursesGUI.cpp +++ lldb/source/Core/IOHandlerCursesGUI.cpp @@ -28,6 +28,7 @@ #include "lldb/Core/StreamFile.h" #include "lldb/Core/ValueObjectUpdater.h" #include "lldb/Host/File.h" +#include "lldb/Host/HostInfo.h" #include "lldb/Utility/Predicate.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StreamString.h" @@ -1619,6 +1620,33 @@ } }; +class LazyBooleanFieldDelegate : public ChoicesFieldDelegate { +public: + LazyBooleanFieldDelegate(const char *label, const char *calculate_label) + : ChoicesFieldDelegate(label, 3, GetPossibleOptions(calculate_label)) {} + + static constexpr const char *kNo = "No"; + static constexpr const char *kYes = "Yes"; + + std::vector<std::string> GetPossibleOptions(const char *calculate_label) { + std::vector<std::string> options; + options.push_back(calculate_label); + options.push_back(kYes); + options.push_back(kNo); + return options; + } + + LazyBool GetLazyBoolean() { + std::string choice = GetChoiceContent(); + if (choice == kNo) + return eLazyBoolNo; + else if (choice == kYes) + return eLazyBoolYes; + else + return eLazyBoolCalculate; + } +}; + template <class T> class ListFieldDelegate : public FieldDelegate { public: ListFieldDelegate(const char *label, T default_field) @@ -1909,6 +1937,21 @@ SelectionType m_selection_type; }; +class ArgumentsFieldDelegate : public ListFieldDelegate<TextFieldDelegate> { +public: + ArgumentsFieldDelegate() + : ListFieldDelegate("Arguments", + TextFieldDelegate("Argument", "", false)) {} + + Args GetArguments() { + Args arguments; + for (int i = 0; i < GetNumberOfFields(); i++) { + arguments.AppendArgument(GetField(i).GetText()); + } + return arguments; + } +}; + template <class KeyFieldDelegateType, class ValueFieldDelegateType> class MappingFieldDelegate : public FieldDelegate { public: @@ -2077,6 +2120,15 @@ EnvironmentVariableListFieldDelegate() : ListFieldDelegate("Environment Variables", EnvironmentVariableFieldDelegate()) {} + + Environment GetEnvironment() { + Environment environment; + for (int i = 0; i < GetNumberOfFields(); i++) { + environment.insert( + std::make_pair(GetField(i).GetName(), GetField(i).GetValue())); + } + return environment; + } }; class FormAction { @@ -2201,6 +2253,14 @@ return delegate; } + LazyBooleanFieldDelegate *AddLazyBooleanField(const char *label, + const char *calculate_label) { + LazyBooleanFieldDelegate *delegate = + new LazyBooleanFieldDelegate(label, calculate_label); + m_fields.push_back(FieldDelegateUP(delegate)); + return delegate; + } + ChoicesFieldDelegate *AddChoicesField(const char *label, int height, std::vector<std::string> choices) { ChoicesFieldDelegate *delegate = @@ -2230,6 +2290,12 @@ return delegate; } + ArgumentsFieldDelegate *AddArgumentsField() { + ArgumentsFieldDelegate *delegate = new ArgumentsFieldDelegate(); + m_fields.push_back(FieldDelegateUP(delegate)); + return delegate; + } + template <class K, class V> MappingFieldDelegate<K, V> *AddMappingField(K key_field, V value_field) { MappingFieldDelegate<K, V> *delegate = @@ -3031,6 +3097,309 @@ ChoicesFieldDelegate *m_load_dependent_files_field; }; +class ProcessLaunchFormDelegate : public FormDelegate { +public: + ProcessLaunchFormDelegate(Debugger &debugger, WindowSP main_window_sp) + : m_debugger(debugger), m_main_window_sp(main_window_sp) { + + m_arguments_field = AddArgumentsField(); + m_enviroment_field = AddEnvironmentVariableListField(); + + m_show_advanced_field = AddBooleanField("Show advanced settings.", false); + + m_stop_at_entry_field = AddBooleanField("Stop at entry point.", false); + m_working_directory_field = + AddDirectoryField("Working Directory", "", /*need_to_exist=*/true, + /*required=*/false); + m_disable_aslr_field = + AddLazyBooleanField("Disable ASLR", "Use target setting"); + m_plugin_field = AddProcessPluginField(); + m_arch_field = AddArchField("Architecture", "", /*required=*/false); + m_shell_field = AddFileField("Shell", "", /*need_to_exist=*/true, + /*required=*/false); + m_expand_shell_arguments_field = + AddBooleanField("Expand shell arguments.", false); + m_standard_output_field = + AddFileField("Standard Output", "", /*need_to_exist=*/false, + /*required=*/false); + m_standard_error_field = + AddFileField("Standard Error", "", /*need_to_exist=*/false, + /*required=*/false); + m_standard_input_field = + AddFileField("Standard Input", "", /*need_to_exist=*/false, + /*required=*/false); + + AddAction("Launch", [this](Window &window) { Launch(window); }); + } + + std::string GetName() override { return "Launch Process"; } + + void UpdateFieldsVisibility() override { + if (m_show_advanced_field->GetBoolean()) { + m_stop_at_entry_field->FieldDelegateShow(); + m_working_directory_field->FieldDelegateShow(); + m_disable_aslr_field->FieldDelegateShow(); + m_plugin_field->FieldDelegateShow(); + m_arch_field->FieldDelegateShow(); + m_shell_field->FieldDelegateShow(); + m_expand_shell_arguments_field->FieldDelegateShow(); + m_standard_input_field->FieldDelegateShow(); + m_standard_output_field->FieldDelegateShow(); + m_standard_error_field->FieldDelegateShow(); + } else { + m_stop_at_entry_field->FieldDelegateHide(); + m_working_directory_field->FieldDelegateHide(); + m_disable_aslr_field->FieldDelegateHide(); + m_plugin_field->FieldDelegateHide(); + m_arch_field->FieldDelegateHide(); + m_shell_field->FieldDelegateHide(); + m_expand_shell_arguments_field->FieldDelegateHide(); + m_standard_input_field->FieldDelegateHide(); + m_standard_output_field->FieldDelegateHide(); + m_standard_error_field->FieldDelegateHide(); + } + } + + void SetArguments(ProcessLaunchInfo &launch_info) { + TargetSP target = m_debugger.GetSelectedTarget(); + Args arguments = m_arguments_field->GetArguments(); + if (arguments.GetArgumentCount() != 0) + launch_info.GetArguments().AppendArguments(arguments); + + launch_info.GetArguments().AppendArguments( + target->GetProcessLaunchInfo().GetArguments()); + } + + void SetEnvironment(ProcessLaunchInfo &launch_info) { + Environment environment = m_enviroment_field->GetEnvironment(); + launch_info.GetEnvironment().insert(environment.begin(), environment.end()); + + TargetSP target = m_debugger.GetSelectedTarget(); + Environment target_environment = target->GetEnvironment(); + launch_info.GetEnvironment().insert(target_environment.begin(), + target_environment.end()); + } + + void SetStopAtEntry(ProcessLaunchInfo &launch_info) { + if (m_stop_at_entry_field->GetBoolean()) + launch_info.GetFlags().Set(eLaunchFlagStopAtEntry); + } + + void SetWorkingDirectory(ProcessLaunchInfo &launch_info) { + if (m_working_directory_field->IsSpecified()) + launch_info.SetWorkingDirectory( + m_working_directory_field->GetResolvedFileSpec()); + } + + void SetDisableASLR(ProcessLaunchInfo &launch_info) { + bool disable_aslr; + LazyBool lazy_boolean = m_disable_aslr_field->GetLazyBoolean(); + if (lazy_boolean == eLazyBoolCalculate) + disable_aslr = m_debugger.GetSelectedTarget()->GetDisableASLR(); + else + disable_aslr = lazy_boolean == eLazyBoolYes; + + if (disable_aslr) + launch_info.GetFlags().Set(eLaunchFlagDisableASLR); + else + launch_info.GetFlags().Clear(eLaunchFlagDisableASLR); + } + + void SetPlugin(ProcessLaunchInfo &launch_info) { + launch_info.SetProcessPluginName(m_plugin_field->GetPluginName()); + } + + void SetArch(ProcessLaunchInfo &launch_info) { + TargetSP target_sp = m_debugger.GetSelectedTarget(); + PlatformSP platform_sp = + target_sp ? target_sp->GetPlatform() : PlatformSP(); + launch_info.GetArchitecture() = Platform::GetAugmentedArchSpec( + platform_sp.get(), m_arch_field->GetArchString()); + } + + void SetShell(ProcessLaunchInfo &launch_info) { + if (m_shell_field->IsSpecified()) + launch_info.SetShell(m_shell_field->GetResolvedFileSpec()); + else + launch_info.SetShell(HostInfo::GetDefaultShell()); + launch_info.SetShellExpandArguments( + m_expand_shell_arguments_field->GetBoolean()); + } + + void AddFileActions(ProcessLaunchInfo &launch_info) { + FileAction action; + const FileSpec dev_null(FileSystem::DEV_NULL); + + if (m_standard_input_field->IsSpecified()) { + action.Open(STDIN_FILENO, m_standard_input_field->GetFileSpec(), true, + false); + launch_info.AppendFileAction(action); + } else { + action.Open(STDIN_FILENO, dev_null, true, false); + launch_info.AppendFileAction(action); + } + + if (m_standard_output_field->IsSpecified()) { + action.Open(STDOUT_FILENO, m_standard_output_field->GetFileSpec(), false, + true); + launch_info.AppendFileAction(action); + } else { + action.Open(STDOUT_FILENO, dev_null, false, true); + launch_info.AppendFileAction(action); + } + + if (m_standard_error_field->IsSpecified()) { + action.Open(STDERR_FILENO, m_standard_error_field->GetFileSpec(), false, + true); + launch_info.AppendFileAction(action); + } else { + action.Open(STDERR_FILENO, dev_null, false, true); + launch_info.AppendFileAction(action); + } + } + + void SetTargetSettings(ProcessLaunchInfo &launch_info) { + TargetSP target = m_debugger.GetSelectedTarget(); + + if (target->GetInheritTCC()) + launch_info.GetFlags().Set(eLaunchFlagInheritTCCFromParent); + + if (target->GetDetachOnError()) + launch_info.GetFlags().Set(eLaunchFlagDetachOnError); + + if (target->GetDisableSTDIO()) + launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO); + } + + void SetExecutable(ProcessLaunchInfo &launch_info) { + TargetSP target = m_debugger.GetSelectedTarget(); + ModuleSP executable_module = target->GetExecutableModule(); + llvm::StringRef target_settings_argv0 = target->GetArg0(); + + if (!target_settings_argv0.empty()) { + launch_info.GetArguments().AppendArgument(target_settings_argv0); + launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(), + false); + return; + } + + launch_info.SetExecutableFile(executable_module->GetPlatformFileSpec(), + true); + } + + ProcessLaunchInfo GetLaunchInfo() { + ProcessLaunchInfo launch_info; + + SetArguments(launch_info); + SetEnvironment(launch_info); + SetStopAtEntry(launch_info); + SetWorkingDirectory(launch_info); + SetDisableASLR(launch_info); + SetPlugin(launch_info); + SetArch(launch_info); + SetShell(launch_info); + AddFileActions(launch_info); + SetTargetSettings(launch_info); + SetExecutable(launch_info); + + return launch_info; + } + + bool StopRunningProcess() { + ExecutionContext exe_ctx = + m_debugger.GetCommandInterpreter().GetExecutionContext(); + + if (!exe_ctx.HasProcessScope()) + return false; + + Process *process = exe_ctx.GetProcessPtr(); + if (!(process && process->IsAlive())) + return false; + + FormDelegateSP form_delegate_sp = + FormDelegateSP(new DetachOrKillProcessFormDelegate(process)); + Rect bounds = m_main_window_sp->GetCenteredRect(85, 8); + WindowSP form_window_sp = m_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 true; + } + + Target *GetTarget() { + Target *target = m_debugger.GetSelectedTarget().get(); + + if (target == nullptr) { + SetError("No target exists!"); + return nullptr; + } + + ModuleSP exe_module_sp = target->GetExecutableModule(); + + if (exe_module_sp == nullptr) { + SetError("No executable in target!"); + return nullptr; + } + + return target; + } + + void Launch(Window &window) { + ClearError(); + + bool all_fields_are_valid = CheckFieldsValidity(); + if (!all_fields_are_valid) + return; + + bool process_is_running = StopRunningProcess(); + if (process_is_running) + return; + + Target *target = GetTarget(); + if (HasError()) + return; + + StreamString stream; + ProcessLaunchInfo launch_info = GetLaunchInfo(); + Status status = target->Launch(launch_info, &stream); + + if (status.Fail()) { + SetError(status.AsCString()); + return; + } + + ProcessSP process_sp(target->GetProcessSP()); + if (!process_sp) { + SetError("Launched successfully but target has no process!"); + return; + } + + window.GetParent()->RemoveSubWindow(&window); + } + +protected: + Debugger &m_debugger; + WindowSP m_main_window_sp; + + ArgumentsFieldDelegate *m_arguments_field; + EnvironmentVariableListFieldDelegate *m_enviroment_field; + + BooleanFieldDelegate *m_show_advanced_field; + + BooleanFieldDelegate *m_stop_at_entry_field; + DirectoryFieldDelegate *m_working_directory_field; + LazyBooleanFieldDelegate *m_disable_aslr_field; + ProcessPluginFieldDelegate *m_plugin_field; + ArchFieldDelegate *m_arch_field; + FileFieldDelegate *m_shell_field; + BooleanFieldDelegate *m_expand_shell_arguments_field; + FileFieldDelegate *m_standard_input_field; + FileFieldDelegate *m_standard_output_field; + FileFieldDelegate *m_standard_error_field; +}; + class MenuDelegate { public: virtual ~MenuDelegate() = default; @@ -5348,6 +5717,18 @@ form_window_sp->SetDelegate(window_delegate_sp); return MenuActionResult::Handled; } + case eMenuID_ProcessLaunch: { + WindowSP main_window_sp = m_app.GetMainWindow(); + FormDelegateSP form_delegate_sp = FormDelegateSP( + new ProcessLaunchFormDelegate(m_debugger, main_window_sp)); + Rect bounds = main_window_sp->GetCenteredRect(80, 22); + 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_ProcessContinue: { ExecutionContext exe_ctx =
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits