diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -122,7 +122,16 @@ void SetRunArguments(const Args &args); + // Get the whole environment including the platform inherited environment and + // the target specific environment, excluding the unset environment variables. Environment GetEnvironment() const; + // Get the platform inherited environment, excluding the unset environment + // variables. + Environment GetInheritedEnvironment() const; + // Get the target specific environment only, without the platform inherited + // environment. + Environment GetTargetEnvironment() const; + // Set the target specific environment. void SetEnvironment(Environment env); bool GetSkipPrologue() const; 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 @@ -1259,6 +1259,14 @@ const std::string &GetText() { return m_content; } + void SetText(const char *text) { + if (text == nullptr) { + m_content.clear(); + return; + } + m_content = text; + } + protected: std::string m_label; bool m_required; @@ -1618,6 +1626,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 GetPossibleOptions(const char *calculate_label) { + std::vector 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 ListFieldDelegate : public FieldDelegate { public: ListFieldDelegate(const char *label, T default_field) @@ -1908,6 +1943,29 @@ SelectionType m_selection_type; }; +class ArgumentsFieldDelegate : public ListFieldDelegate { +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; + } + + void AddArguments(const Args &arguments) { + for (size_t i = 0; i < arguments.GetArgumentCount(); i++) { + AddNewField(); + TextFieldDelegate &field = GetField(GetNumberOfFields() - 1); + field.SetText(arguments.GetArgumentAtIndex(i)); + } + } +}; + template class MappingFieldDelegate : public FieldDelegate { public: @@ -2068,14 +2126,36 @@ const std::string &GetName() { return GetKeyField().GetName(); } const std::string &GetValue() { return GetValueField().GetText(); } + + void SetName(const char *name) { return GetKeyField().SetText(name); } + + void SetValue(const char *value) { return GetValueField().SetText(value); } }; class EnvironmentVariableListFieldDelegate : public ListFieldDelegate { public: - EnvironmentVariableListFieldDelegate() - : ListFieldDelegate("Environment Variables", - EnvironmentVariableFieldDelegate()) {} + EnvironmentVariableListFieldDelegate(const char *label) + : ListFieldDelegate(label, 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; + } + + void AddEnvironmentVariables(const Environment &environment) { + for (auto &variable : environment) { + AddNewField(); + EnvironmentVariableFieldDelegate &field = + GetField(GetNumberOfFields() - 1); + field.SetName(variable.getKey().str().c_str()); + field.SetValue(variable.getValue().c_str()); + } + } }; class FormAction { @@ -2200,6 +2280,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 choices) { ChoicesFieldDelegate *delegate = @@ -2229,6 +2317,12 @@ return delegate; } + ArgumentsFieldDelegate *AddArgumentsField() { + ArgumentsFieldDelegate *delegate = new ArgumentsFieldDelegate(); + m_fields.push_back(FieldDelegateUP(delegate)); + return delegate; + } + template MappingFieldDelegate *AddMappingField(K key_field, V value_field) { MappingFieldDelegate *delegate = @@ -2252,9 +2346,10 @@ return delegate; } - EnvironmentVariableListFieldDelegate *AddEnvironmentVariableListField() { + EnvironmentVariableListFieldDelegate * + AddEnvironmentVariableListField(const char *label) { EnvironmentVariableListFieldDelegate *delegate = - new EnvironmentVariableListFieldDelegate(); + new EnvironmentVariableListFieldDelegate(label); m_fields.push_back(FieldDelegateUP(delegate)); return delegate; } @@ -3030,6 +3125,396 @@ 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(); + SetArgumentsFieldDefaultValue(); + m_target_environment_field = + AddEnvironmentVariableListField("Target Environment Variables"); + SetTargetEnvironmentFieldDefaultValue(); + m_working_directory_field = AddDirectoryField( + "Working Directory", GetDefaultWorkingDirectory().c_str(), true, false); + + m_show_advanced_field = AddBooleanField("Show advanced settings.", false); + + m_stop_at_entry_field = AddBooleanField("Stop at entry point.", false); + m_detach_on_error_field = + AddBooleanField("Detach on error.", GetDefaultDetachOnError()); + m_disable_aslr_field = + AddBooleanField("Disable ASLR", GetDefaultDisableASLR()); + m_plugin_field = AddProcessPluginField(); + m_arch_field = AddArchField("Architecture", "", false); + m_shell_field = AddFileField("Shell", "", true, false); + m_expand_shell_arguments_field = + AddBooleanField("Expand shell arguments.", false); + + m_disable_standard_io_field = + AddBooleanField("Disable Standard IO", GetDefaultDisableStandardIO()); + m_standard_output_field = + AddFileField("Standard Output File", "", /*need_to_exist=*/false, + /*required=*/false); + m_standard_error_field = + AddFileField("Standard Error File", "", /*need_to_exist=*/false, + /*required=*/false); + m_standard_input_field = + AddFileField("Standard Input File", "", /*need_to_exist=*/false, + /*required=*/false); + + m_show_inherited_environment_field = + AddBooleanField("Show inherited environment variables.", false); + m_inherited_environment_field = + AddEnvironmentVariableListField("Inherited Environment Variables"); + SetInheritedEnvironmentFieldDefaultValue(); + + 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_detach_on_error_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_disable_standard_io_field->FieldDelegateShow(); + if (m_disable_standard_io_field->GetBoolean()) { + m_standard_input_field->FieldDelegateHide(); + m_standard_output_field->FieldDelegateHide(); + m_standard_error_field->FieldDelegateHide(); + } else { + m_standard_input_field->FieldDelegateShow(); + m_standard_output_field->FieldDelegateShow(); + m_standard_error_field->FieldDelegateShow(); + } + m_show_inherited_environment_field->FieldDelegateShow(); + if (m_show_inherited_environment_field->GetBoolean()) + m_inherited_environment_field->FieldDelegateShow(); + else + m_inherited_environment_field->FieldDelegateHide(); + } else { + m_stop_at_entry_field->FieldDelegateHide(); + m_detach_on_error_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_disable_standard_io_field->FieldDelegateHide(); + m_standard_input_field->FieldDelegateHide(); + m_standard_output_field->FieldDelegateHide(); + m_standard_error_field->FieldDelegateHide(); + m_show_inherited_environment_field->FieldDelegateHide(); + m_inherited_environment_field->FieldDelegateHide(); + } + } + + // Methods for setting the default value of the fields. + + void SetArgumentsFieldDefaultValue() { + TargetSP target = m_debugger.GetSelectedTarget(); + if (target == nullptr) + return; + + const Args &target_arguments = + target->GetProcessLaunchInfo().GetArguments(); + m_arguments_field->AddArguments(target_arguments); + } + + void SetTargetEnvironmentFieldDefaultValue() { + TargetSP target = m_debugger.GetSelectedTarget(); + if (target == nullptr) + return; + + const Environment &target_environment = target->GetTargetEnvironment(); + m_target_environment_field->AddEnvironmentVariables(target_environment); + } + + void SetInheritedEnvironmentFieldDefaultValue() { + TargetSP target = m_debugger.GetSelectedTarget(); + if (target == nullptr) + return; + + const Environment &inherited_environment = + target->GetInheritedEnvironment(); + m_inherited_environment_field->AddEnvironmentVariables( + inherited_environment); + } + + std::string GetDefaultWorkingDirectory() { + TargetSP target = m_debugger.GetSelectedTarget(); + if (target == nullptr) + return ""; + + PlatformSP platform = target->GetPlatform(); + return platform->GetWorkingDirectory().GetPath(); + } + + bool GetDefaultDisableASLR() { + TargetSP target = m_debugger.GetSelectedTarget(); + if (target == nullptr) + return false; + + return target->GetDisableASLR(); + } + + bool GetDefaultDisableStandardIO() { + TargetSP target = m_debugger.GetSelectedTarget(); + if (target == nullptr) + return true; + + return target->GetDisableSTDIO(); + } + + bool GetDefaultDetachOnError() { + TargetSP target = m_debugger.GetSelectedTarget(); + if (target == nullptr) + return true; + + return target->GetDetachOnError(); + } + + // Methods for getting the necessary information and setting them to the + // ProcessLaunchInfo. + + void GetExecutableSettings(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); + } + + void GetArguments(ProcessLaunchInfo &launch_info) { + TargetSP target = m_debugger.GetSelectedTarget(); + Args arguments = m_arguments_field->GetArguments(); + launch_info.GetArguments().AppendArguments(arguments); + } + + void GetEnvironment(ProcessLaunchInfo &launch_info) { + Environment target_environment = + m_target_environment_field->GetEnvironment(); + Environment inherited_environment = + m_inherited_environment_field->GetEnvironment(); + launch_info.GetEnvironment().insert(target_environment.begin(), + target_environment.end()); + launch_info.GetEnvironment().insert(inherited_environment.begin(), + inherited_environment.end()); + } + + void GetWorkingDirectory(ProcessLaunchInfo &launch_info) { + if (m_working_directory_field->IsSpecified()) + launch_info.SetWorkingDirectory( + m_working_directory_field->GetResolvedFileSpec()); + } + + void GetStopAtEntry(ProcessLaunchInfo &launch_info) { + if (m_stop_at_entry_field->GetBoolean()) + launch_info.GetFlags().Set(eLaunchFlagStopAtEntry); + else + launch_info.GetFlags().Clear(eLaunchFlagStopAtEntry); + } + + void GetDetachOnError(ProcessLaunchInfo &launch_info) { + if (m_detach_on_error_field->GetBoolean()) + launch_info.GetFlags().Set(eLaunchFlagDetachOnError); + else + launch_info.GetFlags().Clear(eLaunchFlagDetachOnError); + } + + void GetDisableASLR(ProcessLaunchInfo &launch_info) { + if (m_disable_aslr_field->GetBoolean()) + launch_info.GetFlags().Set(eLaunchFlagDisableASLR); + else + launch_info.GetFlags().Clear(eLaunchFlagDisableASLR); + } + + void GetPlugin(ProcessLaunchInfo &launch_info) { + launch_info.SetProcessPluginName(m_plugin_field->GetPluginName()); + } + + void GetArch(ProcessLaunchInfo &launch_info) { + if (!m_arch_field->IsSpecified()) + return; + + 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 GetShell(ProcessLaunchInfo &launch_info) { + if (!m_shell_field->IsSpecified()) + return; + + launch_info.SetShell(m_shell_field->GetResolvedFileSpec()); + launch_info.SetShellExpandArguments( + m_expand_shell_arguments_field->GetBoolean()); + } + + void GetStandardIO(ProcessLaunchInfo &launch_info) { + if (m_disable_standard_io_field->GetBoolean()) { + launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO); + return; + } + + FileAction action; + if (m_standard_input_field->IsSpecified()) { + action.Open(STDIN_FILENO, m_standard_input_field->GetFileSpec(), 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); + } + if (m_standard_error_field->IsSpecified()) { + action.Open(STDERR_FILENO, m_standard_error_field->GetFileSpec(), false, + true); + launch_info.AppendFileAction(action); + } + } + + void GetInheritTCC(ProcessLaunchInfo &launch_info) { + if (m_debugger.GetSelectedTarget()->GetInheritTCC()) + launch_info.GetFlags().Set(eLaunchFlagInheritTCCFromParent); + } + + ProcessLaunchInfo GetLaunchInfo() { + ProcessLaunchInfo launch_info; + + GetExecutableSettings(launch_info); + GetArguments(launch_info); + GetEnvironment(launch_info); + GetWorkingDirectory(launch_info); + GetStopAtEntry(launch_info); + GetDetachOnError(launch_info); + GetDisableASLR(launch_info); + GetPlugin(launch_info); + GetArch(launch_info); + GetShell(launch_info); + GetStandardIO(launch_info); + GetInheritTCC(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_target_environment_field; + DirectoryFieldDelegate *m_working_directory_field; + + BooleanFieldDelegate *m_show_advanced_field; + + BooleanFieldDelegate *m_stop_at_entry_field; + BooleanFieldDelegate *m_detach_on_error_field; + BooleanFieldDelegate *m_disable_aslr_field; + ProcessPluginFieldDelegate *m_plugin_field; + ArchFieldDelegate *m_arch_field; + FileFieldDelegate *m_shell_field; + BooleanFieldDelegate *m_expand_shell_arguments_field; + BooleanFieldDelegate *m_disable_standard_io_field; + FileFieldDelegate *m_standard_input_field; + FileFieldDelegate *m_standard_output_field; + FileFieldDelegate *m_standard_error_field; + + BooleanFieldDelegate *m_show_inherited_environment_field; + EnvironmentVariableListFieldDelegate *m_inherited_environment_field; +}; + class MenuDelegate { public: virtual ~MenuDelegate() = default; @@ -5612,6 +6097,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 = diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -3985,6 +3985,45 @@ return ComputeEnvironment(); } +Environment TargetProperties::GetInheritedEnvironment() const { + Environment environment; + + if (m_target == nullptr) + return environment; + + if (!m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, ePropertyInheritEnv, + g_target_properties[ePropertyInheritEnv].default_uint_value != 0)) + return environment; + + PlatformSP platform_sp = m_target->GetPlatform(); + if (platform_sp == nullptr) + return environment; + + Environment platform_environment = platform_sp->GetEnvironment(); + for (const auto &KV : platform_environment) + environment[KV.first()] = KV.second; + + Args property_unset_environment; + m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, ePropertyUnsetEnvVars, + property_unset_environment); + for (const auto &var : property_unset_environment) + environment.erase(var.ref()); + + return environment; +} + +Environment TargetProperties::GetTargetEnvironment() const { + Args property_environment; + m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, ePropertyEnvVars, + property_environment); + Environment environment; + for (const auto &KV : Environment(property_environment)) + environment[KV.first()] = KV.second; + + return environment; +} + void TargetProperties::SetEnvironment(Environment env) { // TODO: Get rid of the Args intermediate step const uint32_t idx = ePropertyEnvVars;