//===-- CommandObjectTarget.cpp ---------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "CommandObjectTarget.h"

// C Includes
// C++ Includes
#include <cerrno>

// Other libraries and framework includes
// Project includes
#include "lldb/Interpreter/Args.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/IOHandler.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/Section.h"
#include "lldb/Core/State.h"
#include "lldb/Core/Timer.h"
#include "lldb/Core/ValueObjectVariable.h"
#include "lldb/DataFormatters/ValueObjectPrinter.h"
#include "lldb/Host/StringConvert.h"
#include "lldb/Host/Symbols.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Interpreter/Options.h"
#include "lldb/Interpreter/OptionGroupArchitecture.h"
#include "lldb/Interpreter/OptionGroupBoolean.h"
#include "lldb/Interpreter/OptionGroupFile.h"
#include "lldb/Interpreter/OptionGroupFormat.h"
#include "lldb/Interpreter/OptionGroupVariable.h"
#include "lldb/Interpreter/OptionGroupPlatform.h"
#include "lldb/Interpreter/OptionGroupUInt64.h"
#include "lldb/Interpreter/OptionGroupUUID.h"
#include "lldb/Interpreter/OptionGroupString.h"
#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/FuncUnwinders.h"
#include "lldb/Symbol/LineTable.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/SymbolFile.h"
#include "lldb/Symbol/SymbolVendor.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/ABI.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadSpec.h"

using namespace lldb;
using namespace lldb_private;

static void
DumpTargetInfo (uint32_t target_idx, Target *target, const char *prefix_cstr, bool show_stopped_process_status, Stream &strm)
{
    const ArchSpec &target_arch = target->GetArchitecture();

    Module *exe_module = target->GetExecutableModulePointer();
    char exe_path[PATH_MAX];
    bool exe_valid = false;
    if (exe_module)
        exe_valid = exe_module->GetFileSpec().GetPath (exe_path, sizeof(exe_path));

    if (!exe_valid)
        ::strcpy (exe_path, "<none>");

    strm.Printf ("%starget #%u: %s", prefix_cstr ? prefix_cstr : "", target_idx, exe_path);

    uint32_t properties = 0;
    if (target_arch.IsValid())
    {
        strm.Printf ("%sarch=", properties++ > 0 ? ", " : " ( ");
        target_arch.DumpTriple (strm);
        properties++;
    }
    PlatformSP platform_sp (target->GetPlatform());
    if (platform_sp)
        strm.Printf ("%splatform=%s", properties++ > 0 ? ", " : " ( ", platform_sp->GetName().GetCString());

    ProcessSP process_sp (target->GetProcessSP());
    bool show_process_status = false;
    if (process_sp)
    {
        lldb::pid_t pid = process_sp->GetID();
        StateType state = process_sp->GetState();
        if (show_stopped_process_status)
            show_process_status = StateIsStoppedState(state, true);
        const char *state_cstr = StateAsCString (state);
        if (pid != LLDB_INVALID_PROCESS_ID)
            strm.Printf ("%spid=%" PRIu64, properties++ > 0 ? ", " : " ( ", pid);
        strm.Printf ("%sstate=%s", properties++ > 0 ? ", " : " ( ", state_cstr);
    }
    if (properties > 0)
        strm.PutCString (" )\n");
    else
        strm.EOL();
    if (show_process_status)
    {
        const bool only_threads_with_stop_reason = true;
        const uint32_t start_frame = 0;
        const uint32_t num_frames = 1;
        const uint32_t num_frames_with_source = 1;
        process_sp->GetStatus (strm);
        process_sp->GetThreadStatus (strm, 
                                     only_threads_with_stop_reason, 
                                     start_frame,
                                     num_frames,
                                     num_frames_with_source);

    }
}

static uint32_t
DumpTargetList (TargetList &target_list, bool show_stopped_process_status, Stream &strm)
{
    const uint32_t num_targets = target_list.GetNumTargets();
    if (num_targets)
    {
        TargetSP selected_target_sp (target_list.GetSelectedTarget());
        strm.PutCString ("Current targets:\n");
        for (uint32_t i = 0; i < num_targets; ++i)
        {
            TargetSP target_sp (target_list.GetTargetAtIndex (i));
            if (target_sp)
            {
                bool is_selected = target_sp.get() == selected_target_sp.get();
                DumpTargetInfo (i, 
                                target_sp.get(), 
                                is_selected ? "* " : "  ", 
                                show_stopped_process_status,
                                strm);
            }
        }
    }
    return num_targets;
}

#pragma mark CommandObjectTargetCreate

//-------------------------------------------------------------------------
// "target create"
//-------------------------------------------------------------------------

class CommandObjectTargetCreate : public CommandObjectParsed
{
public:
    CommandObjectTargetCreate(CommandInterpreter &interpreter) :
        CommandObjectParsed(interpreter,
                            "target create",
                            "Create a target using the argument as the main executable.",
                            nullptr),
        m_option_group (interpreter),
        m_arch_option (),
        m_core_file (LLDB_OPT_SET_1, false, "core", 'c', 0, eArgTypeFilename, "Fullpath to a core file to use for this target."),
        m_platform_path (LLDB_OPT_SET_1, false, "platform-path", 'P', 0, eArgTypePath, "Path to the remote file to use for this target."),
        m_symbol_file (LLDB_OPT_SET_1, false, "symfile", 's', 0, eArgTypeFilename, "Fullpath to a stand alone debug symbols file for when debug symbols are not in the executable."),
        m_remote_file (LLDB_OPT_SET_1, false, "remote-file", 'r', 0, eArgTypeFilename, "Fullpath to the file on the remote host if debugging remotely."),
        m_add_dependents (LLDB_OPT_SET_1, false, "no-dependents", 'd', "Don't load dependent files when creating the target, just add the specified executable.", true, true)
    {
        CommandArgumentEntry arg;
        CommandArgumentData file_arg;

        // Define the first (and only) variant of this arg.
            file_arg.arg_type = eArgTypeFilename;
        file_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg.push_back (file_arg);

        // Push the data for the first argument into the m_arguments vector.
        m_arguments.push_back (arg);

        m_option_group.Append (&m_arch_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Append (&m_core_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Append (&m_platform_path, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Append (&m_symbol_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Append (&m_remote_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Append (&m_add_dependents, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Finalize();
    }

    ~CommandObjectTargetCreate() override = default;

    Options *
    GetOptions () override
    {
        return &m_option_group;
    }

    int
    HandleArgumentCompletion (Args &input,
                              int &cursor_index,
                              int &cursor_char_position,
                              OptionElementVector &opt_element_vector,
                              int match_start_point,
                              int max_return_elements,
                              bool &word_complete,
                              StringList &matches) override
    {
        std::string completion_str (input.GetArgumentAtIndex(cursor_index));
        completion_str.erase (cursor_char_position);

        CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter,
                                                            CommandCompletions::eDiskFileCompletion,
                                                            completion_str.c_str(),
                                                            match_start_point,
                                                            max_return_elements,
                                                            nullptr,
                                                            word_complete,
                                                            matches);
        return matches.GetSize();
    }

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        const size_t argc = command.GetArgumentCount();
        FileSpec core_file (m_core_file.GetOptionValue().GetCurrentValue());
        FileSpec remote_file (m_remote_file.GetOptionValue().GetCurrentValue());

        if (core_file)
        {
            if (!core_file.Exists())
            {
                result.AppendErrorWithFormat("core file '%s' doesn't exist", core_file.GetPath().c_str());
                result.SetStatus (eReturnStatusFailed);
                return false;
                
            }
            if (!core_file.Readable())
            {
                result.AppendErrorWithFormat("core file '%s' is not readable", core_file.GetPath().c_str());
                result.SetStatus (eReturnStatusFailed);
                return false;
            }
        }

        if (argc == 1 || core_file || remote_file)
        {
            FileSpec symfile (m_symbol_file.GetOptionValue().GetCurrentValue());
            if (symfile)
            {
                if (symfile.Exists())
                {
                    if (!symfile.Readable())
                    {
                        result.AppendErrorWithFormat("symbol file '%s' is not readable", symfile.GetPath().c_str());
                        result.SetStatus (eReturnStatusFailed);
                        return false;
                    }
                }
                else
                {
                    char symfile_path[PATH_MAX];
                    symfile.GetPath(symfile_path, sizeof(symfile_path));
                    result.AppendErrorWithFormat("invalid symbol file path '%s'", symfile_path);
                    result.SetStatus (eReturnStatusFailed);
                    return false;
                }
            }

            const char *file_path = command.GetArgumentAtIndex(0);
            Timer scoped_timer(__PRETTY_FUNCTION__, "(lldb) target create '%s'", file_path);
            FileSpec file_spec;

            if (file_path)
                file_spec.SetFile (file_path, true);

            bool must_set_platform_path = false;

            Debugger &debugger = m_interpreter.GetDebugger();

            TargetSP target_sp;
            const char *arch_cstr = m_arch_option.GetArchitectureName();
            const bool get_dependent_files = m_add_dependents.GetOptionValue().GetCurrentValue();
            Error error(debugger.GetTargetList().CreateTarget(debugger,
                                                              file_path,
                                                              arch_cstr,
                                                              get_dependent_files,
                                                              nullptr,
                                                              target_sp));

            if (target_sp)
            {
                // Only get the platform after we create the target because we might have
                // switched platforms depending on what the arguments were to CreateTarget()
                // we can't rely on the selected platform.

                PlatformSP platform_sp = target_sp->GetPlatform();

                if (remote_file)
                {
                    if (platform_sp)
                    {
                        // I have a remote file.. two possible cases
                        if (file_spec && file_spec.Exists())
                        {
                            // if the remote file does not exist, push it there
                            if (!platform_sp->GetFileExists (remote_file))
                            {
                                Error err = platform_sp->PutFile(file_spec, remote_file);
                                if (err.Fail())
                                {
                                    result.AppendError(err.AsCString());
                                    result.SetStatus (eReturnStatusFailed);
                                    return false;
                                }
                            }
                        }
                        else
                        {
                            // there is no local file and we need one
                            // in order to make the remote ---> local transfer we need a platform
                            // TODO: if the user has passed in a --platform argument, use it to fetch the right platform
                            if (!platform_sp)
                            {
                                result.AppendError("unable to perform remote debugging without a platform");
                                result.SetStatus (eReturnStatusFailed);
                                return false;
                            }
                            if (file_path)
                            {
                                // copy the remote file to the local file
                                Error err = platform_sp->GetFile(remote_file, file_spec);
                                if (err.Fail())
                                {
                                    result.AppendError(err.AsCString());
                                    result.SetStatus (eReturnStatusFailed);
                                    return false;
                                }
                            }
                            else
                            {
                                // make up a local file
                                result.AppendError("remote --> local transfer without local path is not implemented yet");
                                result.SetStatus (eReturnStatusFailed);
                                return false;
                            }
                        }
                    }
                    else
                    {
                        result.AppendError("no platform found for target");
                        result.SetStatus (eReturnStatusFailed);
                        return false;
                    }
                }

                if (symfile || remote_file)
                {
                    ModuleSP module_sp (target_sp->GetExecutableModule());
                    if (module_sp)
                    {
                        if (symfile)
                            module_sp->SetSymbolFileFileSpec(symfile);
                        if (remote_file)
                        {
                            std::string remote_path = remote_file.GetPath();
                            target_sp->SetArg0(remote_path.c_str());
                            module_sp->SetPlatformFileSpec(remote_file);
                        }
                    }
                }

                debugger.GetTargetList().SetSelectedTarget(target_sp.get());
                if (must_set_platform_path)
                {
                    ModuleSpec main_module_spec(file_spec);
                    ModuleSP module_sp = target_sp->GetSharedModule(main_module_spec);
                    if (module_sp)
                        module_sp->SetPlatformFileSpec(remote_file);
                }
                if (core_file)
                {
                    char core_path[PATH_MAX];
                    core_file.GetPath(core_path, sizeof(core_path));
                    if (core_file.Exists())
                    {
                        if (!core_file.Readable())
                        {
                            result.AppendMessageWithFormat ("Core file '%s' is not readable.\n", core_path);
                            result.SetStatus (eReturnStatusFailed);
                            return false;
                        }
                        FileSpec core_file_dir;
                        core_file_dir.GetDirectory() = core_file.GetDirectory();
                        target_sp->GetExecutableSearchPaths ().Append (core_file_dir);

                        ProcessSP process_sp(target_sp->CreateProcess(m_interpreter.GetDebugger().GetListener(), nullptr, &core_file));

                        if (process_sp)
                        {
                            // Seems weird that we Launch a core file, but that is
                            // what we do!
                            error = process_sp->LoadCore();

                            if (error.Fail())
                            {
                                result.AppendError(error.AsCString("can't find plug-in for core file"));
                                result.SetStatus (eReturnStatusFailed);
                                return false;
                            }
                            else
                            {
                                result.AppendMessageWithFormat ("Core file '%s' (%s) was loaded.\n", core_path, target_sp->GetArchitecture().GetArchitectureName());
                                result.SetStatus (eReturnStatusSuccessFinishNoResult);
                            }
                        }
                        else
                        {
                            result.AppendErrorWithFormat ("Unable to find process plug-in for core file '%s'\n", core_path);
                            result.SetStatus (eReturnStatusFailed);
                        }
                    }
                    else
                    {
                        result.AppendErrorWithFormat ("Core file '%s' does not exist\n", core_path);
                        result.SetStatus (eReturnStatusFailed);
                    }
                }
                else
                {
                    result.AppendMessageWithFormat ("Current executable set to '%s' (%s).\n", file_path, target_sp->GetArchitecture().GetArchitectureName());
                    result.SetStatus (eReturnStatusSuccessFinishNoResult);
                }
            }
            else
            {
                result.AppendError(error.AsCString());
                result.SetStatus (eReturnStatusFailed);
            }
        }
        else
        {
            result.AppendErrorWithFormat("'%s' takes exactly one executable path argument, or use the --core option.\n", m_cmd_name.c_str());
            result.SetStatus (eReturnStatusFailed);
        }
        return result.Succeeded();
    }

private:
    OptionGroupOptions m_option_group;
    OptionGroupArchitecture m_arch_option;
    OptionGroupFile m_core_file;
    OptionGroupFile m_platform_path;
    OptionGroupFile m_symbol_file;
    OptionGroupFile m_remote_file;
    OptionGroupBoolean m_add_dependents;
};

#pragma mark CommandObjectTargetList

//----------------------------------------------------------------------
// "target list"
//----------------------------------------------------------------------

class CommandObjectTargetList : public CommandObjectParsed
{
public:
    CommandObjectTargetList (CommandInterpreter &interpreter) :
        CommandObjectParsed(interpreter,
                            "target list",
                            "List all current targets in the current debug session.",
                            nullptr)
    {
    }

    ~CommandObjectTargetList() override = default;

protected:
    bool
    DoExecute (Args& args, CommandReturnObject &result) override
    {
        if (args.GetArgumentCount() == 0)
        {
            Stream &strm = result.GetOutputStream();

            bool show_stopped_process_status = false;
            if (DumpTargetList (m_interpreter.GetDebugger().GetTargetList(), show_stopped_process_status, strm) == 0)
            {
                strm.PutCString ("No targets.\n");
            }
            result.SetStatus (eReturnStatusSuccessFinishResult);
        }
        else
        {
            result.AppendError ("the 'target list' command takes no arguments\n");
            result.SetStatus (eReturnStatusFailed);
        }
        return result.Succeeded();
    }
};

#pragma mark CommandObjectTargetSelect

//----------------------------------------------------------------------
// "target select"
//----------------------------------------------------------------------

class CommandObjectTargetSelect : public CommandObjectParsed
{
public:
    CommandObjectTargetSelect (CommandInterpreter &interpreter) :
        CommandObjectParsed(interpreter,
                            "target select",
                            "Select a target as the current target by target index.",
                            nullptr)
    {
    }

    ~CommandObjectTargetSelect() override = default;

protected:
    bool
    DoExecute (Args& args, CommandReturnObject &result) override
    {
        if (args.GetArgumentCount() == 1)
        {
            bool success = false;
            const char *target_idx_arg = args.GetArgumentAtIndex(0);
            uint32_t target_idx = StringConvert::ToUInt32 (target_idx_arg, UINT32_MAX, 0, &success);
            if (success)
            {
                TargetList &target_list = m_interpreter.GetDebugger().GetTargetList();
                const uint32_t num_targets = target_list.GetNumTargets();
                if (target_idx < num_targets)
                {        
                    TargetSP target_sp (target_list.GetTargetAtIndex (target_idx));
                    if (target_sp)
                    {
                        Stream &strm = result.GetOutputStream();
                        target_list.SetSelectedTarget (target_sp.get());
                        bool show_stopped_process_status = false;
                        DumpTargetList (target_list, show_stopped_process_status, strm);
                        result.SetStatus (eReturnStatusSuccessFinishResult);
                    }
                    else
                    {
                        result.AppendErrorWithFormat ("target #%u is NULL in target list\n", target_idx);
                        result.SetStatus (eReturnStatusFailed);
                    }
                }
                else
                {
                    if (num_targets > 0)
                    {
                        result.AppendErrorWithFormat ("index %u is out of range, valid target indexes are 0 - %u\n",
                                                      target_idx,
                                                      num_targets - 1);
                    }
                    else
                    {
                        result.AppendErrorWithFormat ("index %u is out of range since there are no active targets\n",
                                                      target_idx);
                    }
                    result.SetStatus (eReturnStatusFailed);
                }
            }
            else
            {
                result.AppendErrorWithFormat("invalid index string value '%s'\n", target_idx_arg);
                result.SetStatus (eReturnStatusFailed);
            }
        }
        else
        {
            result.AppendError ("'target select' takes a single argument: a target index\n");
            result.SetStatus (eReturnStatusFailed);
        }
        return result.Succeeded();
    }
};

#pragma mark CommandObjectTargetSelect

//----------------------------------------------------------------------
// "target delete"
//----------------------------------------------------------------------

class CommandObjectTargetDelete : public CommandObjectParsed
{
public:
    CommandObjectTargetDelete (CommandInterpreter &interpreter) :
        CommandObjectParsed(interpreter,
                            "target delete",
                            "Delete one or more targets by target index.",
                            nullptr),
        m_option_group(interpreter),
        m_all_option(LLDB_OPT_SET_1, false, "all", 'a', "Delete all targets.", false, true),
        m_cleanup_option(
            LLDB_OPT_SET_1,
            false,
            "clean", 'c',
            "Perform extra cleanup to minimize memory consumption after deleting the target.  "
            "By default, LLDB will keep in memory any modules previously loaded by the target as well "
            "as all of its debug info.  Specifying --clean will unload all of these shared modules and "
            "cause them to be reparsed again the next time the target is run",
            false, true)
    {
        m_option_group.Append(&m_all_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Append(&m_cleanup_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Finalize();
    }

    ~CommandObjectTargetDelete() override = default;

    Options *
    GetOptions () override
    {
        return &m_option_group;
    }

protected:
    bool
    DoExecute (Args& args, CommandReturnObject &result) override
    {
        const size_t argc = args.GetArgumentCount();
        std::vector<TargetSP> delete_target_list;
        TargetList &target_list = m_interpreter.GetDebugger().GetTargetList();
        TargetSP target_sp;

        if (m_all_option.GetOptionValue())
        {
            for (int i = 0; i < target_list.GetNumTargets(); ++i)
                delete_target_list.push_back(target_list.GetTargetAtIndex(i));
        }
        else if (argc > 0)
        {
            const uint32_t num_targets = target_list.GetNumTargets();
            // Bail out if don't have any targets.
            if (num_targets == 0) {
                result.AppendError("no targets to delete");
                result.SetStatus(eReturnStatusFailed);
                return false;
            }

            for (uint32_t arg_idx = 0; arg_idx < argc; ++arg_idx)
            {
                const char *target_idx_arg = args.GetArgumentAtIndex(arg_idx);
                bool success = false;
                uint32_t target_idx = StringConvert::ToUInt32 (target_idx_arg, UINT32_MAX, 0, &success);
                if (!success)
                {
                    result.AppendErrorWithFormat("invalid target index '%s'\n", target_idx_arg);
                    result.SetStatus (eReturnStatusFailed);
                    return false;
                }
                if (target_idx < num_targets)
                {
                    target_sp = target_list.GetTargetAtIndex (target_idx);
                    if (target_sp)
                    {
                        delete_target_list.push_back (target_sp);
                        continue;
                    }
                }
                if (num_targets > 1)
                    result.AppendErrorWithFormat ("target index %u is out of range, valid target indexes are 0 - %u\n",
                                                    target_idx,
                                                    num_targets - 1);
                else
                    result.AppendErrorWithFormat("target index %u is out of range, the only valid index is 0\n",
                                                target_idx);

                result.SetStatus (eReturnStatusFailed);
                return false;
            }
        }
        else
        {
            target_sp = target_list.GetSelectedTarget();
            if (!target_sp)
            {
                result.AppendErrorWithFormat("no target is currently selected\n");
                result.SetStatus (eReturnStatusFailed);
                return false;
            }
            delete_target_list.push_back (target_sp);
        }

        const size_t num_targets_to_delete = delete_target_list.size();
        for (size_t idx = 0; idx < num_targets_to_delete; ++idx)
        {
            target_sp = delete_target_list[idx];
            target_list.DeleteTarget(target_sp);
            target_sp->Destroy();
        }
        // If "--clean" was specified, prune any orphaned shared modules from
        // the global shared module list
        if (m_cleanup_option.GetOptionValue ())
        {
            const bool mandatory = true;
            ModuleList::RemoveOrphanSharedModules(mandatory);
        }
        result.GetOutputStream().Printf("%u targets deleted.\n", (uint32_t)num_targets_to_delete);
        result.SetStatus(eReturnStatusSuccessFinishResult);

        return true;
    }

    OptionGroupOptions m_option_group;
    OptionGroupBoolean m_all_option;
    OptionGroupBoolean m_cleanup_option;
};

#pragma mark CommandObjectTargetVariable

//----------------------------------------------------------------------
// "target variable"
//----------------------------------------------------------------------

class CommandObjectTargetVariable : public CommandObjectParsed
{
    static const uint32_t SHORT_OPTION_FILE = 0x66696c65;   // 'file'
    static const uint32_t SHORT_OPTION_SHLB = 0x73686c62;   // 'shlb'

public:
    CommandObjectTargetVariable(CommandInterpreter &interpreter)
        : CommandObjectParsed(interpreter, "target variable",
                              "Read global variables for the current target, before or while running a process.",
                              nullptr, eCommandRequiresTarget),
          m_option_group(interpreter),
          m_option_variable(false), // Don't include frame options
          m_option_format(eFormatDefault),
          m_option_compile_units(LLDB_OPT_SET_1, false, "file", SHORT_OPTION_FILE, 0, eArgTypeFilename,
                                 "A basename or fullpath to a file that contains global variables. This option can be "
                                 "specified multiple times."),
          m_option_shared_libraries(LLDB_OPT_SET_1, false, "shlib", SHORT_OPTION_SHLB, 0, eArgTypeFilename,
                                    "A basename or fullpath to a shared library to use in the search for global "
                                    "variables. This option can be specified multiple times."),
          m_varobj_options()
    {
        CommandArgumentEntry arg;
        CommandArgumentData var_name_arg;

        // Define the first (and only) variant of this arg.
        var_name_arg.arg_type = eArgTypeVarName;
        var_name_arg.arg_repetition = eArgRepeatPlus;

        // There is only one variant this argument could be; put it into the argument entry.
        arg.push_back (var_name_arg);

        // Push the data for the first argument into the m_arguments vector.
        m_arguments.push_back (arg);

        m_option_group.Append (&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Append (&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Append (&m_option_format, OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_GDB_FMT, LLDB_OPT_SET_1);
        m_option_group.Append (&m_option_compile_units, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);   
        m_option_group.Append (&m_option_shared_libraries, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);   
        m_option_group.Finalize();
    }

    ~CommandObjectTargetVariable() override = default;

    void
    DumpValueObject (Stream &s, VariableSP &var_sp, ValueObjectSP &valobj_sp, const char *root_name)
    {
        DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions());

        if (!valobj_sp->GetTargetSP()->GetDisplayRuntimeSupportValues() &&
            valobj_sp->IsRuntimeSupportValue())
            return;
        
        switch (var_sp->GetScope())
        {
            case eValueTypeVariableGlobal:
                if (m_option_variable.show_scope)
                    s.PutCString("GLOBAL: ");
                break;

            case eValueTypeVariableStatic:
                if (m_option_variable.show_scope)
                    s.PutCString("STATIC: ");
                break;

            case eValueTypeVariableArgument:
                if (m_option_variable.show_scope)
                    s.PutCString("   ARG: ");
                break;

            case eValueTypeVariableLocal:
                if (m_option_variable.show_scope)
                    s.PutCString(" LOCAL: ");
                break;

            case eValueTypeVariableThreadLocal:
                if (m_option_variable.show_scope)
                    s.PutCString("THREAD: ");
                break;

            default:
                break;
        }

        if (m_option_variable.show_decl)
        {
            bool show_fullpaths = false;
            bool show_module = true;
            if (var_sp->DumpDeclaration(&s, show_fullpaths, show_module))
                s.PutCString (": ");
        }

        const Format format = m_option_format.GetFormat();
        if (format != eFormatDefault)
            options.SetFormat(format);

        options.SetRootValueObjectName(root_name);

        valobj_sp->Dump(s,options);
    }

    static size_t GetVariableCallback (void *baton,
                                       const char *name,
                                       VariableList &variable_list)
    {
        Target *target = static_cast<Target *>(baton);
        if (target)
        {
            return target->GetImages().FindGlobalVariables (ConstString(name),
                                                            true, 
                                                            UINT32_MAX, 
                                                            variable_list);
        }
        return 0;
    }

    Options *
    GetOptions () override
    {
        return &m_option_group;
    }

protected:
    void
    DumpGlobalVariableList(const ExecutionContext &exe_ctx, const SymbolContext &sc, const VariableList &variable_list, Stream &s)
    {
        size_t count = variable_list.GetSize();
        if (count > 0)
        {
            if (sc.module_sp)
            {
                if (sc.comp_unit)
                {
                    s.Printf ("Global variables for %s in %s:\n",
                              sc.comp_unit->GetPath().c_str(),
                              sc.module_sp->GetFileSpec().GetPath().c_str());
                }
                else
                {
                    s.Printf ("Global variables for %s\n",
                              sc.module_sp->GetFileSpec().GetPath().c_str());
                }
            }
            else if (sc.comp_unit)
            {
                s.Printf ("Global variables for %s\n",
                          sc.comp_unit->GetPath().c_str());
            }

            for (uint32_t i = 0; i < count; ++i)
            {
                VariableSP var_sp (variable_list.GetVariableAtIndex(i));
                if (var_sp)
                {
                    ValueObjectSP valobj_sp (ValueObjectVariable::Create (exe_ctx.GetBestExecutionContextScope(), var_sp));

                    if (valobj_sp)
                        DumpValueObject (s, var_sp, valobj_sp, var_sp->GetName().GetCString());
                }
            }
        }
    }

    bool
    DoExecute (Args& args, CommandReturnObject &result) override
    {
        Target *target = m_exe_ctx.GetTargetPtr();
        const size_t argc = args.GetArgumentCount();
        Stream &s = result.GetOutputStream();

        if (argc > 0)
        {

            for (size_t idx = 0; idx < argc; ++idx)
            {
                VariableList variable_list;
                ValueObjectList valobj_list;

                const char *arg = args.GetArgumentAtIndex(idx);
                size_t matches = 0;
                bool use_var_name = false;
                if (m_option_variable.use_regex)
                {
                    RegularExpression regex(arg);
                    if (!regex.IsValid ())
                    {
                        result.GetErrorStream().Printf ("error: invalid regular expression: '%s'\n", arg);
                        result.SetStatus (eReturnStatusFailed);
                        return false;
                    }
                    use_var_name = true;
                    matches = target->GetImages().FindGlobalVariables (regex,
                                                                       true, 
                                                                       UINT32_MAX, 
                                                                       variable_list);
                }
                else
                {
                    Error error (Variable::GetValuesForVariableExpressionPath (arg,
                                                                               m_exe_ctx.GetBestExecutionContextScope(),
                                                                               GetVariableCallback,
                                                                               target,
                                                                               variable_list,
                                                                               valobj_list));
                    matches = variable_list.GetSize();
                }

                if (matches == 0)
                {
                    result.GetErrorStream().Printf ("error: can't find global variable '%s'\n", arg);
                    result.SetStatus (eReturnStatusFailed);
                    return false;
                }
                else
                {
                    for (uint32_t global_idx=0; global_idx<matches; ++global_idx)
                    {
                        VariableSP var_sp (variable_list.GetVariableAtIndex(global_idx));
                        if (var_sp)
                        {
                            ValueObjectSP valobj_sp (valobj_list.GetValueObjectAtIndex(global_idx));
                            if (!valobj_sp)
                                valobj_sp = ValueObjectVariable::Create (m_exe_ctx.GetBestExecutionContextScope(), var_sp);

                            if (valobj_sp)
                                DumpValueObject (s, var_sp, valobj_sp, use_var_name ? var_sp->GetName().GetCString() : arg);
                        }
                    }
                }
            }
        }
        else
        {
            const FileSpecList &compile_units = m_option_compile_units.GetOptionValue().GetCurrentValue();
            const FileSpecList &shlibs = m_option_shared_libraries.GetOptionValue().GetCurrentValue();
            SymbolContextList sc_list;
            const size_t num_compile_units = compile_units.GetSize();
            const size_t num_shlibs = shlibs.GetSize();
            if (num_compile_units == 0 && num_shlibs == 0)
            {
                bool success = false;
                StackFrame *frame = m_exe_ctx.GetFramePtr();
                CompileUnit *comp_unit = nullptr;
                if (frame)
                {
                    SymbolContext sc = frame->GetSymbolContext (eSymbolContextCompUnit);
                    if (sc.comp_unit)
                    {
                        const bool can_create = true;
                        VariableListSP comp_unit_varlist_sp (sc.comp_unit->GetVariableList(can_create));
                        if (comp_unit_varlist_sp)
                        {
                            size_t count = comp_unit_varlist_sp->GetSize();
                            if (count > 0)
                            {
                                DumpGlobalVariableList(m_exe_ctx, sc, *comp_unit_varlist_sp, s);
                                success = true;
                            }
                        }
                    }
                }
                if (!success)
                {
                    if (frame)
                    {
                        if (comp_unit)
                            result.AppendErrorWithFormat ("no global variables in current compile unit: %s\n",
                                                          comp_unit->GetPath().c_str());
                        else
                            result.AppendErrorWithFormat ("no debug information for frame %u\n", frame->GetFrameIndex());
                    }
                    else
                        result.AppendError ("'target variable' takes one or more global variable names as arguments\n");
                    result.SetStatus (eReturnStatusFailed);
                }
            }
            else
            {
                SymbolContextList sc_list;
                const bool append = true;
                // We have one or more compile unit or shlib
                if (num_shlibs > 0)
                {
                    for (size_t shlib_idx=0; shlib_idx<num_shlibs; ++shlib_idx)
                    {
                        const FileSpec module_file(shlibs.GetFileSpecAtIndex(shlib_idx));
                        ModuleSpec module_spec (module_file);

                        ModuleSP module_sp (target->GetImages().FindFirstModule(module_spec));
                        if (module_sp)
                        {
                            if (num_compile_units > 0)
                            {
                                for (size_t cu_idx=0; cu_idx<num_compile_units; ++cu_idx)
                                    module_sp->FindCompileUnits(compile_units.GetFileSpecAtIndex(cu_idx), append, sc_list);
                            }
                            else
                            {
                                SymbolContext sc;
                                sc.module_sp = module_sp;
                                sc_list.Append(sc);
                            }
                        }
                        else
                        {
                            // Didn't find matching shlib/module in target...
                            result.AppendErrorWithFormat ("target doesn't contain the specified shared library: %s\n",
                                                          module_file.GetPath().c_str());
                        }
                    }
                }
                else
                {
                    // No shared libraries, we just want to find globals for the compile units files that were specified
                    for (size_t cu_idx=0; cu_idx<num_compile_units; ++cu_idx)
                        target->GetImages().FindCompileUnits(compile_units.GetFileSpecAtIndex(cu_idx), append, sc_list);
                }

                const uint32_t num_scs = sc_list.GetSize();
                if (num_scs > 0)
                {
                    SymbolContext sc;
                    for (uint32_t sc_idx=0; sc_idx<num_scs; ++sc_idx)
                    {
                        if (sc_list.GetContextAtIndex(sc_idx, sc))
                        {
                            if (sc.comp_unit)
                            {
                                const bool can_create = true;
                                VariableListSP comp_unit_varlist_sp (sc.comp_unit->GetVariableList(can_create));
                                if (comp_unit_varlist_sp)
                                    DumpGlobalVariableList(m_exe_ctx, sc, *comp_unit_varlist_sp, s);
                            }
                            else if (sc.module_sp)
                            {
                                // Get all global variables for this module
                                lldb_private::RegularExpression all_globals_regex("."); // Any global with at least one character
                                VariableList variable_list;
                                sc.module_sp->FindGlobalVariables(all_globals_regex, append, UINT32_MAX, variable_list);
                                DumpGlobalVariableList(m_exe_ctx, sc, variable_list, s);
                            }
                        }
                    }
                }
            }
        }

        if (m_interpreter.TruncationWarningNecessary())
        {
            result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(),
                                            m_cmd_name.c_str());
            m_interpreter.TruncationWarningGiven();
        }

        return result.Succeeded();
    }

    OptionGroupOptions m_option_group;
    OptionGroupVariable m_option_variable;
    OptionGroupFormat m_option_format;
    OptionGroupFileList m_option_compile_units;
    OptionGroupFileList m_option_shared_libraries;
    OptionGroupValueObjectDisplay m_varobj_options;
};

#pragma mark CommandObjectTargetModulesSearchPathsAdd

class CommandObjectTargetModulesSearchPathsAdd : public CommandObjectParsed
{
public:
    CommandObjectTargetModulesSearchPathsAdd (CommandInterpreter &interpreter) :
        CommandObjectParsed(interpreter,
                            "target modules search-paths add",
                            "Add new image search paths substitution pairs to the current target.",
                            nullptr)
    {
        CommandArgumentEntry arg;
        CommandArgumentData old_prefix_arg;
        CommandArgumentData new_prefix_arg;

        // Define the first variant of this arg pair.
        old_prefix_arg.arg_type = eArgTypeOldPathPrefix;
        old_prefix_arg.arg_repetition = eArgRepeatPairPlus;

        // Define the first variant of this arg pair.
        new_prefix_arg.arg_type = eArgTypeNewPathPrefix;
        new_prefix_arg.arg_repetition = eArgRepeatPairPlus;

        // There are two required arguments that must always occur together, i.e. an argument "pair".  Because they
        // must always occur together, they are treated as two variants of one argument rather than two independent
        // arguments.  Push them both into the first argument position for m_arguments...

        arg.push_back (old_prefix_arg);
        arg.push_back (new_prefix_arg);

        m_arguments.push_back (arg);
    }

    ~CommandObjectTargetModulesSearchPathsAdd() override = default;

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
        if (target)
        {
            const size_t argc = command.GetArgumentCount();
            if (argc & 1)
            {
                result.AppendError ("add requires an even number of arguments\n");
                result.SetStatus (eReturnStatusFailed);
            }
            else
            {
                for (size_t i = 0; i < argc; i+=2)
                {
                    const char *from = command.GetArgumentAtIndex(i);
                    const char *to = command.GetArgumentAtIndex(i+1);

                    if (from[0] && to[0])
                    {
                        Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
                        if (log)
                        {
                            log->Printf ("target modules search path adding ImageSearchPath pair: '%s' -> '%s'",
                                         from, to);
                        }
                        bool last_pair = ((argc - i) == 2);
                        target->GetImageSearchPathList().Append (ConstString(from),
                                                                 ConstString(to),
                                                                 last_pair); // Notify if this is the last pair
                        result.SetStatus (eReturnStatusSuccessFinishNoResult);
                    }
                    else
                    {
                        if (from[0])
                            result.AppendError ("<path-prefix> can't be empty\n");
                        else
                            result.AppendError ("<new-path-prefix> can't be empty\n");
                        result.SetStatus (eReturnStatusFailed);
                    }
                }
            }
        }
        else
        {
            result.AppendError ("invalid target\n");
            result.SetStatus (eReturnStatusFailed);
        }
        return result.Succeeded();
    }
};

#pragma mark CommandObjectTargetModulesSearchPathsClear

class CommandObjectTargetModulesSearchPathsClear : public CommandObjectParsed
{
public:
    CommandObjectTargetModulesSearchPathsClear (CommandInterpreter &interpreter) :
        CommandObjectParsed (interpreter,
                             "target modules search-paths clear",
                             "Clear all current image search path substitution pairs from the current target.",
                             "target modules search-paths clear")
    {
    }

    ~CommandObjectTargetModulesSearchPathsClear() override = default;

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
        if (target)
        {
            bool notify = true;
            target->GetImageSearchPathList().Clear(notify);
            result.SetStatus (eReturnStatusSuccessFinishNoResult);
        }
        else
        {
            result.AppendError ("invalid target\n");
            result.SetStatus (eReturnStatusFailed);
        }
        return result.Succeeded();
    }
};

#pragma mark CommandObjectTargetModulesSearchPathsInsert

class CommandObjectTargetModulesSearchPathsInsert : public CommandObjectParsed
{
public:
    CommandObjectTargetModulesSearchPathsInsert (CommandInterpreter &interpreter) :
        CommandObjectParsed(interpreter,
                            "target modules search-paths insert",
                            "Insert a new image search path substitution pair into the current target at the specified index.",
                            nullptr)
    {
        CommandArgumentEntry arg1;
        CommandArgumentEntry arg2;
        CommandArgumentData index_arg;
        CommandArgumentData old_prefix_arg;
        CommandArgumentData new_prefix_arg;

        // Define the first and only variant of this arg.
        index_arg.arg_type = eArgTypeIndex;
        index_arg.arg_repetition = eArgRepeatPlain;

        // Put the one and only variant into the first arg for m_arguments:
        arg1.push_back (index_arg);

        // Define the first variant of this arg pair.
        old_prefix_arg.arg_type = eArgTypeOldPathPrefix;
        old_prefix_arg.arg_repetition = eArgRepeatPairPlus;

        // Define the first variant of this arg pair.
        new_prefix_arg.arg_type = eArgTypeNewPathPrefix;
        new_prefix_arg.arg_repetition = eArgRepeatPairPlus;

        // There are two required arguments that must always occur together, i.e. an argument "pair".  Because they
        // must always occur together, they are treated as two variants of one argument rather than two independent
        // arguments.  Push them both into the same argument position for m_arguments...

        arg2.push_back (old_prefix_arg);
        arg2.push_back (new_prefix_arg);

        // Add arguments to m_arguments.
        m_arguments.push_back (arg1);
        m_arguments.push_back (arg2);
    }

    ~CommandObjectTargetModulesSearchPathsInsert() override = default;

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
        if (target)
        {
            size_t argc = command.GetArgumentCount();
            // check for at least 3 arguments and an odd number of parameters
            if (argc >= 3 && argc & 1)
            {
                bool success = false;

                uint32_t insert_idx = StringConvert::ToUInt32(command.GetArgumentAtIndex(0), UINT32_MAX, 0, &success);

                if (!success)
                {
                    result.AppendErrorWithFormat("<index> parameter is not an integer: '%s'.\n", command.GetArgumentAtIndex(0));
                    result.SetStatus (eReturnStatusFailed);
                    return result.Succeeded();
                }

                // shift off the index
                command.Shift();
                argc = command.GetArgumentCount();

                for (uint32_t i = 0; i < argc; i += 2, ++insert_idx)
                {
                    const char *from = command.GetArgumentAtIndex(i);
                    const char *to = command.GetArgumentAtIndex(i+1);

                    if (from[0] && to[0])
                    {
                        bool last_pair = ((argc - i) == 2);
                        target->GetImageSearchPathList().Insert (ConstString(from),
                                                                 ConstString(to),
                                                                 insert_idx,
                                                                 last_pair);
                        result.SetStatus (eReturnStatusSuccessFinishNoResult);
                    }
                    else
                    {
                        if (from[0])
                            result.AppendError ("<path-prefix> can't be empty\n");
                        else
                            result.AppendError ("<new-path-prefix> can't be empty\n");
                        result.SetStatus (eReturnStatusFailed);
                        return false;
                    }
                }
            }
            else
            {
                result.AppendError ("insert requires at least three arguments\n");
                result.SetStatus (eReturnStatusFailed);
                return result.Succeeded();
            }

        }
        else
        {
            result.AppendError ("invalid target\n");
            result.SetStatus (eReturnStatusFailed);
        }
        return result.Succeeded();
    }
};

#pragma mark CommandObjectTargetModulesSearchPathsList

class CommandObjectTargetModulesSearchPathsList : public CommandObjectParsed
{
public:
    CommandObjectTargetModulesSearchPathsList (CommandInterpreter &interpreter) :
        CommandObjectParsed (interpreter,
                             "target modules search-paths list",
                             "List all current image search path substitution pairs in the current target.",
                             "target modules search-paths list")
    {
    }

    ~CommandObjectTargetModulesSearchPathsList() override = default;

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
        if (target)
        {
            if (command.GetArgumentCount() != 0)
            {
                result.AppendError ("list takes no arguments\n");
                result.SetStatus (eReturnStatusFailed);
                return result.Succeeded();
            }

            target->GetImageSearchPathList().Dump(&result.GetOutputStream());
            result.SetStatus (eReturnStatusSuccessFinishResult);
        }
        else
        {
            result.AppendError ("invalid target\n");
            result.SetStatus (eReturnStatusFailed);
        }
        return result.Succeeded();
    }
};

#pragma mark CommandObjectTargetModulesSearchPathsQuery

class CommandObjectTargetModulesSearchPathsQuery : public CommandObjectParsed
{
public:
    CommandObjectTargetModulesSearchPathsQuery (CommandInterpreter &interpreter) :
        CommandObjectParsed(interpreter,
                            "target modules search-paths query",
                            "Transform a path using the first applicable image search path.",
                            nullptr)
    {
        CommandArgumentEntry arg;
        CommandArgumentData path_arg;

        // Define the first (and only) variant of this arg.
        path_arg.arg_type = eArgTypeDirectoryName;
        path_arg.arg_repetition = eArgRepeatPlain;

        // There is only one variant this argument could be; put it into the argument entry.
        arg.push_back (path_arg);

        // Push the data for the first argument into the m_arguments vector.
        m_arguments.push_back (arg);
    }

    ~CommandObjectTargetModulesSearchPathsQuery() override = default;

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
        if (target)
        {
            if (command.GetArgumentCount() != 1)
            {
                result.AppendError ("query requires one argument\n");
                result.SetStatus (eReturnStatusFailed);
                return result.Succeeded();
            }

            ConstString orig(command.GetArgumentAtIndex(0));
            ConstString transformed;
            if (target->GetImageSearchPathList().RemapPath(orig, transformed))
                result.GetOutputStream().Printf("%s\n", transformed.GetCString());
            else
                result.GetOutputStream().Printf("%s\n", orig.GetCString());

            result.SetStatus (eReturnStatusSuccessFinishResult);
        }
        else
        {
            result.AppendError ("invalid target\n");
            result.SetStatus (eReturnStatusFailed);
        }
        return result.Succeeded();
    }
};

//----------------------------------------------------------------------
// Static Helper functions
//----------------------------------------------------------------------
static void
DumpModuleArchitecture (Stream &strm, Module *module, bool full_triple, uint32_t width)
{
    if (module)
    {
        StreamString arch_strm;

        if (full_triple)
            module->GetArchitecture().DumpTriple(arch_strm);
        else
            arch_strm.PutCString(module->GetArchitecture().GetArchitectureName());
        std::string arch_str = arch_strm.GetString();

        if (width)
            strm.Printf("%-*s", width, arch_str.c_str());
        else
            strm.PutCString(arch_str.c_str());
    }
}

static void
DumpModuleUUID (Stream &strm, Module *module)
{
    if (module && module->GetUUID().IsValid())
        module->GetUUID().Dump (&strm);
    else
        strm.PutCString("                                    ");
}

static uint32_t
DumpCompileUnitLineTable (CommandInterpreter &interpreter,
                          Stream &strm,
                          Module *module,
                          const FileSpec &file_spec,
                          bool load_addresses)
{
    uint32_t num_matches = 0;
    if (module)
    {
        SymbolContextList sc_list;
        num_matches = module->ResolveSymbolContextsForFileSpec (file_spec,
                                                                0,
                                                                false,
                                                                eSymbolContextCompUnit,
                                                                sc_list);

        for (uint32_t i = 0; i < num_matches; ++i)
        {
            SymbolContext sc;
            if (sc_list.GetContextAtIndex(i, sc))
            {
                if (i > 0)
                    strm << "\n\n";

                strm << "Line table for " << *static_cast<FileSpec*> (sc.comp_unit) << " in `"
                << module->GetFileSpec().GetFilename() << "\n";
                LineTable *line_table = sc.comp_unit->GetLineTable();
                if (line_table)
                    line_table->GetDescription (&strm, 
                                                interpreter.GetExecutionContext().GetTargetPtr(), 
                                                lldb::eDescriptionLevelBrief);
                else
                    strm << "No line table";
            }
        }
    }
    return num_matches;
}

static void
DumpFullpath (Stream &strm, const FileSpec *file_spec_ptr, uint32_t width)
{
    if (file_spec_ptr)
    {
        if (width > 0)
        {
            std::string fullpath = file_spec_ptr->GetPath();
            strm.Printf("%-*s", width, fullpath.c_str());
            return;
        }
        else
        {
            file_spec_ptr->Dump(&strm);
            return;
        }
    }
    // Keep the width spacing correct if things go wrong...
    if (width > 0)
        strm.Printf("%-*s", width, "");
}

static void
DumpDirectory (Stream &strm, const FileSpec *file_spec_ptr, uint32_t width)
{
    if (file_spec_ptr)
    {
        if (width > 0)
            strm.Printf("%-*s", width, file_spec_ptr->GetDirectory().AsCString(""));
        else
            file_spec_ptr->GetDirectory().Dump(&strm);
        return;
    }
    // Keep the width spacing correct if things go wrong...
    if (width > 0)
        strm.Printf("%-*s", width, "");
}

static void
DumpBasename (Stream &strm, const FileSpec *file_spec_ptr, uint32_t width)
{
    if (file_spec_ptr)
    {
        if (width > 0)
            strm.Printf("%-*s", width, file_spec_ptr->GetFilename().AsCString(""));
        else
            file_spec_ptr->GetFilename().Dump(&strm);
        return;
    }
    // Keep the width spacing correct if things go wrong...
    if (width > 0)
        strm.Printf("%-*s", width, "");
}

static size_t
DumpModuleObjfileHeaders(Stream &strm, ModuleList &module_list)
{
    size_t num_dumped = 0;
    std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
    const size_t num_modules = module_list.GetSize();
    if (num_modules > 0)
    {
        strm.Printf("Dumping headers for %" PRIu64 " module(s).\n", static_cast<uint64_t>(num_modules));
        strm.IndentMore();
        for (size_t image_idx = 0; image_idx < num_modules; ++image_idx)
        {
            Module *module = module_list.GetModulePointerAtIndexUnlocked(image_idx);
            if (module)
            {
                if (num_dumped++ > 0)
                {
                    strm.EOL();
                    strm.EOL();
                }
                ObjectFile *objfile = module->GetObjectFile();
                objfile->Dump(&strm);
            }
        }
        strm.IndentLess();
    }
    return num_dumped;
}

static void
DumpModuleSymtab (CommandInterpreter &interpreter, Stream &strm, Module *module, SortOrder sort_order)
{
    if (module)
    {
        SymbolVendor *sym_vendor = module->GetSymbolVendor ();
        if (sym_vendor)
        {
            Symtab *symtab = sym_vendor->GetSymtab();
            if (symtab)
                symtab->Dump(&strm, interpreter.GetExecutionContext().GetTargetPtr(), sort_order);
        }
    }
}

static void
DumpModuleSections (CommandInterpreter &interpreter, Stream &strm, Module *module)
{
    if (module)
    {
        SectionList *section_list = module->GetSectionList();
        if (section_list)
        {
            strm.Printf ("Sections for '%s' (%s):\n",
                         module->GetSpecificationDescription().c_str(),
                         module->GetArchitecture().GetArchitectureName());
            strm.IndentMore();
            section_list->Dump(&strm, interpreter.GetExecutionContext().GetTargetPtr(), true, UINT32_MAX);
            strm.IndentLess();
        }
    }
}

static bool
DumpModuleSymbolVendor (Stream &strm, Module *module)
{
    if (module)
    {
        SymbolVendor *symbol_vendor = module->GetSymbolVendor(true);
        if (symbol_vendor)
        {
            symbol_vendor->Dump(&strm);
            return true;
        }
    }
    return false;
}

static void
DumpAddress (ExecutionContextScope *exe_scope, const Address &so_addr, bool verbose, Stream &strm)
{
    strm.IndentMore();
    strm.Indent ("    Address: ");
    so_addr.Dump (&strm, exe_scope, Address::DumpStyleModuleWithFileAddress);
    strm.PutCString (" (");
    so_addr.Dump (&strm, exe_scope, Address::DumpStyleSectionNameOffset);
    strm.PutCString (")\n");
    strm.Indent ("    Summary: ");
    const uint32_t save_indent = strm.GetIndentLevel ();
    strm.SetIndentLevel (save_indent + 13);
    so_addr.Dump (&strm, exe_scope, Address::DumpStyleResolvedDescription);
    strm.SetIndentLevel (save_indent);
    // Print out detailed address information when verbose is enabled
    if (verbose)
    {
        strm.EOL();
        so_addr.Dump (&strm, exe_scope, Address::DumpStyleDetailedSymbolContext);
    }
    strm.IndentLess();
}

static bool
LookupAddressInModule (CommandInterpreter &interpreter, 
                       Stream &strm, 
                       Module *module, 
                       uint32_t resolve_mask, 
                       lldb::addr_t raw_addr, 
                       lldb::addr_t offset,
                       bool verbose)
{
    if (module)
    {
        lldb::addr_t addr = raw_addr - offset;
        Address so_addr;
        SymbolContext sc;
        Target *target = interpreter.GetExecutionContext().GetTargetPtr();
        if (target && !target->GetSectionLoadList().IsEmpty())
        {
            if (!target->GetSectionLoadList().ResolveLoadAddress (addr, so_addr))
                return false;
            else if (so_addr.GetModule().get() != module)
                return false;
        }
        else
        {
            if (!module->ResolveFileAddress (addr, so_addr))
                return false;
        }

        ExecutionContextScope *exe_scope = interpreter.GetExecutionContext().GetBestExecutionContextScope();
        DumpAddress (exe_scope, so_addr, verbose, strm);
//        strm.IndentMore();
//        strm.Indent ("    Address: ");
//        so_addr.Dump (&strm, exe_scope, Address::DumpStyleModuleWithFileAddress);
//        strm.PutCString (" (");
//        so_addr.Dump (&strm, exe_scope, Address::DumpStyleSectionNameOffset);
//        strm.PutCString (")\n");
//        strm.Indent ("    Summary: ");
//        const uint32_t save_indent = strm.GetIndentLevel ();
//        strm.SetIndentLevel (save_indent + 13);
//        so_addr.Dump (&strm, exe_scope, Address::DumpStyleResolvedDescription);
//        strm.SetIndentLevel (save_indent);
//        // Print out detailed address information when verbose is enabled
//        if (verbose)
//        {
//            strm.EOL();
//            so_addr.Dump (&strm, exe_scope, Address::DumpStyleDetailedSymbolContext);
//        }
//        strm.IndentLess();
        return true;
    }

    return false;
}

static uint32_t
LookupSymbolInModule (CommandInterpreter &interpreter, Stream &strm, Module *module, const char *name, bool name_is_regex, bool verbose)
{
    if (module)
    {
        SymbolContext sc;

        SymbolVendor *sym_vendor = module->GetSymbolVendor ();
        if (sym_vendor)
        {
            Symtab *symtab = sym_vendor->GetSymtab();
            if (symtab)
            {
                std::vector<uint32_t> match_indexes;
                ConstString symbol_name (name);
                uint32_t num_matches = 0;
                if (name_is_regex)
                {
                    RegularExpression name_regexp(name);
                    num_matches = symtab->AppendSymbolIndexesMatchingRegExAndType (name_regexp, 
                                                                                   eSymbolTypeAny,
                                                                                   match_indexes);
                }
                else
                {
                    num_matches = symtab->AppendSymbolIndexesWithName (symbol_name, match_indexes);
                }

                if (num_matches > 0)
                {
                    strm.Indent ();
                    strm.Printf("%u symbols match %s'%s' in ", num_matches,
                                name_is_regex ? "the regular expression " : "", name);
                    DumpFullpath (strm, &module->GetFileSpec(), 0);
                    strm.PutCString(":\n");
                    strm.IndentMore ();
                    for (uint32_t i = 0; i < num_matches; ++i)
                    {
                        Symbol *symbol = symtab->SymbolAtIndex(match_indexes[i]);
                        if (symbol && symbol->ValueIsAddress())
                        {
                            DumpAddress (interpreter.GetExecutionContext().GetBestExecutionContextScope(),
                                         symbol->GetAddressRef(),
                                         verbose,
                                         strm);
                        }
                    }
                    strm.IndentLess ();
                    return num_matches;
                }
            }
        }
    }
    return 0;
}

static void
DumpSymbolContextList (ExecutionContextScope *exe_scope, Stream &strm, SymbolContextList &sc_list, bool verbose)
{
    strm.IndentMore ();

    const uint32_t num_matches = sc_list.GetSize();

    for (uint32_t i = 0; i < num_matches; ++i)
    {
        SymbolContext sc;
        if (sc_list.GetContextAtIndex(i, sc))
        {
            AddressRange range;

            sc.GetAddressRange(eSymbolContextEverything, 
                               0, 
                               true, 
                               range);

            DumpAddress (exe_scope, range.GetBaseAddress(), verbose, strm);
        }
    }
    strm.IndentLess ();
}

static size_t
LookupFunctionInModule (CommandInterpreter &interpreter,
                        Stream &strm,
                        Module *module,
                        const char *name,
                        bool name_is_regex,
                        bool include_inlines,
                        bool include_symbols,
                        bool verbose)
{
    if (module && name && name[0])
    {
        SymbolContextList sc_list;
        const bool append = true;
        size_t num_matches = 0;
        if (name_is_regex)
        {
            RegularExpression function_name_regex (name);
            num_matches = module->FindFunctions (function_name_regex, 
                                                 include_symbols,
                                                 include_inlines, 
                                                 append, 
                                                 sc_list);
        }
        else
        {
            ConstString function_name (name);
            num_matches = module->FindFunctions(function_name,
                                                nullptr,
                                                eFunctionNameTypeAuto,
                                                include_symbols,
                                                include_inlines,
                                                append, 
                                                sc_list);
        }

        if (num_matches)
        {
            strm.Indent ();
            strm.Printf("%" PRIu64 " match%s found in ", (uint64_t)num_matches, num_matches > 1 ? "es" : "");
            DumpFullpath (strm, &module->GetFileSpec(), 0);
            strm.PutCString(":\n");
            DumpSymbolContextList (interpreter.GetExecutionContext().GetBestExecutionContextScope(), strm, sc_list, verbose);
        }
        return num_matches;
    }
    return 0;
}

static size_t
LookupTypeInModule (CommandInterpreter &interpreter,
                    Stream &strm, 
                    Module *module, 
                    const char *name_cstr, 
                    bool name_is_regex)
{
    if (module && name_cstr && name_cstr[0])
    {
        TypeList type_list;
        const uint32_t max_num_matches = UINT32_MAX;
        size_t num_matches = 0;
        bool name_is_fully_qualified = false;
        SymbolContext sc;

        ConstString name(name_cstr);
        llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files;
        num_matches = module->FindTypes(sc, name, name_is_fully_qualified, max_num_matches, searched_symbol_files, type_list);

        if (num_matches)
        {
            strm.Indent ();
            strm.Printf("%" PRIu64 " match%s found in ", (uint64_t)num_matches, num_matches > 1 ? "es" : "");
            DumpFullpath (strm, &module->GetFileSpec(), 0);
            strm.PutCString(":\n");
            for (TypeSP type_sp : type_list.Types())
            {
                if (type_sp)
                {
                    // Resolve the clang type so that any forward references
                    // to types that haven't yet been parsed will get parsed.
                    type_sp->GetFullCompilerType ();
                    type_sp->GetDescription (&strm, eDescriptionLevelFull, true);
                    // Print all typedef chains
                    TypeSP typedef_type_sp (type_sp);
                    TypeSP typedefed_type_sp (typedef_type_sp->GetTypedefType());
                    while (typedefed_type_sp)
                    {
                        strm.EOL();
                        strm.Printf("     typedef '%s': ", typedef_type_sp->GetName().GetCString());
                        typedefed_type_sp->GetFullCompilerType ();
                        typedefed_type_sp->GetDescription (&strm, eDescriptionLevelFull, true);
                        typedef_type_sp = typedefed_type_sp;
                        typedefed_type_sp = typedef_type_sp->GetTypedefType();
                    }
                }
                strm.EOL();
            }
        }
        return num_matches;
    }
    return 0;
}

static size_t
LookupTypeHere (CommandInterpreter &interpreter,
                Stream &strm,
                const SymbolContext &sym_ctx,
                const char *name_cstr, 
                bool name_is_regex)
{
    if (!sym_ctx.module_sp)
        return 0;

    TypeList type_list;
    const uint32_t max_num_matches = UINT32_MAX;
    size_t num_matches = 1;
    bool name_is_fully_qualified = false;

    ConstString name(name_cstr);
    llvm::DenseSet<SymbolFile *> searched_symbol_files;
    num_matches = sym_ctx.module_sp->FindTypes(sym_ctx, name, name_is_fully_qualified, max_num_matches, searched_symbol_files, type_list);

    if (num_matches)
    {
        strm.Indent ();
        strm.PutCString("Best match found in ");
        DumpFullpath (strm, &sym_ctx.module_sp->GetFileSpec(), 0);
        strm.PutCString(":\n");

        TypeSP type_sp (type_list.GetTypeAtIndex(0));
        if (type_sp)
        {
            // Resolve the clang type so that any forward references
            // to types that haven't yet been parsed will get parsed.
            type_sp->GetFullCompilerType ();
            type_sp->GetDescription (&strm, eDescriptionLevelFull, true);
            // Print all typedef chains
            TypeSP typedef_type_sp (type_sp);
            TypeSP typedefed_type_sp (typedef_type_sp->GetTypedefType());
            while (typedefed_type_sp)
            {
                strm.EOL();
                strm.Printf("     typedef '%s': ", typedef_type_sp->GetName().GetCString());
                typedefed_type_sp->GetFullCompilerType ();
                typedefed_type_sp->GetDescription (&strm, eDescriptionLevelFull, true);
                typedef_type_sp = typedefed_type_sp;
                typedefed_type_sp = typedef_type_sp->GetTypedefType();
            }
        }
        strm.EOL();
    }
    return num_matches;
}

static uint32_t
LookupFileAndLineInModule (CommandInterpreter &interpreter, 
                           Stream &strm,
                           Module *module, 
                           const FileSpec &file_spec, 
                           uint32_t line, 
                           bool check_inlines,
                           bool verbose)
{
    if (module && file_spec)
    {
        SymbolContextList sc_list;
        const uint32_t num_matches = module->ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines,
                                                                              eSymbolContextEverything, sc_list);
        if (num_matches > 0)
        {
            strm.Indent ();
            strm.Printf("%u match%s found in ", num_matches, num_matches > 1 ? "es" : "");
            strm << file_spec;
            if (line > 0)
                strm.Printf (":%u", line);
            strm << " in ";
            DumpFullpath (strm, &module->GetFileSpec(), 0);
            strm.PutCString(":\n");
            DumpSymbolContextList (interpreter.GetExecutionContext().GetBestExecutionContextScope(), strm, sc_list, verbose);
            return num_matches;
        }
    }
    return 0;
}

static size_t
FindModulesByName (Target *target, 
                   const char *module_name, 
                   ModuleList &module_list, 
                   bool check_global_list)
{
    FileSpec module_file_spec(module_name, false);
    ModuleSpec module_spec (module_file_spec);

    const size_t initial_size = module_list.GetSize ();

    if (check_global_list)
    {
        // Check the global list
        std::lock_guard<std::recursive_mutex> guard(Module::GetAllocationModuleCollectionMutex());
        const size_t num_modules = Module::GetNumberAllocatedModules();
        ModuleSP module_sp;
        for (size_t image_idx = 0; image_idx<num_modules; ++image_idx)
        {
            Module *module = Module::GetAllocatedModuleAtIndex(image_idx);

            if (module)
            {
                if (module->MatchesModuleSpec (module_spec))
                {
                    module_sp = module->shared_from_this();
                    module_list.AppendIfNeeded(module_sp);
                }
            }
        }
    }
    else
    {
        if (target)
        {
            const size_t num_matches = target->GetImages().FindModules (module_spec, module_list);

            // Not found in our module list for our target, check the main
            // shared module list in case it is a extra file used somewhere
            // else
            if (num_matches == 0)
            {
                module_spec.GetArchitecture() = target->GetArchitecture();
                ModuleList::FindSharedModules (module_spec, module_list);
            }
        }
        else
        {
            ModuleList::FindSharedModules (module_spec,module_list);
        }
    }

    return module_list.GetSize () - initial_size;
}

#pragma mark CommandObjectTargetModulesModuleAutoComplete

//----------------------------------------------------------------------
// A base command object class that can auto complete with module file
// paths
//----------------------------------------------------------------------

class CommandObjectTargetModulesModuleAutoComplete : public CommandObjectParsed
{
public:
    CommandObjectTargetModulesModuleAutoComplete (CommandInterpreter &interpreter,
                                      const char *name,
                                      const char *help,
                                      const char *syntax) :
        CommandObjectParsed (interpreter, name, help, syntax)
    {
        CommandArgumentEntry arg;
        CommandArgumentData file_arg;

        // Define the first (and only) variant of this arg.
        file_arg.arg_type = eArgTypeFilename;
        file_arg.arg_repetition = eArgRepeatStar;

        // There is only one variant this argument could be; put it into the argument entry.
        arg.push_back (file_arg);

        // Push the data for the first argument into the m_arguments vector.
        m_arguments.push_back (arg);
    }

    ~CommandObjectTargetModulesModuleAutoComplete() override = default;

    int
    HandleArgumentCompletion (Args &input,
                              int &cursor_index,
                              int &cursor_char_position,
                              OptionElementVector &opt_element_vector,
                              int match_start_point,
                              int max_return_elements,
                              bool &word_complete,
                              StringList &matches) override
    {
        // Arguments are the standard module completer.
        std::string completion_str (input.GetArgumentAtIndex(cursor_index));
        completion_str.erase (cursor_char_position);

        CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter,
                                                            CommandCompletions::eModuleCompletion,
                                                            completion_str.c_str(),
                                                            match_start_point,
                                                            max_return_elements,
                                                            nullptr,
                                                            word_complete,
                                                            matches);
        return matches.GetSize();
    }
};

#pragma mark CommandObjectTargetModulesSourceFileAutoComplete

//----------------------------------------------------------------------
// A base command object class that can auto complete with module source
// file paths
//----------------------------------------------------------------------

class CommandObjectTargetModulesSourceFileAutoComplete : public CommandObjectParsed
{
public:
    CommandObjectTargetModulesSourceFileAutoComplete (CommandInterpreter &interpreter,
                                                      const char *name,
                                                      const char *help,
                                                      const char *syntax,
                                                      uint32_t flags) :
        CommandObjectParsed (interpreter, name, help, syntax, flags)
    {
        CommandArgumentEntry arg;
        CommandArgumentData source_file_arg;

        // Define the first (and only) variant of this arg.
        source_file_arg.arg_type = eArgTypeSourceFile;
        source_file_arg.arg_repetition = eArgRepeatPlus;

        // There is only one variant this argument could be; put it into the argument entry.
        arg.push_back (source_file_arg);

        // Push the data for the first argument into the m_arguments vector.
        m_arguments.push_back (arg);
    }

    ~CommandObjectTargetModulesSourceFileAutoComplete() override = default;

    int
    HandleArgumentCompletion (Args &input,
                              int &cursor_index,
                              int &cursor_char_position,
                              OptionElementVector &opt_element_vector,
                              int match_start_point,
                              int max_return_elements,
                              bool &word_complete,
                              StringList &matches) override
    {
        // Arguments are the standard source file completer.
        std::string completion_str (input.GetArgumentAtIndex(cursor_index));
        completion_str.erase (cursor_char_position);

        CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter,
                                                            CommandCompletions::eSourceFileCompletion,
                                                            completion_str.c_str(),
                                                            match_start_point,
                                                            max_return_elements,
                                                            nullptr,
                                                            word_complete,
                                                            matches);
        return matches.GetSize();
    }
};

#pragma mark CommandObjectTargetModulesDumpObjfile

class CommandObjectTargetModulesDumpObjfile : public CommandObjectTargetModulesModuleAutoComplete
{
public:
    CommandObjectTargetModulesDumpObjfile(CommandInterpreter &interpreter)
        : CommandObjectTargetModulesModuleAutoComplete(interpreter, "target modules dump objfile",
                                                       "Dump the object file headers from one or more target modules.",
                                                       nullptr)
    {
    }

    ~CommandObjectTargetModulesDumpObjfile() override = default;

protected:
    bool
    DoExecute(Args &command, CommandReturnObject &result) override
    {
        Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
        if (target == nullptr)
        {
            result.AppendError("invalid target, create a debug target using the 'target create' command");
            result.SetStatus(eReturnStatusFailed);
            return false;
        }

        uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
        result.GetOutputStream().SetAddressByteSize(addr_byte_size);
        result.GetErrorStream().SetAddressByteSize(addr_byte_size);

        size_t num_dumped = 0;
        if (command.GetArgumentCount() == 0)
        {
            // Dump all headers for all modules images
            num_dumped = DumpModuleObjfileHeaders(result.GetOutputStream(), target->GetImages());
            if (num_dumped == 0)
            {
                result.AppendError("the target has no associated executable images");
                result.SetStatus(eReturnStatusFailed);
            }
        }
        else
        {
            // Find the modules that match the basename or full path.
            ModuleList module_list;
            const char *arg_cstr;
            for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr; ++arg_idx)
            {
                size_t num_matched = FindModulesByName(target, arg_cstr, module_list, true);
                if (num_matched == 0)
                {
                    result.AppendWarningWithFormat("Unable to find an image that matches '%s'.\n", arg_cstr);
                }
            }
            // Dump all the modules we found.
            num_dumped = DumpModuleObjfileHeaders(result.GetOutputStream(), module_list);
        }

        if (num_dumped > 0)
        {
            result.SetStatus(eReturnStatusSuccessFinishResult);
        }
        else
        {
            result.AppendError("no matching executable images found");
            result.SetStatus(eReturnStatusFailed);
        }
        return result.Succeeded();
    }
};

#pragma mark CommandObjectTargetModulesDumpSymtab

class CommandObjectTargetModulesDumpSymtab : public CommandObjectTargetModulesModuleAutoComplete
{
public:
    CommandObjectTargetModulesDumpSymtab (CommandInterpreter &interpreter) :
        CommandObjectTargetModulesModuleAutoComplete(interpreter,
                                                     "target modules dump symtab",
                                                     "Dump the symbol table from one or more target modules.",
                                                     nullptr),
        m_options(interpreter)
    {
    }

    ~CommandObjectTargetModulesDumpSymtab() override = default;

    Options *
    GetOptions () override
    {
        return &m_options;
    }

    class CommandOptions : public Options
    {
    public:
        CommandOptions (CommandInterpreter &interpreter) :
        Options(interpreter),
        m_sort_order (eSortOrderNone)
        {
        }

        ~CommandOptions() override = default;

        Error
        SetOptionValue (uint32_t option_idx, const char *option_arg) override
        {
            Error error;
            const int short_option = m_getopt_table[option_idx].val;

            switch (short_option)
            {
                case 's':
                    m_sort_order = (SortOrder) Args::StringToOptionEnum (option_arg, 
                                                                         g_option_table[option_idx].enum_values, 
                                                                         eSortOrderNone,
                                                                         error);
                    break;

                default:
                    error.SetErrorStringWithFormat("invalid short option character '%c'", short_option);
                    break;
            }
            return error;
        }

        void
        OptionParsingStarting () override
        {
            m_sort_order = eSortOrderNone;
        }

        const OptionDefinition*
        GetDefinitions () override
        {
            return g_option_table;
        }

        // Options table: Required for subclasses of Options.
        static OptionDefinition g_option_table[];

        SortOrder m_sort_order;
    };

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
        if (target == nullptr)
        {
            result.AppendError ("invalid target, create a debug target using the 'target create' command");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }
        else
        {
            uint32_t num_dumped = 0;

            uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
            result.GetOutputStream().SetAddressByteSize(addr_byte_size);
            result.GetErrorStream().SetAddressByteSize(addr_byte_size);

            if (command.GetArgumentCount() == 0)
            {
                // Dump all sections for all modules images
                std::lock_guard<std::recursive_mutex> guard(target->GetImages().GetMutex());
                const size_t num_modules = target->GetImages().GetSize();
                if (num_modules > 0)
                {
                    result.GetOutputStream().Printf("Dumping symbol table for %" PRIu64 " modules.\n", (uint64_t)num_modules);
                    for (size_t image_idx = 0; image_idx<num_modules; ++image_idx)
                    {
                        if (num_dumped > 0)
                        {
                            result.GetOutputStream().EOL();
                            result.GetOutputStream().EOL();
                        }
                        num_dumped++;
                        DumpModuleSymtab (m_interpreter,
                                          result.GetOutputStream(),
                                          target->GetImages().GetModulePointerAtIndexUnlocked(image_idx),
                                          m_options.m_sort_order);
                    }
                }
                else
                {
                    result.AppendError ("the target has no associated executable images");
                    result.SetStatus (eReturnStatusFailed);
                    return false;
                }
            }
            else
            {
                // Dump specified images (by basename or fullpath)
                const char *arg_cstr;
                for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr; ++arg_idx)
                {
                    ModuleList module_list;
                    const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, true);
                    if (num_matches > 0)
                    {
                        for (size_t i = 0; i < num_matches; ++i)
                        {
                            Module *module = module_list.GetModulePointerAtIndex(i);
                            if (module)
                            {
                                if (num_dumped > 0)
                                {
                                    result.GetOutputStream().EOL();
                                    result.GetOutputStream().EOL();
                                }
                                num_dumped++;
                                DumpModuleSymtab (m_interpreter, result.GetOutputStream(), module, m_options.m_sort_order);
                            }
                        }
                    }
                    else
                        result.AppendWarningWithFormat("Unable to find an image that matches '%s'.\n", arg_cstr);
                }
            }

            if (num_dumped > 0)
                result.SetStatus (eReturnStatusSuccessFinishResult);
            else
            {
                result.AppendError ("no matching executable images found");
                result.SetStatus (eReturnStatusFailed);
            }
        }
        return result.Succeeded();
    }

    CommandOptions m_options;
};

static OptionEnumValueElement
g_sort_option_enumeration[4] =
{
    { eSortOrderNone,       "none",     "No sorting, use the original symbol table order."},
    { eSortOrderByAddress,  "address",  "Sort output by symbol address."},
    { eSortOrderByName,     "name",     "Sort output by symbol name."},
    { 0,                    nullptr,    nullptr }
};

OptionDefinition
CommandObjectTargetModulesDumpSymtab::CommandOptions::g_option_table[] =
{
    { LLDB_OPT_SET_1, false, "sort", 's', OptionParser::eRequiredArgument, nullptr, g_sort_option_enumeration, 0, eArgTypeSortOrder, "Supply a sort order when dumping the symbol table."},
    { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr }
};

#pragma mark CommandObjectTargetModulesDumpSections

//----------------------------------------------------------------------
// Image section dumping command
//----------------------------------------------------------------------

class CommandObjectTargetModulesDumpSections : public CommandObjectTargetModulesModuleAutoComplete
{
public:
    CommandObjectTargetModulesDumpSections (CommandInterpreter &interpreter) :
        CommandObjectTargetModulesModuleAutoComplete(interpreter,
                                                     "target modules dump sections",
                                                     "Dump the sections from one or more target modules.",
                                                     //"target modules dump sections [<file1> ...]")
                                                     nullptr)
    {
    }

    ~CommandObjectTargetModulesDumpSections() override = default;

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
        if (target == nullptr)
        {
            result.AppendError ("invalid target, create a debug target using the 'target create' command");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }
        else
        {
            uint32_t num_dumped = 0;

            uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
            result.GetOutputStream().SetAddressByteSize(addr_byte_size);
            result.GetErrorStream().SetAddressByteSize(addr_byte_size);

            if (command.GetArgumentCount() == 0)
            {
                // Dump all sections for all modules images
                const size_t num_modules = target->GetImages().GetSize();
                if (num_modules > 0)
                {
                    result.GetOutputStream().Printf("Dumping sections for %" PRIu64 " modules.\n", (uint64_t)num_modules);
                    for (size_t image_idx = 0;  image_idx<num_modules; ++image_idx)
                    {
                        num_dumped++;
                        DumpModuleSections (m_interpreter, result.GetOutputStream(), target->GetImages().GetModulePointerAtIndex(image_idx));
                    }
                }
                else
                {
                    result.AppendError ("the target has no associated executable images");
                    result.SetStatus (eReturnStatusFailed);
                    return false;
                }
            }
            else
            {
                // Dump specified images (by basename or fullpath)
                const char *arg_cstr;
                for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr; ++arg_idx)
                {
                    ModuleList module_list;
                    const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, true);
                    if (num_matches > 0)
                    {
                        for (size_t i = 0; i < num_matches; ++i)
                        {
                            Module *module = module_list.GetModulePointerAtIndex(i);
                            if (module)
                            {
                                num_dumped++;
                                DumpModuleSections (m_interpreter, result.GetOutputStream(), module);
                            }
                        }
                    }
                    else
                    {
                        // Check the global list
                        std::lock_guard<std::recursive_mutex> guard(Module::GetAllocationModuleCollectionMutex());

                        result.AppendWarningWithFormat("Unable to find an image that matches '%s'.\n", arg_cstr);
                    }
                }
            }

            if (num_dumped > 0)
                result.SetStatus (eReturnStatusSuccessFinishResult);
            else
            {
                result.AppendError ("no matching executable images found");
                result.SetStatus (eReturnStatusFailed);
            }
        }
        return result.Succeeded();
    }
};

#pragma mark CommandObjectTargetModulesDumpSymfile

//----------------------------------------------------------------------
// Image debug symbol dumping command
//----------------------------------------------------------------------

class CommandObjectTargetModulesDumpSymfile : public CommandObjectTargetModulesModuleAutoComplete
{
public:
    CommandObjectTargetModulesDumpSymfile (CommandInterpreter &interpreter) :
        CommandObjectTargetModulesModuleAutoComplete(interpreter,
                                                     "target modules dump symfile",
                                                     "Dump the debug symbol file for one or more target modules.",
                                                     //"target modules dump symfile [<file1> ...]")
                                                     nullptr)
    {
    }

    ~CommandObjectTargetModulesDumpSymfile() override = default;

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
        if (target == nullptr)
        {
            result.AppendError ("invalid target, create a debug target using the 'target create' command");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }
        else
        {
            uint32_t num_dumped = 0;

            uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
            result.GetOutputStream().SetAddressByteSize(addr_byte_size);
            result.GetErrorStream().SetAddressByteSize(addr_byte_size);

            if (command.GetArgumentCount() == 0)
            {
                // Dump all sections for all modules images
                const ModuleList &target_modules = target->GetImages();
                std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
                const size_t num_modules = target_modules.GetSize();
                if (num_modules > 0)
                {
                    result.GetOutputStream().Printf("Dumping debug symbols for %" PRIu64 " modules.\n", (uint64_t)num_modules);
                    for (uint32_t image_idx = 0;  image_idx<num_modules; ++image_idx)
                    {
                        if (DumpModuleSymbolVendor (result.GetOutputStream(), target_modules.GetModulePointerAtIndexUnlocked(image_idx)))
                            num_dumped++;
                    }
                }
                else
                {
                    result.AppendError ("the target has no associated executable images");
                    result.SetStatus (eReturnStatusFailed);
                    return false;
                }
            }
            else
            {
                // Dump specified images (by basename or fullpath)
                const char *arg_cstr;
                for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr; ++arg_idx)
                {
                    ModuleList module_list;
                    const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, true);
                    if (num_matches > 0)
                    {
                        for (size_t i = 0; i < num_matches; ++i)
                        {
                            Module *module = module_list.GetModulePointerAtIndex(i);
                            if (module)
                            {
                                if (DumpModuleSymbolVendor (result.GetOutputStream(), module))
                                    num_dumped++;
                            }
                        }
                    }
                    else
                        result.AppendWarningWithFormat("Unable to find an image that matches '%s'.\n", arg_cstr);
                }
            }

            if (num_dumped > 0)
                result.SetStatus (eReturnStatusSuccessFinishResult);
            else
            {
                result.AppendError ("no matching executable images found");
                result.SetStatus (eReturnStatusFailed);
            }
        }
        return result.Succeeded();
    }
};

#pragma mark CommandObjectTargetModulesDumpLineTable

//----------------------------------------------------------------------
// Image debug line table dumping command
//----------------------------------------------------------------------

class CommandObjectTargetModulesDumpLineTable : public CommandObjectTargetModulesSourceFileAutoComplete
{
public:
    CommandObjectTargetModulesDumpLineTable (CommandInterpreter &interpreter) :
        CommandObjectTargetModulesSourceFileAutoComplete(interpreter,
                                                         "target modules dump line-table",
                                                         "Dump the line table for one or more compilation units.",
                                                         nullptr,
                                                         eCommandRequiresTarget)
    {
    }

    ~CommandObjectTargetModulesDumpLineTable() override = default;

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        Target *target = m_exe_ctx.GetTargetPtr();
        uint32_t total_num_dumped = 0;

        uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
        result.GetOutputStream().SetAddressByteSize(addr_byte_size);
        result.GetErrorStream().SetAddressByteSize(addr_byte_size);

        if (command.GetArgumentCount() == 0)
        {
            result.AppendError ("file option must be specified.");
            result.SetStatus (eReturnStatusFailed);
            return result.Succeeded();
        }
        else
        {
            // Dump specified images (by basename or fullpath)
            const char *arg_cstr;
            for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr; ++arg_idx)
            {
                FileSpec file_spec(arg_cstr, false);

                const ModuleList &target_modules = target->GetImages();
                std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
                const size_t num_modules = target_modules.GetSize();
                if (num_modules > 0)
                {
                    uint32_t num_dumped = 0;
                    for (uint32_t i = 0; i<num_modules; ++i)
                    {
                        if (DumpCompileUnitLineTable (m_interpreter,
                                                      result.GetOutputStream(),
                                                      target_modules.GetModulePointerAtIndexUnlocked(i),
                                                      file_spec,
                                                      m_exe_ctx.GetProcessPtr() && m_exe_ctx.GetProcessRef().IsAlive()))
                            num_dumped++;
                    }
                    if (num_dumped == 0)
                        result.AppendWarningWithFormat ("No source filenames matched '%s'.\n", arg_cstr);
                    else
                        total_num_dumped += num_dumped;
                }
            }
        }

        if (total_num_dumped > 0)
            result.SetStatus (eReturnStatusSuccessFinishResult);
        else
        {
            result.AppendError ("no source filenames matched any command arguments");
            result.SetStatus (eReturnStatusFailed);
        }
        return result.Succeeded();
    }
};

#pragma mark CommandObjectTargetModulesDump

//----------------------------------------------------------------------
// Dump multi-word command for target modules
//----------------------------------------------------------------------

class CommandObjectTargetModulesDump : public CommandObjectMultiword
{
public:
    //------------------------------------------------------------------
    // Constructors and Destructors
    //------------------------------------------------------------------
    CommandObjectTargetModulesDump(CommandInterpreter &interpreter)
        : CommandObjectMultiword(
              interpreter, "target modules dump", "Commands for dumping information about one or more target modules.",
              "target modules dump [headers|symtab|sections|symfile|line-table] [<file1> <file2> ...]")
    {
        LoadSubCommand("objfile", CommandObjectSP(new CommandObjectTargetModulesDumpObjfile(interpreter)));
        LoadSubCommand ("symtab",      CommandObjectSP (new CommandObjectTargetModulesDumpSymtab (interpreter)));
        LoadSubCommand ("sections",    CommandObjectSP (new CommandObjectTargetModulesDumpSections (interpreter)));
        LoadSubCommand ("symfile",     CommandObjectSP (new CommandObjectTargetModulesDumpSymfile (interpreter)));
        LoadSubCommand ("line-table",  CommandObjectSP (new CommandObjectTargetModulesDumpLineTable (interpreter)));
    }

    ~CommandObjectTargetModulesDump() override = default;
};

class CommandObjectTargetModulesAdd : public CommandObjectParsed
{
public:
    CommandObjectTargetModulesAdd (CommandInterpreter &interpreter) :
        CommandObjectParsed (interpreter,
                             "target modules add",
                             "Add a new module to the current target's modules.",
                             "target modules add [<module>]"),
        m_option_group (interpreter),
        m_symbol_file (LLDB_OPT_SET_1, false, "symfile", 's', 0, eArgTypeFilename, "Fullpath to a stand alone debug symbols file for when debug symbols are not in the executable.")
    {
        m_option_group.Append (&m_uuid_option_group, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Append (&m_symbol_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Finalize();
    }

    ~CommandObjectTargetModulesAdd() override = default;

    Options *
    GetOptions () override
    {
        return &m_option_group;
    }

    int
    HandleArgumentCompletion (Args &input,
                              int &cursor_index,
                              int &cursor_char_position,
                              OptionElementVector &opt_element_vector,
                              int match_start_point,
                              int max_return_elements,
                              bool &word_complete,
                              StringList &matches) override
    {
        std::string completion_str (input.GetArgumentAtIndex(cursor_index));
        completion_str.erase (cursor_char_position);

        CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter,
                                                            CommandCompletions::eDiskFileCompletion,
                                                            completion_str.c_str(),
                                                            match_start_point,
                                                            max_return_elements,
                                                            nullptr,
                                                            word_complete,
                                                            matches);
        return matches.GetSize();
    }

protected:
    OptionGroupOptions m_option_group;
    OptionGroupUUID m_uuid_option_group;
    OptionGroupFile m_symbol_file;

    bool
    DoExecute (Args& args, CommandReturnObject &result) override
    {
        Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
        if (target == nullptr)
        {
            result.AppendError ("invalid target, create a debug target using the 'target create' command");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }
        else
        {
            bool flush = false;

            const size_t argc = args.GetArgumentCount();
            if (argc == 0)
            {
                if (m_uuid_option_group.GetOptionValue ().OptionWasSet())
                {
                    // We are given a UUID only, go locate the file
                    ModuleSpec module_spec;
                    module_spec.GetUUID() = m_uuid_option_group.GetOptionValue ().GetCurrentValue();
                    if (m_symbol_file.GetOptionValue().OptionWasSet())
                        module_spec.GetSymbolFileSpec() = m_symbol_file.GetOptionValue().GetCurrentValue();
                    if (Symbols::DownloadObjectAndSymbolFile (module_spec))
                    {
                        ModuleSP module_sp (target->GetSharedModule (module_spec));
                        if (module_sp)
                        {
                            result.SetStatus (eReturnStatusSuccessFinishResult);
                            return true;
                        }
                        else
                        {
                            StreamString strm;
                            module_spec.GetUUID().Dump (&strm);
                            if (module_spec.GetFileSpec())
                            {
                                if (module_spec.GetSymbolFileSpec())
                                {
                                    result.AppendErrorWithFormat ("Unable to create the executable or symbol file with UUID %s with path %s and symbol file %s",
                                                                  strm.GetString().c_str(),
                                                                  module_spec.GetFileSpec().GetPath().c_str(),
                                                                  module_spec.GetSymbolFileSpec().GetPath().c_str());
                                }
                                else
                                {
                                    result.AppendErrorWithFormat ("Unable to create the executable or symbol file with UUID %s with path %s",
                                                                  strm.GetString().c_str(),
                                                                  module_spec.GetFileSpec().GetPath().c_str());
                                }
                            }
                            else
                            {
                                result.AppendErrorWithFormat ("Unable to create the executable or symbol file with UUID %s",
                                                              strm.GetString().c_str());
                            }
                            result.SetStatus (eReturnStatusFailed);
                            return false;
                        }
                    }
                    else
                    {
                        StreamString strm;
                        module_spec.GetUUID().Dump (&strm);
                        result.AppendErrorWithFormat ("Unable to locate the executable or symbol file with UUID %s", strm.GetString().c_str());
                        result.SetStatus (eReturnStatusFailed);
                        return false;
                    }
                }
                else
                {
                    result.AppendError ("one or more executable image paths must be specified");
                    result.SetStatus (eReturnStatusFailed);
                    return false;
                }
            }
            else
            {
                for (size_t i = 0; i < argc; ++i)
                {
                    const char *path = args.GetArgumentAtIndex(i);
                    if (path)
                    {
                        FileSpec file_spec(path, true);
                        if (file_spec.Exists())
                        {
                            ModuleSpec module_spec (file_spec);
                            if (m_uuid_option_group.GetOptionValue ().OptionWasSet())
                                module_spec.GetUUID() = m_uuid_option_group.GetOptionValue ().GetCurrentValue();
                            if (m_symbol_file.GetOptionValue().OptionWasSet())
                                module_spec.GetSymbolFileSpec() = m_symbol_file.GetOptionValue().GetCurrentValue();
                            if (!module_spec.GetArchitecture().IsValid())
                                module_spec.GetArchitecture() = target->GetArchitecture();
                            Error error;
                            ModuleSP module_sp (target->GetSharedModule (module_spec, &error));
                            if (!module_sp)
                            {
                                const char *error_cstr = error.AsCString();
                                if (error_cstr)
                                    result.AppendError (error_cstr);
                                else
                                    result.AppendErrorWithFormat ("unsupported module: %s", path);
                                result.SetStatus (eReturnStatusFailed);
                                return false;
                            }
                            else
                            {
                                flush = true;
                            }
                            result.SetStatus (eReturnStatusSuccessFinishResult);
                        }
                        else
                        {
                            char resolved_path[PATH_MAX];
                            result.SetStatus (eReturnStatusFailed);
                            if (file_spec.GetPath (resolved_path, sizeof(resolved_path)))
                            {
                                if (strcmp (resolved_path, path) != 0)
                                {
                                    result.AppendErrorWithFormat ("invalid module path '%s' with resolved path '%s'\n", path, resolved_path);
                                    break;
                                }
                            }
                            result.AppendErrorWithFormat ("invalid module path '%s'\n", path);
                            break;
                        }
                    }
                }
            }

            if (flush)
            {
                ProcessSP process = target->GetProcessSP();
                if (process)
                    process->Flush();
            }
        }

        return result.Succeeded();
    }
};

class CommandObjectTargetModulesLoad : public CommandObjectTargetModulesModuleAutoComplete
{
public:
    CommandObjectTargetModulesLoad (CommandInterpreter &interpreter) : 
        CommandObjectTargetModulesModuleAutoComplete (interpreter,
                                                      "target modules load",
                                                      "Set the load addresses for one or more sections in a target module.",
                                                      "target modules load [--file <module> --uuid <uuid>] <sect-name> <address> [<sect-name> <address> ....]"),
        m_option_group (interpreter),
        m_file_option (LLDB_OPT_SET_1, false, "file", 'f', 0, eArgTypeName, "Fullpath or basename for module to load.", ""),
        m_slide_option(LLDB_OPT_SET_1, false, "slide", 's', 0, eArgTypeOffset, "Set the load address for all sections to be the virtual address in the file plus the offset.", 0)
    {
        m_option_group.Append (&m_uuid_option_group, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Append (&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Append (&m_slide_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); 
        m_option_group.Finalize();
    }

    ~CommandObjectTargetModulesLoad() override = default;

    Options *
    GetOptions () override
    {
        return &m_option_group;
    }

protected:
    bool
    DoExecute (Args& args, CommandReturnObject &result) override
    {
        Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
        if (target == nullptr)
        {
            result.AppendError ("invalid target, create a debug target using the 'target create' command");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }
        else
        {
            const size_t argc = args.GetArgumentCount();
            ModuleSpec module_spec;
            bool search_using_module_spec = false;
            if (m_file_option.GetOptionValue().OptionWasSet())
            {
                search_using_module_spec = true;
                const char *arg_cstr = m_file_option.GetOptionValue().GetCurrentValue();
                const bool use_global_module_list = true;
                ModuleList module_list;
                const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, use_global_module_list);
                if (num_matches == 1)
                {
                    module_spec.GetFileSpec() = module_list.GetModuleAtIndex(0)->GetFileSpec();
                }
                else if (num_matches > 1 )
                {
                    search_using_module_spec = false;
                    result.AppendErrorWithFormat ("more than 1 module matched by name '%s'\n", arg_cstr);
                    result.SetStatus (eReturnStatusFailed);
                }
                else
                {
                    search_using_module_spec = false;
                    result.AppendErrorWithFormat ("no object file for module '%s'\n", arg_cstr);
                    result.SetStatus (eReturnStatusFailed);
                }
            }

            if (m_uuid_option_group.GetOptionValue().OptionWasSet())
            {
                search_using_module_spec = true;
                module_spec.GetUUID() = m_uuid_option_group.GetOptionValue().GetCurrentValue();
            }

            if (search_using_module_spec)
            {
                ModuleList matching_modules;
                const size_t num_matches = target->GetImages().FindModules (module_spec, matching_modules);

                char path[PATH_MAX];
                if (num_matches == 1)
                {
                    Module *module = matching_modules.GetModulePointerAtIndex(0);
                    if (module)
                    {
                        ObjectFile *objfile = module->GetObjectFile();
                        if (objfile)
                        {
                            SectionList *section_list = module->GetSectionList();
                            if (section_list)
                            {
                                bool changed = false;
                                if (argc == 0)
                                {
                                    if (m_slide_option.GetOptionValue().OptionWasSet())
                                    {
                                        const addr_t slide = m_slide_option.GetOptionValue().GetCurrentValue();
                                        const bool slide_is_offset = true;
                                        module->SetLoadAddress (*target, slide, slide_is_offset, changed);
                                    }
                                    else
                                    {
                                        result.AppendError ("one or more section name + load address pair must be specified");
                                        result.SetStatus (eReturnStatusFailed);
                                        return false;
                                    }
                                }
                                else
                                {
                                    if (m_slide_option.GetOptionValue().OptionWasSet())
                                    {
                                        result.AppendError ("The \"--slide <offset>\" option can't be used in conjunction with setting section load addresses.\n");
                                        result.SetStatus (eReturnStatusFailed);
                                        return false;
                                    }

                                    for (size_t i = 0; i < argc; i += 2)
                                    {
                                        const char *sect_name = args.GetArgumentAtIndex(i);
                                        const char *load_addr_cstr = args.GetArgumentAtIndex(i+1);
                                        if (sect_name && load_addr_cstr)
                                        {
                                            ConstString const_sect_name(sect_name);
                                            bool success = false;
                                            addr_t load_addr = StringConvert::ToUInt64(load_addr_cstr, LLDB_INVALID_ADDRESS, 0, &success);
                                            if (success)
                                            {
                                                SectionSP section_sp (section_list->FindSectionByName(const_sect_name));
                                                if (section_sp)
                                                {
                                                    if (section_sp->IsThreadSpecific())
                                                    {
                                                        result.AppendErrorWithFormat ("thread specific sections are not yet supported (section '%s')\n", sect_name);
                                                        result.SetStatus (eReturnStatusFailed);
                                                        break;
                                                    }
                                                    else
                                                    {
                                                        if (target->GetSectionLoadList().SetSectionLoadAddress (section_sp, load_addr))
                                                            changed = true;
                                                        result.AppendMessageWithFormat("section '%s' loaded at 0x%" PRIx64 "\n", sect_name, load_addr);
                                                    }
                                                }
                                                else
                                                {
                                                    result.AppendErrorWithFormat ("no section found that matches the section name '%s'\n", sect_name);
                                                    result.SetStatus (eReturnStatusFailed);
                                                    break;
                                                }
                                            }
                                            else
                                            {
                                                result.AppendErrorWithFormat ("invalid load address string '%s'\n", load_addr_cstr);
                                                result.SetStatus (eReturnStatusFailed);
                                                break;
                                            }
                                        }
                                        else
                                        {
                                            if (sect_name)
                                                result.AppendError ("section names must be followed by a load address.\n");
                                            else
                                                result.AppendError ("one or more section name + load address pair must be specified.\n");
                                            result.SetStatus (eReturnStatusFailed);
                                            break;
                                        }
                                    }
                                }

                                if (changed)
                                {
                                    target->ModulesDidLoad (matching_modules);
                                    Process *process = m_exe_ctx.GetProcessPtr();
                                    if (process)
                                        process->Flush();
                                }
                            }
                            else
                            {
                                module->GetFileSpec().GetPath (path, sizeof(path));
                                result.AppendErrorWithFormat ("no sections in object file '%s'\n", path);
                                result.SetStatus (eReturnStatusFailed);
                            }
                        }
                        else
                        {
                            module->GetFileSpec().GetPath (path, sizeof(path));
                            result.AppendErrorWithFormat ("no object file for module '%s'\n", path);
                            result.SetStatus (eReturnStatusFailed);
                        }
                    }
                    else
                    {
                        FileSpec *module_spec_file = module_spec.GetFileSpecPtr();
                        if (module_spec_file)
                        {
                            module_spec_file->GetPath (path, sizeof(path));
                            result.AppendErrorWithFormat ("invalid module '%s'.\n", path);
                        }
                        else
                            result.AppendError ("no module spec");
                        result.SetStatus (eReturnStatusFailed);
                    }
                }
                else
                {
                    std::string uuid_str;

                    if (module_spec.GetFileSpec())
                        module_spec.GetFileSpec().GetPath (path, sizeof(path));
                    else
                        path[0] = '\0';

                    if (module_spec.GetUUIDPtr())
                        uuid_str = module_spec.GetUUID().GetAsString();
                    if (num_matches > 1)
                    {
                        result.AppendErrorWithFormat ("multiple modules match%s%s%s%s:\n", 
                                                      path[0] ? " file=" : "", 
                                                      path,
                                                      !uuid_str.empty() ? " uuid=" : "", 
                                                      uuid_str.c_str());
                        for (size_t i = 0; i < num_matches; ++i)
                        {
                            if (matching_modules.GetModulePointerAtIndex(i)->GetFileSpec().GetPath (path, sizeof(path)))
                                result.AppendMessageWithFormat("%s\n", path);
                        }
                    }
                    else
                    {
                        result.AppendErrorWithFormat ("no modules were found  that match%s%s%s%s.\n", 
                                                      path[0] ? " file=" : "", 
                                                      path,
                                                      !uuid_str.empty() ? " uuid=" : "", 
                                                      uuid_str.c_str());
                    }
                    result.SetStatus (eReturnStatusFailed);
                }
            }
            else
            {
                result.AppendError ("either the \"--file <module>\" or the \"--uuid <uuid>\" option must be specified.\n");
                result.SetStatus (eReturnStatusFailed);
                return false;
            }
        }
        return result.Succeeded();
    }

    OptionGroupOptions m_option_group;
    OptionGroupUUID m_uuid_option_group;
    OptionGroupString m_file_option;
    OptionGroupUInt64 m_slide_option;
};

//----------------------------------------------------------------------
// List images with associated information
//----------------------------------------------------------------------
class CommandObjectTargetModulesList : public CommandObjectParsed
{
public:
    class CommandOptions : public Options
    {
    public:
        CommandOptions (CommandInterpreter &interpreter) :
            Options(interpreter),
            m_format_array(),
            m_use_global_module_list (false),
            m_module_addr (LLDB_INVALID_ADDRESS)
        {
        }

        ~CommandOptions() override = default;

        Error
        SetOptionValue (uint32_t option_idx, const char *option_arg) override
        {
            Error error;

            const int short_option = m_getopt_table[option_idx].val;
            if (short_option == 'g')
            {
                m_use_global_module_list = true;
            }
            else if (short_option == 'a')
            {
                ExecutionContext exe_ctx (m_interpreter.GetExecutionContext());
                m_module_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error);
            }
            else
            {
                unsigned long width = 0;
                if (option_arg)
                    width = strtoul(option_arg, nullptr, 0);
                m_format_array.push_back(std::make_pair(short_option, width));
            }
            return error;
        }

        void
        OptionParsingStarting () override
        {
            m_format_array.clear();
            m_use_global_module_list = false;
            m_module_addr = LLDB_INVALID_ADDRESS;
        }

        const OptionDefinition*
        GetDefinitions () override
        {
            return g_option_table;
        }

        // Options table: Required for subclasses of Options.

        static OptionDefinition g_option_table[];

        // Instance variables to hold the values for command options.
        typedef std::vector< std::pair<char, uint32_t> > FormatWidthCollection;
        FormatWidthCollection m_format_array;
        bool m_use_global_module_list;
        lldb::addr_t m_module_addr;
    };

    CommandObjectTargetModulesList (CommandInterpreter &interpreter) :
        CommandObjectParsed (interpreter,
                             "target modules list",
                             "List current executable and dependent shared library images.",
                             "target modules list [<cmd-options>]"),
        m_options (interpreter)
    {
    }

    ~CommandObjectTargetModulesList() override = default;

    Options *
    GetOptions () override
    {
        return &m_options;
    }

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
        const bool use_global_module_list = m_options.m_use_global_module_list;
        // Define a local module list here to ensure it lives longer than any "locker"
        // object which might lock its contents below (through the "module_list_ptr"
        // variable).
        ModuleList module_list;
        if (target == nullptr && !use_global_module_list)
        {
            result.AppendError ("invalid target, create a debug target using the 'target create' command");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }
        else
        {
            if (target)
            {
                uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
                result.GetOutputStream().SetAddressByteSize(addr_byte_size);
                result.GetErrorStream().SetAddressByteSize(addr_byte_size);
            }
            // Dump all sections for all modules images
            Stream &strm = result.GetOutputStream();

            if (m_options.m_module_addr != LLDB_INVALID_ADDRESS)
            {
                if (target)
                {
                    Address module_address;
                    if (module_address.SetLoadAddress(m_options.m_module_addr, target))
                    {
                        ModuleSP module_sp (module_address.GetModule());
                        if (module_sp)
                        {
                            PrintModule (target, module_sp.get(), 0, strm);
                            result.SetStatus (eReturnStatusSuccessFinishResult);
                        }
                        else
                        {
                            result.AppendErrorWithFormat ("Couldn't find module matching address: 0x%" PRIx64 ".", m_options.m_module_addr);
                            result.SetStatus (eReturnStatusFailed);
                        }
                    }
                    else
                    {
                        result.AppendErrorWithFormat ("Couldn't find module containing address: 0x%" PRIx64 ".", m_options.m_module_addr);
                        result.SetStatus (eReturnStatusFailed);
                    }
                }
                else
                {
                    result.AppendError ("Can only look up modules by address with a valid target.");
                    result.SetStatus (eReturnStatusFailed);
                }
                return result.Succeeded();
            }

            size_t num_modules = 0;

            // This locker will be locked on the mutex in module_list_ptr if it is non-nullptr.
            // Otherwise it will lock the AllocationModuleCollectionMutex when accessing
            // the global module list directly.
            std::unique_lock<std::recursive_mutex> guard(Module::GetAllocationModuleCollectionMutex(), std::defer_lock);

            const ModuleList *module_list_ptr = nullptr;
            const size_t argc = command.GetArgumentCount();
            if (argc == 0)
            {
                if (use_global_module_list)
                {
                    guard.lock();
                    num_modules = Module::GetNumberAllocatedModules();
                }
                else
                {
                    module_list_ptr = &target->GetImages();
                }
            }
            else
            {
                for (size_t i = 0; i < argc; ++i)
                {
                    // Dump specified images (by basename or fullpath)
                    const char *arg_cstr = command.GetArgumentAtIndex(i);
                    const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, use_global_module_list);
                    if (num_matches == 0)
                    {
                        if (argc == 1)
                        {
                            result.AppendErrorWithFormat ("no modules found that match '%s'", arg_cstr);
                            result.SetStatus (eReturnStatusFailed);
                            return false;
                        }
                    }
                }

                module_list_ptr = &module_list;
            }

            std::unique_lock<std::recursive_mutex> lock;
            if (module_list_ptr != nullptr)
            {
                lock = std::unique_lock<std::recursive_mutex>(module_list_ptr->GetMutex());

                num_modules = module_list_ptr->GetSize();
            }

            if (num_modules > 0)
            {
                for (uint32_t image_idx = 0; image_idx<num_modules; ++image_idx)
                {
                    ModuleSP module_sp;
                    Module *module;
                    if (module_list_ptr)
                    {
                        module_sp = module_list_ptr->GetModuleAtIndexUnlocked(image_idx);
                        module = module_sp.get();
                    }
                    else
                    {
                        module = Module::GetAllocatedModuleAtIndex(image_idx);
                        module_sp = module->shared_from_this();
                    }

                    const size_t indent = strm.Printf("[%3u] ", image_idx);
                    PrintModule (target, module, indent, strm);

                }
                result.SetStatus (eReturnStatusSuccessFinishResult);
            }
            else
            {
                if (argc)
                {
                    if (use_global_module_list)
                        result.AppendError ("the global module list has no matching modules");
                    else
                        result.AppendError ("the target has no matching modules");
                }
                else
                {
                    if (use_global_module_list)
                        result.AppendError ("the global module list is empty");
                    else
                        result.AppendError ("the target has no associated executable images");
                }
                result.SetStatus (eReturnStatusFailed);
                return false;
            }
        }
        return result.Succeeded();
    }

    void
    PrintModule (Target *target, Module *module, int indent, Stream &strm)
    {
        if (module == nullptr)
        {
            strm.PutCString("Null module");
            return;
        }

        bool dump_object_name = false;
        if (m_options.m_format_array.empty())
        {
            m_options.m_format_array.push_back(std::make_pair('u', 0));
            m_options.m_format_array.push_back(std::make_pair('h', 0));
            m_options.m_format_array.push_back(std::make_pair('f', 0));
            m_options.m_format_array.push_back(std::make_pair('S', 0));
        }
        const size_t num_entries = m_options.m_format_array.size();
        bool print_space = false;
        for (size_t i = 0; i < num_entries; ++i)
        {
            if (print_space)
                strm.PutChar(' ');
            print_space = true;
            const char format_char = m_options.m_format_array[i].first;
            uint32_t width = m_options.m_format_array[i].second;
            switch (format_char)
            {
                case 'A':
                    DumpModuleArchitecture (strm, module, false, width);
                    break;

                case 't':
                    DumpModuleArchitecture (strm, module, true, width);
                    break;

                case 'f':
                    DumpFullpath (strm, &module->GetFileSpec(), width);
                    dump_object_name = true;
                    break;

                case 'd':
                    DumpDirectory (strm, &module->GetFileSpec(), width);
                    break;

                case 'b':
                    DumpBasename (strm, &module->GetFileSpec(), width);
                    dump_object_name = true;
                    break;

                case 'h':
                case 'o':
                    // Image header address
                    {
                        uint32_t addr_nibble_width = target ? (target->GetArchitecture().GetAddressByteSize() * 2) : 16;

                        ObjectFile *objfile = module->GetObjectFile ();
                        if (objfile)
                        {
                            Address header_addr(objfile->GetHeaderAddress());
                            if (header_addr.IsValid())
                            {
                                if (target && !target->GetSectionLoadList().IsEmpty())
                                {
                                    lldb::addr_t header_load_addr = header_addr.GetLoadAddress (target);
                                    if (header_load_addr == LLDB_INVALID_ADDRESS)
                                    {
                                        header_addr.Dump (&strm, target, Address::DumpStyleModuleWithFileAddress, Address::DumpStyleFileAddress);
                                    }
                                    else
                                    {
                                        if (format_char == 'o')
                                        {
                                            // Show the offset of slide for the image
                                            strm.Printf ("0x%*.*" PRIx64, addr_nibble_width, addr_nibble_width, header_load_addr - header_addr.GetFileAddress());
                                        }
                                        else
                                        {
                                            // Show the load address of the image
                                            strm.Printf ("0x%*.*" PRIx64, addr_nibble_width, addr_nibble_width, header_load_addr);
                                        }
                                    }
                                    break;
                                }
                                // The address was valid, but the image isn't loaded, output the address in an appropriate format
                                header_addr.Dump (&strm, target, Address::DumpStyleFileAddress);
                                break;
                            }
                        }
                        strm.Printf ("%*s", addr_nibble_width + 2, "");
                    }
                    break;

                case 'r':
                    {
                        size_t ref_count = 0;
                        ModuleSP module_sp (module->shared_from_this());
                        if (module_sp)
                        {
                            // Take one away to make sure we don't count our local "module_sp"
                            ref_count = module_sp.use_count() - 1;
                        }
                        if (width)
                            strm.Printf("{%*" PRIu64 "}", width, (uint64_t)ref_count);
                        else
                            strm.Printf("{%" PRIu64 "}", (uint64_t)ref_count);
                    }
                    break;

                case 's':
                case 'S':
                    {
                        const SymbolVendor *symbol_vendor = module->GetSymbolVendor();
                        if (symbol_vendor)
                        {
                            const FileSpec symfile_spec = symbol_vendor->GetMainFileSpec();
                            if (format_char == 'S')
                            {
                                // Dump symbol file only if different from module file
                                if (!symfile_spec || symfile_spec == module->GetFileSpec())
                                {
                                    print_space = false;
                                    break;
                                }
                                // Add a newline and indent past the index
                                strm.Printf ("\n%*s", indent, "");
                            }
                            DumpFullpath (strm, &symfile_spec, width);
                            dump_object_name = true;
                            break;
                        }
                        strm.Printf("%.*s", width, "<NONE>");
                    }
                    break;

                case 'm':
                    module->GetModificationTime().Dump(&strm, width);
                    break;

                case 'p':
                    strm.Printf("%p", static_cast<void*>(module));
                    break;

                case 'u':
                    DumpModuleUUID(strm, module);
                    break;

                default:
                    break;
            }
        }
        if (dump_object_name)
        {
            const char *object_name = module->GetObjectName().GetCString();
            if (object_name)
                strm.Printf ("(%s)", object_name);
        }
        strm.EOL();
    }

    CommandOptions m_options;
};

OptionDefinition
CommandObjectTargetModulesList::CommandOptions::g_option_table[] =
{
    { LLDB_OPT_SET_1, false, "address",    'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Display the image at this address."},
    { LLDB_OPT_SET_1, false, "arch",       'A', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth,   "Display the architecture when listing images."},
    { LLDB_OPT_SET_1, false, "triple",     't', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth,   "Display the triple when listing images."},
    { LLDB_OPT_SET_1, false, "header",     'h', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,    "Display the image header address as a load address if debugging, a file address otherwise."},
    { LLDB_OPT_SET_1, false, "offset",     'o', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,    "Display the image header address offset from the header file address (the slide amount)."},
    { LLDB_OPT_SET_1, false, "uuid",       'u', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,    "Display the UUID when listing images."},
    { LLDB_OPT_SET_1, false, "fullpath",   'f', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth,   "Display the fullpath to the image object file."},
    { LLDB_OPT_SET_1, false, "directory",  'd', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth,   "Display the directory with optional width for the image object file."},
    { LLDB_OPT_SET_1, false, "basename",   'b', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth,   "Display the basename with optional width for the image object file."},
    { LLDB_OPT_SET_1, false, "symfile",    's', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth,   "Display the fullpath to the image symbol file with optional width."},
    { LLDB_OPT_SET_1, false, "symfile-unique", 'S', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth,   "Display the symbol file with optional width only if it is different from the executable object file."},
    { LLDB_OPT_SET_1, false, "mod-time",   'm', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth,   "Display the modification time with optional width of the module."},
    { LLDB_OPT_SET_1, false, "ref-count",  'r', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth,   "Display the reference count if the module is still in the shared module cache."},
    { LLDB_OPT_SET_1, false, "pointer",    'p', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeNone,    "Display the module pointer."},
    { LLDB_OPT_SET_1, false, "global",     'g', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,    "Display the modules from the global module list, not just the current target."},
    { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr }
};

#pragma mark CommandObjectTargetModulesShowUnwind

//----------------------------------------------------------------------
// Lookup unwind information in images
//----------------------------------------------------------------------

class CommandObjectTargetModulesShowUnwind : public CommandObjectParsed
{
public:
    enum
    {
        eLookupTypeInvalid = -1,
        eLookupTypeAddress = 0,
        eLookupTypeSymbol,
        eLookupTypeFunction,
        eLookupTypeFunctionOrSymbol,
        kNumLookupTypes
    };

    class CommandOptions : public Options
    {
    public:
        CommandOptions (CommandInterpreter &interpreter) :
            Options(interpreter),
            m_type(eLookupTypeInvalid),
            m_str(),
            m_addr(LLDB_INVALID_ADDRESS)
        {
        }

        ~CommandOptions() override = default;

        Error
        SetOptionValue (uint32_t option_idx, const char *option_arg) override
        {
            Error error;

            const int short_option = m_getopt_table[option_idx].val;

            switch (short_option)
            {
                case 'a':
                {
                    ExecutionContext exe_ctx (m_interpreter.GetExecutionContext());
                    m_str = option_arg;
                    m_type = eLookupTypeAddress;
                    m_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error);
                    if (m_addr == LLDB_INVALID_ADDRESS)
                        error.SetErrorStringWithFormat ("invalid address string '%s'", option_arg);
                    break;
                }

                case 'n':
                    m_str = option_arg;
                    m_type = eLookupTypeFunctionOrSymbol;
                    break;

                default:
                    error.SetErrorStringWithFormat ("unrecognized option %c.", short_option);
                    break;
            }

            return error;
        }

        void
        OptionParsingStarting () override
        {
            m_type = eLookupTypeInvalid;
            m_str.clear();
            m_addr = LLDB_INVALID_ADDRESS;
        }

        const OptionDefinition*
        GetDefinitions () override
        {
            return g_option_table;
        }

        // Options table: Required for subclasses of Options.

        static OptionDefinition g_option_table[];

        // Instance variables to hold the values for command options.

        int             m_type;         // Should be a eLookupTypeXXX enum after parsing options
        std::string     m_str;          // Holds name lookup
        lldb::addr_t    m_addr;         // Holds the address to lookup
    };

    CommandObjectTargetModulesShowUnwind (CommandInterpreter &interpreter) :
        CommandObjectParsed(interpreter,
                            "target modules show-unwind",
                            "Show synthesized unwind instructions for a function.",
                            nullptr,
                            eCommandRequiresTarget        |
                            eCommandRequiresProcess       |
                            eCommandProcessMustBeLaunched |
                            eCommandProcessMustBePaused   ),
        m_options (interpreter)
    {
    }

    ~CommandObjectTargetModulesShowUnwind() override = default;

    Options *
    GetOptions () override
    {
        return &m_options;
    }

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        Target *target = m_exe_ctx.GetTargetPtr();
        Process *process = m_exe_ctx.GetProcessPtr();
        ABI *abi = nullptr;
        if (process)
          abi = process->GetABI().get();

        if (process == nullptr)
        {
            result.AppendError ("You must have a process running to use this command.");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        ThreadList threads(process->GetThreadList());
        if (threads.GetSize() == 0)
        {
            result.AppendError ("The process must be paused to use this command.");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        ThreadSP thread(threads.GetThreadAtIndex(0));
        if (!thread)
        {
            result.AppendError ("The process must be paused to use this command.");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        SymbolContextList sc_list;

        if (m_options.m_type == eLookupTypeFunctionOrSymbol)
        {
            ConstString function_name (m_options.m_str.c_str());
            target->GetImages().FindFunctions (function_name, eFunctionNameTypeAuto, true, false, true, sc_list);
        }
        else if (m_options.m_type == eLookupTypeAddress && target)
        {
            Address addr;
            if (target->GetSectionLoadList().ResolveLoadAddress (m_options.m_addr, addr))
            {
                SymbolContext sc;
                ModuleSP module_sp (addr.GetModule());
                module_sp->ResolveSymbolContextForAddress (addr, eSymbolContextEverything, sc);
                if (sc.function || sc.symbol)
                {
                    sc_list.Append(sc);
                }
            }
        }
        else
        {
            result.AppendError ("address-expression or function name option must be specified.");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        size_t num_matches = sc_list.GetSize();
        if (num_matches == 0)
        {
            result.AppendErrorWithFormat ("no unwind data found that matches '%s'.", m_options.m_str.c_str());
            result.SetStatus (eReturnStatusFailed);
            return false;
        }

        for (uint32_t idx = 0; idx < num_matches; idx++)
        {
            SymbolContext sc;
            sc_list.GetContextAtIndex(idx, sc);
            if (sc.symbol == nullptr && sc.function == nullptr)
                continue;
            if (!sc.module_sp || sc.module_sp->GetObjectFile() == nullptr)
                continue;
            AddressRange range;
            if (!sc.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, range))
                continue;
            if (!range.GetBaseAddress().IsValid())
                continue;
            ConstString funcname(sc.GetFunctionName());
            if (funcname.IsEmpty())
                continue;
            addr_t start_addr = range.GetBaseAddress().GetLoadAddress(target);
            if (abi)
                start_addr = abi->FixCodeAddress(start_addr);

            FuncUnwindersSP func_unwinders_sp (sc.module_sp->GetObjectFile()->GetUnwindTable().GetUncachedFuncUnwindersContainingAddress(start_addr, sc));
            if (!func_unwinders_sp)
                continue;

            result.GetOutputStream().Printf("UNWIND PLANS for %s`%s (start addr 0x%" PRIx64 ")\n\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr);

            UnwindPlanSP non_callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtNonCallSite(*target, *thread, -1);
            if (non_callsite_unwind_plan)
            {
                result.GetOutputStream().Printf("Asynchronous (not restricted to call-sites) UnwindPlan is '%s'\n", non_callsite_unwind_plan->GetSourceName().AsCString());
            }
            UnwindPlanSP callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(*target, -1);
            if (callsite_unwind_plan)
            {
                result.GetOutputStream().Printf("Synchronous (restricted to call-sites) UnwindPlan is '%s'\n", callsite_unwind_plan->GetSourceName().AsCString());
            }
            UnwindPlanSP fast_unwind_plan = func_unwinders_sp->GetUnwindPlanFastUnwind(*target, *thread);
            if (fast_unwind_plan)
            {
                result.GetOutputStream().Printf("Fast UnwindPlan is '%s'\n", fast_unwind_plan->GetSourceName().AsCString());
            }

            result.GetOutputStream().Printf("\n");

            UnwindPlanSP assembly_sp = func_unwinders_sp->GetAssemblyUnwindPlan(*target, *thread, 0);
            if (assembly_sp)
            {
                result.GetOutputStream().Printf("Assembly language inspection UnwindPlan:\n");
                assembly_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS);
                result.GetOutputStream().Printf("\n");
            }

            UnwindPlanSP ehframe_sp = func_unwinders_sp->GetEHFrameUnwindPlan(*target, 0);
            if (ehframe_sp)
            {
                result.GetOutputStream().Printf("eh_frame UnwindPlan:\n");
                ehframe_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS);
                result.GetOutputStream().Printf("\n");
            }

            UnwindPlanSP ehframe_augmented_sp = func_unwinders_sp->GetEHFrameAugmentedUnwindPlan(*target, *thread, 0);
            if (ehframe_augmented_sp)
            {
                result.GetOutputStream().Printf("eh_frame augmented UnwindPlan:\n");
                ehframe_augmented_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS);
                result.GetOutputStream().Printf("\n");
            }

            UnwindPlanSP arm_unwind_sp = func_unwinders_sp->GetArmUnwindUnwindPlan(*target, 0);
            if (arm_unwind_sp)
            {
                result.GetOutputStream().Printf("ARM.exidx unwind UnwindPlan:\n");
                arm_unwind_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS);
                result.GetOutputStream().Printf("\n");
            }

            UnwindPlanSP compact_unwind_sp = func_unwinders_sp->GetCompactUnwindUnwindPlan(*target, 0);
            if (compact_unwind_sp)
            {
                result.GetOutputStream().Printf("Compact unwind UnwindPlan:\n");
                compact_unwind_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS);
                result.GetOutputStream().Printf("\n");
            }

            if (fast_unwind_plan)
            {
                result.GetOutputStream().Printf("Fast UnwindPlan:\n");
                fast_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS);
                result.GetOutputStream().Printf("\n");
            }

            ABISP abi_sp = process->GetABI();
            if (abi_sp)
            {
                UnwindPlan arch_default(lldb::eRegisterKindGeneric);
                if (abi_sp->CreateDefaultUnwindPlan (arch_default))
                {
                    result.GetOutputStream().Printf("Arch default UnwindPlan:\n");
                    arch_default.Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS);
                    result.GetOutputStream().Printf("\n");
                }

                UnwindPlan arch_entry(lldb::eRegisterKindGeneric);
                if (abi_sp->CreateFunctionEntryUnwindPlan (arch_entry))
                {
                    result.GetOutputStream().Printf("Arch default at entry point UnwindPlan:\n");
                    arch_entry.Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS);
                    result.GetOutputStream().Printf("\n");
                }
            }

            result.GetOutputStream().Printf ("\n");
        }
        return result.Succeeded();
    }

    CommandOptions m_options;
};

OptionDefinition
CommandObjectTargetModulesShowUnwind::CommandOptions::g_option_table[] =
{
    { LLDB_OPT_SET_1,   false,  "name",       'n', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFunctionName, "Show unwind instructions for a function or symbol name."},
    { LLDB_OPT_SET_2,   false,  "address",    'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Show unwind instructions for a function or symbol containing an address"},
    { 0,                false, nullptr,           0, 0,                 nullptr, nullptr, 0, eArgTypeNone, nullptr }
};

//----------------------------------------------------------------------
// Lookup information in images
//----------------------------------------------------------------------
class CommandObjectTargetModulesLookup : public CommandObjectParsed
{
public:
    enum
    {
        eLookupTypeInvalid = -1,
        eLookupTypeAddress = 0,
        eLookupTypeSymbol,
        eLookupTypeFileLine,    // Line is optional
        eLookupTypeFunction,
        eLookupTypeFunctionOrSymbol,
        eLookupTypeType,
        kNumLookupTypes
    };

    class CommandOptions : public Options
    {
    public:
        CommandOptions (CommandInterpreter &interpreter) :
        Options(interpreter)
        {
            OptionParsingStarting();
        }

        ~CommandOptions() override = default;

        Error
        SetOptionValue (uint32_t option_idx, const char *option_arg) override
        {
            Error error;

            const int short_option = m_getopt_table[option_idx].val;

            switch (short_option)
            {
                case 'a':
                    {
                        m_type = eLookupTypeAddress;
                        ExecutionContext exe_ctx (m_interpreter.GetExecutionContext());
                        m_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error);
                    }
                    break;

                case 'o':
                    m_offset = StringConvert::ToUInt64(option_arg, LLDB_INVALID_ADDRESS);
                    if (m_offset == LLDB_INVALID_ADDRESS)
                        error.SetErrorStringWithFormat ("invalid offset string '%s'", option_arg);
                    break;

                case 's':
                    m_str = option_arg;
                    m_type = eLookupTypeSymbol;
                    break;

                case 'f':
                    m_file.SetFile (option_arg, false);
                    m_type = eLookupTypeFileLine;
                    break;

                case 'i':
                    m_include_inlines = false;
                    break;

                case 'l':
                    m_line_number = StringConvert::ToUInt32(option_arg, UINT32_MAX);
                    if (m_line_number == UINT32_MAX)
                        error.SetErrorStringWithFormat ("invalid line number string '%s'", option_arg);
                    else if (m_line_number == 0)
                        error.SetErrorString ("zero is an invalid line number");
                    m_type = eLookupTypeFileLine;
                    break;

                case 'F':
                    m_str = option_arg;
                    m_type = eLookupTypeFunction;
                    break;

                case 'n':
                    m_str = option_arg;
                    m_type = eLookupTypeFunctionOrSymbol;
                    break;

                case 't':
                    m_str = option_arg;
                    m_type = eLookupTypeType;
                    break;

                case 'v':
                    m_verbose = 1;
                    break;

                case 'A':
                    m_print_all = true;
                    break;

                case 'r':
                    m_use_regex = true;
                    break;
            }

            return error;
        }

        void
        OptionParsingStarting () override
        {
            m_type = eLookupTypeInvalid;
            m_str.clear();
            m_file.Clear();
            m_addr = LLDB_INVALID_ADDRESS;
            m_offset = 0;
            m_line_number = 0;
            m_use_regex = false;
            m_include_inlines = true;
            m_verbose = false;
            m_print_all = false;
        }

        const OptionDefinition*
        GetDefinitions () override
        {
            return g_option_table;
        }

        // Options table: Required for subclasses of Options.

        static OptionDefinition g_option_table[];
        int             m_type;         // Should be a eLookupTypeXXX enum after parsing options
        std::string     m_str;          // Holds name lookup
        FileSpec        m_file;         // Files for file lookups
        lldb::addr_t    m_addr;         // Holds the address to lookup
        lldb::addr_t    m_offset;       // Subtract this offset from m_addr before doing lookups.
        uint32_t        m_line_number;  // Line number for file+line lookups
        bool            m_use_regex;    // Name lookups in m_str are regular expressions.
        bool            m_include_inlines;// Check for inline entries when looking up by file/line.
        bool            m_verbose;      // Enable verbose lookup info
        bool            m_print_all;    // Print all matches, even in cases where there's a best match.
    };

    CommandObjectTargetModulesLookup (CommandInterpreter &interpreter) :
        CommandObjectParsed(interpreter,
                            "target modules lookup",
                            "Look up information within executable and dependent shared library images.",
                            nullptr,
                            eCommandRequiresTarget),
        m_options (interpreter)
    {
        CommandArgumentEntry arg;
        CommandArgumentData file_arg;

        // Define the first (and only) variant of this arg.
        file_arg.arg_type = eArgTypeFilename;
        file_arg.arg_repetition = eArgRepeatStar;

        // There is only one variant this argument could be; put it into the argument entry.
        arg.push_back (file_arg);

        // Push the data for the first argument into the m_arguments vector.
        m_arguments.push_back (arg);
    }

    ~CommandObjectTargetModulesLookup() override = default;

    Options *
    GetOptions () override
    {
        return &m_options;
    }

    bool
    LookupHere (CommandInterpreter &interpreter, CommandReturnObject &result, bool &syntax_error)
    {
        switch (m_options.m_type)
        {
            case eLookupTypeAddress:
            case eLookupTypeFileLine:
            case eLookupTypeFunction:
            case eLookupTypeFunctionOrSymbol:
            case eLookupTypeSymbol:
            default:
                return false;
            case eLookupTypeType:
                break;
        }

        StackFrameSP frame = m_exe_ctx.GetFrameSP();

        if (!frame)
            return false;

        const SymbolContext &sym_ctx(frame->GetSymbolContext(eSymbolContextModule));

        if (!sym_ctx.module_sp)
            return false;

        switch (m_options.m_type)
        {
        default:
            return false;
        case eLookupTypeType:
            if (!m_options.m_str.empty())
            {
                if (LookupTypeHere (m_interpreter,
                                    result.GetOutputStream(),
                                    sym_ctx,
                                    m_options.m_str.c_str(),
                                    m_options.m_use_regex))
                {
                    result.SetStatus(eReturnStatusSuccessFinishResult);
                    return true;
                }
            }
            break;
        }

        return true;
    }

    bool
    LookupInModule (CommandInterpreter &interpreter, Module *module, CommandReturnObject &result, bool &syntax_error)
    {
        switch (m_options.m_type)
        {
            case eLookupTypeAddress:
                if (m_options.m_addr != LLDB_INVALID_ADDRESS)
                {
                    if (LookupAddressInModule (m_interpreter,
                                               result.GetOutputStream(),
                                               module,
                                               eSymbolContextEverything | (m_options.m_verbose ? static_cast<int>(eSymbolContextVariable) : 0),
                                               m_options.m_addr,
                                               m_options.m_offset,
                                               m_options.m_verbose))
                    {
                        result.SetStatus(eReturnStatusSuccessFinishResult);
                        return true;
                    }
                }
                break;

            case eLookupTypeSymbol:
                if (!m_options.m_str.empty())
                {
                    if (LookupSymbolInModule (m_interpreter,
                                              result.GetOutputStream(),
                                              module,
                                              m_options.m_str.c_str(),
                                              m_options.m_use_regex,
                                              m_options.m_verbose))
                    {
                        result.SetStatus(eReturnStatusSuccessFinishResult);
                        return true;
                    }
                }
                break;

            case eLookupTypeFileLine:
                if (m_options.m_file)
                {
                    if (LookupFileAndLineInModule (m_interpreter,
                                                   result.GetOutputStream(),
                                                   module,
                                                   m_options.m_file,
                                                   m_options.m_line_number,
                                                   m_options.m_include_inlines,
                                                   m_options.m_verbose))
                    {
                        result.SetStatus(eReturnStatusSuccessFinishResult);
                        return true;
                    }
                }
                break;

            case eLookupTypeFunctionOrSymbol:
            case eLookupTypeFunction:
                if (!m_options.m_str.empty())
                {
                    if (LookupFunctionInModule (m_interpreter,
                                                result.GetOutputStream(),
                                                module,
                                                m_options.m_str.c_str(),
                                                m_options.m_use_regex,
                                                m_options.m_include_inlines,
                                                m_options.m_type == eLookupTypeFunctionOrSymbol, // include symbols
                                                m_options.m_verbose))
                    {
                        result.SetStatus(eReturnStatusSuccessFinishResult);
                        return true;
                    }
                }
                break;

            case eLookupTypeType:
                if (!m_options.m_str.empty())
                {
                    if (LookupTypeInModule (m_interpreter,
                                            result.GetOutputStream(),
                                            module,
                                            m_options.m_str.c_str(),
                                            m_options.m_use_regex))
                    {
                        result.SetStatus(eReturnStatusSuccessFinishResult);
                        return true;
                    }
                }
                break;

            default:
                m_options.GenerateOptionUsage (result.GetErrorStream(), this);
                syntax_error = true;
                break;
        }

        result.SetStatus (eReturnStatusFailed);
        return false;
    }

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
        if (target == nullptr)
        {
            result.AppendError ("invalid target, create a debug target using the 'target create' command");
            result.SetStatus (eReturnStatusFailed);
            return false;
        }
        else
        {
            bool syntax_error = false;
            uint32_t i;
            uint32_t num_successful_lookups = 0;
            uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
            result.GetOutputStream().SetAddressByteSize(addr_byte_size);
            result.GetErrorStream().SetAddressByteSize(addr_byte_size);
            // Dump all sections for all modules images

            if (command.GetArgumentCount() == 0)
            {
                ModuleSP current_module;

                // Where it is possible to look in the current symbol context
                // first, try that.  If this search was successful and --all
                // was not passed, don't print anything else.
                if (LookupHere (m_interpreter, result, syntax_error))
                {
                    result.GetOutputStream().EOL();
                    num_successful_lookups++;
                    if (!m_options.m_print_all)
                    {
                        result.SetStatus (eReturnStatusSuccessFinishResult);
                        return result.Succeeded();
                    }
                }

                // Dump all sections for all other modules

                const ModuleList &target_modules = target->GetImages();
                std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
                const size_t num_modules = target_modules.GetSize();
                if (num_modules > 0)
                {
                    for (i = 0; i < num_modules && !syntax_error; ++i)
                    {
                        Module *module_pointer = target_modules.GetModulePointerAtIndexUnlocked(i);

                        if (module_pointer != current_module.get() &&
                            LookupInModule (m_interpreter, target_modules.GetModulePointerAtIndexUnlocked(i), result, syntax_error))
                        {
                            result.GetOutputStream().EOL();
                            num_successful_lookups++;
                        }
                    }
                }
                else
                {
                    result.AppendError ("the target has no associated executable images");
                    result.SetStatus (eReturnStatusFailed);
                    return false;
                }
            }
            else
            {
                // Dump specified images (by basename or fullpath)
                const char *arg_cstr;
                for (i = 0; (arg_cstr = command.GetArgumentAtIndex(i)) != nullptr && !syntax_error; ++i)
                {
                    ModuleList module_list;
                    const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, false);
                    if (num_matches > 0)
                    {
                        for (size_t j=0; j<num_matches; ++j)
                        {
                            Module *module = module_list.GetModulePointerAtIndex(j);
                            if (module)
                            {
                                if (LookupInModule (m_interpreter, module, result, syntax_error))
                                {
                                    result.GetOutputStream().EOL();
                                    num_successful_lookups++;
                                }
                            }
                        }
                    }
                    else
                        result.AppendWarningWithFormat("Unable to find an image that matches '%s'.\n", arg_cstr);
                }
            }

            if (num_successful_lookups > 0)
                result.SetStatus (eReturnStatusSuccessFinishResult);
            else
                result.SetStatus (eReturnStatusFailed);
        }
        return result.Succeeded();
    }

    CommandOptions m_options;
};

OptionDefinition
CommandObjectTargetModulesLookup::CommandOptions::g_option_table[] =
{
    { LLDB_OPT_SET_1,   true,  "address",    'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Lookup an address in one or more target modules."},
    { LLDB_OPT_SET_1,   false, "offset",     'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeOffset,           "When looking up an address subtract <offset> from any addresses before doing the lookup."},
    { LLDB_OPT_SET_2| LLDB_OPT_SET_4 | LLDB_OPT_SET_5
      /* FIXME: re-enable this for types when the LookupTypeInModule actually uses the regex option: | LLDB_OPT_SET_6 */ ,
                        false, "regex",      'r', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,             "The <name> argument for name lookups are regular expressions."},
    { LLDB_OPT_SET_2,   true,  "symbol",     's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeSymbol,           "Lookup a symbol by name in the symbol tables in one or more target modules."},
    { LLDB_OPT_SET_3,   true,  "file",       'f', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFilename,         "Lookup a file by fullpath or basename in one or more target modules."},
    { LLDB_OPT_SET_3,   false, "line",       'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLineNum,          "Lookup a line number in a file (must be used in conjunction with --file)."},
    { LLDB_OPT_SET_FROM_TO(3,5),
                        false, "no-inlines", 'i', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,             "Ignore inline entries (must be used in conjunction with --file or --function)."},
    { LLDB_OPT_SET_4,   true,  "function",   'F', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFunctionName,     "Lookup a function by name in the debug symbols in one or more target modules."},
    { LLDB_OPT_SET_5,   true,  "name",       'n', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFunctionOrSymbol, "Lookup a function or symbol by name in one or more target modules."},
    { LLDB_OPT_SET_6,   true,  "type",       't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeName,             "Lookup a type by name in the debug symbols in one or more target modules."},
    { LLDB_OPT_SET_ALL, false, "verbose",    'v', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,             "Enable verbose lookup information."},
    { LLDB_OPT_SET_ALL, false, "all",        'A', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,             "Print all matches, not just the best match, if a best match is available."},
    { 0,                false, nullptr,           0, 0,                 nullptr, nullptr, 0, eArgTypeNone,             nullptr }
};

#pragma mark CommandObjectMultiwordImageSearchPaths

//-------------------------------------------------------------------------
// CommandObjectMultiwordImageSearchPaths
//-------------------------------------------------------------------------

class CommandObjectTargetModulesImageSearchPaths : public CommandObjectMultiword
{
public:
    CommandObjectTargetModulesImageSearchPaths(CommandInterpreter &interpreter)
        : CommandObjectMultiword(interpreter, "target modules search-paths",
                                 "Commands for managing module search paths for a target.",
                                 "target modules search-paths <subcommand> [<subcommand-options>]")
    {
        LoadSubCommand ("add",     CommandObjectSP (new CommandObjectTargetModulesSearchPathsAdd (interpreter)));
        LoadSubCommand ("clear",   CommandObjectSP (new CommandObjectTargetModulesSearchPathsClear (interpreter)));
        LoadSubCommand ("insert",  CommandObjectSP (new CommandObjectTargetModulesSearchPathsInsert (interpreter)));
        LoadSubCommand ("list",    CommandObjectSP (new CommandObjectTargetModulesSearchPathsList (interpreter)));
        LoadSubCommand ("query",   CommandObjectSP (new CommandObjectTargetModulesSearchPathsQuery (interpreter)));
    }

    ~CommandObjectTargetModulesImageSearchPaths() override = default;
};

#pragma mark CommandObjectTargetModules

//-------------------------------------------------------------------------
// CommandObjectTargetModules
//-------------------------------------------------------------------------

class CommandObjectTargetModules : public CommandObjectMultiword
{
public:
    //------------------------------------------------------------------
    // Constructors and Destructors
    //------------------------------------------------------------------
    CommandObjectTargetModules(CommandInterpreter &interpreter)
        : CommandObjectMultiword(interpreter, "target modules",
                                 "Commands for accessing information for one or more target modules.",
                                 "target modules <sub-command> ...")
    {
        LoadSubCommand ("add",          CommandObjectSP (new CommandObjectTargetModulesAdd (interpreter)));
        LoadSubCommand ("load",         CommandObjectSP (new CommandObjectTargetModulesLoad (interpreter)));
        LoadSubCommand ("dump",         CommandObjectSP (new CommandObjectTargetModulesDump (interpreter)));
        LoadSubCommand ("list",         CommandObjectSP (new CommandObjectTargetModulesList (interpreter)));
        LoadSubCommand ("lookup",       CommandObjectSP (new CommandObjectTargetModulesLookup (interpreter)));
        LoadSubCommand ("search-paths", CommandObjectSP (new CommandObjectTargetModulesImageSearchPaths (interpreter)));
        LoadSubCommand ("show-unwind",  CommandObjectSP (new CommandObjectTargetModulesShowUnwind (interpreter)));

    }

    ~CommandObjectTargetModules() override = default;

private:
    //------------------------------------------------------------------
    // For CommandObjectTargetModules only
    //------------------------------------------------------------------
    DISALLOW_COPY_AND_ASSIGN (CommandObjectTargetModules);
};

class CommandObjectTargetSymbolsAdd : public CommandObjectParsed
{
public:
    CommandObjectTargetSymbolsAdd (CommandInterpreter &interpreter) :
        CommandObjectParsed (interpreter,
                             "target symbols add",
                             "Add a debug symbol file to one of the target's current modules by specifying a path to a debug symbols file, or using the options to specify a module to download symbols for.",
                             "target symbols add [<symfile>]", eCommandRequiresTarget),
        m_option_group (interpreter),
        m_file_option (LLDB_OPT_SET_1, false, "shlib", 's', CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Fullpath or basename for module to find debug symbols for."),
        m_current_frame_option (LLDB_OPT_SET_2, false, "frame", 'F', "Locate the debug symbols the currently selected frame.", false, true)

    {
        m_option_group.Append (&m_uuid_option_group, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Append (&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
        m_option_group.Append (&m_current_frame_option, LLDB_OPT_SET_2, LLDB_OPT_SET_2);
        m_option_group.Finalize();
    }

    ~CommandObjectTargetSymbolsAdd() override = default;

    int
    HandleArgumentCompletion (Args &input,
                              int &cursor_index,
                              int &cursor_char_position,
                              OptionElementVector &opt_element_vector,
                              int match_start_point,
                              int max_return_elements,
                              bool &word_complete,
                              StringList &matches) override
    {
        std::string completion_str (input.GetArgumentAtIndex(cursor_index));
        completion_str.erase (cursor_char_position);

        CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter,
                                                            CommandCompletions::eDiskFileCompletion,
                                                            completion_str.c_str(),
                                                            match_start_point,
                                                            max_return_elements,
                                                            nullptr,
                                                            word_complete,
                                                            matches);
        return matches.GetSize();
    }

    Options *
    GetOptions () override
    {
        return &m_option_group;
    }

protected:
    bool
    AddModuleSymbols (Target *target,
                      ModuleSpec &module_spec,
                      bool &flush,
                      CommandReturnObject &result)
    {
        const FileSpec &symbol_fspec = module_spec.GetSymbolFileSpec();
        if (symbol_fspec)
        {
            char symfile_path[PATH_MAX];
            symbol_fspec.GetPath (symfile_path, sizeof(symfile_path));

            if (!module_spec.GetUUID().IsValid())
            {
                if (!module_spec.GetFileSpec() && !module_spec.GetPlatformFileSpec())
                    module_spec.GetFileSpec().GetFilename() = symbol_fspec.GetFilename();
            }
            // We now have a module that represents a symbol file
            // that can be used for a module that might exist in the
            // current target, so we need to find that module in the
            // target
            ModuleList matching_module_list;

            size_t num_matches = 0;
            // First extract all module specs from the symbol file
            lldb_private::ModuleSpecList symfile_module_specs;
            if (ObjectFile::GetModuleSpecifications(module_spec.GetSymbolFileSpec(), 0, 0, symfile_module_specs))
            {
                // Now extract the module spec that matches the target architecture
                ModuleSpec target_arch_module_spec;
                ModuleSpec symfile_module_spec;
                target_arch_module_spec.GetArchitecture() = target->GetArchitecture();
                if (symfile_module_specs.FindMatchingModuleSpec(target_arch_module_spec, symfile_module_spec))
                {
                    // See if it has a UUID?
                    if (symfile_module_spec.GetUUID().IsValid())
                    {
                        // It has a UUID, look for this UUID in the target modules
                        ModuleSpec symfile_uuid_module_spec;
                        symfile_uuid_module_spec.GetUUID() = symfile_module_spec.GetUUID();
                        num_matches = target->GetImages().FindModules (symfile_uuid_module_spec, matching_module_list);
                    }
                }

                if (num_matches == 0)
                {
                    // No matches yet, iterate through the module specs to find a UUID value that
                    // we can match up to an image in our target
                    const size_t num_symfile_module_specs = symfile_module_specs.GetSize();
                    for (size_t i = 0; i < num_symfile_module_specs && num_matches == 0; ++i)
                    {
                        if (symfile_module_specs.GetModuleSpecAtIndex(i, symfile_module_spec))
                        {
                            if (symfile_module_spec.GetUUID().IsValid())
                            {
                                // It has a UUID, look for this UUID in the target modules
                                ModuleSpec symfile_uuid_module_spec;
                                symfile_uuid_module_spec.GetUUID() = symfile_module_spec.GetUUID();
                                num_matches = target->GetImages().FindModules (symfile_uuid_module_spec, matching_module_list);
                            }
                        }
                    }
                }
            }

            // Just try to match up the file by basename if we have no matches at this point
            if (num_matches == 0)
                num_matches = target->GetImages().FindModules (module_spec, matching_module_list);

            while (num_matches == 0)
            {
                ConstString filename_no_extension(module_spec.GetFileSpec().GetFileNameStrippingExtension());
                // Empty string returned, lets bail
                if (!filename_no_extension)
                    break;

                // Check if there was no extension to strip and the basename is the same
                if (filename_no_extension == module_spec.GetFileSpec().GetFilename())
                    break;

                // Replace basename with one less extension
                module_spec.GetFileSpec().GetFilename() = filename_no_extension;

                num_matches = target->GetImages().FindModules (module_spec, matching_module_list);
            }

            if (num_matches > 1)
            {
                result.AppendErrorWithFormat ("multiple modules match symbol file '%s', use the --uuid option to resolve the ambiguity.\n", symfile_path);
            }
            else if (num_matches == 1)
            {
                ModuleSP module_sp (matching_module_list.GetModuleAtIndex(0));

                // The module has not yet created its symbol vendor, we can just
                // give the existing target module the symfile path to use for
                // when it decides to create it!
                module_sp->SetSymbolFileFileSpec (symbol_fspec);

                SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor(true, &result.GetErrorStream());
                if (symbol_vendor)
                {
                    SymbolFile *symbol_file = symbol_vendor->GetSymbolFile();

                    if (symbol_file)
                    {
                        ObjectFile *object_file = symbol_file->GetObjectFile();

                        if (object_file && object_file->GetFileSpec() == symbol_fspec)
                        {
                            // Provide feedback that the symfile has been successfully added.
                            const FileSpec &module_fs = module_sp->GetFileSpec();
                            result.AppendMessageWithFormat("symbol file '%s' has been added to '%s'\n",
                                                           symfile_path,
                                                           module_fs.GetPath().c_str());

                            // Let clients know something changed in the module
                            // if it is currently loaded
                            ModuleList module_list;
                            module_list.Append (module_sp);
                            target->SymbolsDidLoad (module_list);

                            // Make sure we load any scripting resources that may be embedded
                            // in the debug info files in case the platform supports that.
                            Error error;
                            StreamString feedback_stream;
                            module_sp->LoadScriptingResourceInTarget (target, error,&feedback_stream);
                            if (error.Fail() && error.AsCString())
                                result.AppendWarningWithFormat("unable to load scripting data for module %s - error reported was %s",
                                                               module_sp->GetFileSpec().GetFileNameStrippingExtension().GetCString(),
                                                               error.AsCString());
                            else if (feedback_stream.GetSize())
                                result.AppendWarningWithFormat("%s",feedback_stream.GetData());

                            flush = true;
                            result.SetStatus (eReturnStatusSuccessFinishResult);
                            return true;
                        }
                    }
                }
                // Clear the symbol file spec if anything went wrong
                module_sp->SetSymbolFileFileSpec (FileSpec());
            }

            if (module_spec.GetUUID().IsValid())
            {
                StreamString ss_symfile_uuid;
                module_spec.GetUUID().Dump(&ss_symfile_uuid);
                result.AppendErrorWithFormat ("symbol file '%s' (%s) does not match any existing module%s\n",
                                              symfile_path,
                                              ss_symfile_uuid.GetData(),
                                              (symbol_fspec.GetFileType() != FileSpec::eFileTypeRegular)
                                                ? "\n       please specify the full path to the symbol file"
                                                : "");
            }
            else
            {
                result.AppendErrorWithFormat ("symbol file '%s' does not match any existing module%s\n",
                                              symfile_path,
                                              (symbol_fspec.GetFileType() != FileSpec::eFileTypeRegular)
                                                ? "\n       please specify the full path to the symbol file"
                                                : "");
            }
        }
        else
        {
            result.AppendError ("one or more executable image paths must be specified");
        }
        result.SetStatus (eReturnStatusFailed);
        return false;
    }

    bool
    DoExecute (Args& args, CommandReturnObject &result) override
    {
        Target *target = m_exe_ctx.GetTargetPtr();
        result.SetStatus (eReturnStatusFailed);
        bool flush = false;
        ModuleSpec module_spec;
        const bool uuid_option_set = m_uuid_option_group.GetOptionValue().OptionWasSet();
        const bool file_option_set = m_file_option.GetOptionValue().OptionWasSet();
        const bool frame_option_set = m_current_frame_option.GetOptionValue().OptionWasSet();
        const size_t argc = args.GetArgumentCount();

        if (argc == 0)
        {
            if (uuid_option_set || file_option_set || frame_option_set)
            {
                bool success = false;
                bool error_set = false;
                if (frame_option_set)
                {
                    Process *process = m_exe_ctx.GetProcessPtr();
                    if (process)
                    {
                        const StateType process_state = process->GetState();
                        if (StateIsStoppedState (process_state, true))
                        {
                            StackFrame *frame = m_exe_ctx.GetFramePtr();
                            if (frame)
                            {
                                ModuleSP frame_module_sp (frame->GetSymbolContext(eSymbolContextModule).module_sp);
                                if (frame_module_sp)
                                {
                                    if (frame_module_sp->GetPlatformFileSpec().Exists())
                                    {
                                        module_spec.GetArchitecture() = frame_module_sp->GetArchitecture();
                                        module_spec.GetFileSpec() = frame_module_sp->GetPlatformFileSpec();
                                    }
                                    module_spec.GetUUID() = frame_module_sp->GetUUID();
                                    success = module_spec.GetUUID().IsValid() || module_spec.GetFileSpec();
                                }
                                else
                                {
                                    result.AppendError ("frame has no module");
                                    error_set = true;
                                }
                            }
                            else
                            {
                                result.AppendError ("invalid current frame");
                                error_set = true;
                            }
                        }
                        else
                        {
                            result.AppendErrorWithFormat ("process is not stopped: %s", StateAsCString(process_state));
                            error_set = true;
                        }
                    }
                    else
                    {
                        result.AppendError ("a process must exist in order to use the --frame option");
                        error_set = true;
                    }
                }
                else
                {
                    if (uuid_option_set)
                    {
                        module_spec.GetUUID() = m_uuid_option_group.GetOptionValue().GetCurrentValue();
                        success |= module_spec.GetUUID().IsValid();
                    }
                    else if (file_option_set)
                    {
                        module_spec.GetFileSpec() = m_file_option.GetOptionValue().GetCurrentValue();
                        ModuleSP module_sp (target->GetImages().FindFirstModule(module_spec));
                        if (module_sp)
                        {
                            module_spec.GetFileSpec() = module_sp->GetFileSpec();
                            module_spec.GetPlatformFileSpec() = module_sp->GetPlatformFileSpec();
                            module_spec.GetUUID() = module_sp->GetUUID();
                            module_spec.GetArchitecture() = module_sp->GetArchitecture();
                        }
                        else
                        {
                            module_spec.GetArchitecture() = target->GetArchitecture();
                        }
                        success |= module_spec.GetUUID().IsValid() || module_spec.GetFileSpec().Exists();
                    }
                }

                if (success)
                {
                    if (Symbols::DownloadObjectAndSymbolFile (module_spec))
                    {
                        if (module_spec.GetSymbolFileSpec())
                            success = AddModuleSymbols (target, module_spec, flush, result);
                    }
                }

                if (!success && !error_set)
                {
                    StreamString error_strm;
                    if (uuid_option_set)
                    {
                        error_strm.PutCString("unable to find debug symbols for UUID ");
                        module_spec.GetUUID().Dump (&error_strm);
                    }
                    else if (file_option_set)
                    {
                        error_strm.PutCString("unable to find debug symbols for the executable file ");
                        error_strm << module_spec.GetFileSpec();
                    }
                    else if (frame_option_set)
                    {
                        error_strm.PutCString("unable to find debug symbols for the current frame");                            
                    }
                    result.AppendError (error_strm.GetData());
                }
            }
            else
            {
                result.AppendError ("one or more symbol file paths must be specified, or options must be specified");
            }
        }
        else
        {
            if (uuid_option_set)
            {
                result.AppendError ("specify either one or more paths to symbol files or use the --uuid option without arguments");
            }
            else if (file_option_set)
            {
                result.AppendError ("specify either one or more paths to symbol files or use the --file option without arguments");
            }
            else if (frame_option_set)
            {
                result.AppendError ("specify either one or more paths to symbol files or use the --frame option without arguments");
            }
            else
            {
                PlatformSP platform_sp (target->GetPlatform());

                for (size_t i = 0; i < argc; ++i)
                {
                    const char *symfile_path = args.GetArgumentAtIndex(i);
                    if (symfile_path)
                    {
                        module_spec.GetSymbolFileSpec().SetFile(symfile_path, true);
                        if (platform_sp)
                        {
                            FileSpec symfile_spec;
                            if (platform_sp->ResolveSymbolFile(*target, module_spec, symfile_spec).Success())
                                module_spec.GetSymbolFileSpec() = symfile_spec;
                        }

                        ArchSpec arch;
                        bool symfile_exists = module_spec.GetSymbolFileSpec().Exists();

                        if (symfile_exists)
                        {
                            if (!AddModuleSymbols (target, module_spec, flush, result))
                                break;
                        }
                        else
                        {
                            char resolved_symfile_path[PATH_MAX];
                            if (module_spec.GetSymbolFileSpec().GetPath (resolved_symfile_path, sizeof(resolved_symfile_path)))
                            {
                                if (strcmp (resolved_symfile_path, symfile_path) != 0)
                                {
                                    result.AppendErrorWithFormat ("invalid module path '%s' with resolved path '%s'\n", symfile_path, resolved_symfile_path);
                                    break;
                                }
                            }
                            result.AppendErrorWithFormat ("invalid module path '%s'\n", symfile_path);
                            break;
                        }
                    }
                }
            }
        }

        if (flush)
        {
            Process *process = m_exe_ctx.GetProcessPtr();
            if (process)
                process->Flush();
        }
        return result.Succeeded();
    }

    OptionGroupOptions m_option_group;
    OptionGroupUUID m_uuid_option_group;
    OptionGroupFile m_file_option;
    OptionGroupBoolean m_current_frame_option;
};

#pragma mark CommandObjectTargetSymbols

//-------------------------------------------------------------------------
// CommandObjectTargetSymbols
//-------------------------------------------------------------------------

class CommandObjectTargetSymbols : public CommandObjectMultiword
{
public:
    //------------------------------------------------------------------
    // Constructors and Destructors
    //------------------------------------------------------------------
    CommandObjectTargetSymbols(CommandInterpreter &interpreter)
        : CommandObjectMultiword(interpreter, "target symbols", "Commands for adding and managing debug symbol files.",
                                 "target symbols <sub-command> ...")
    {
        LoadSubCommand ("add", CommandObjectSP (new CommandObjectTargetSymbolsAdd (interpreter)));
    }

    ~CommandObjectTargetSymbols() override = default;

private:
    //------------------------------------------------------------------
    // For CommandObjectTargetModules only
    //------------------------------------------------------------------
    DISALLOW_COPY_AND_ASSIGN (CommandObjectTargetSymbols);
};

#pragma mark CommandObjectTargetStopHookAdd

//-------------------------------------------------------------------------
// CommandObjectTargetStopHookAdd
//-------------------------------------------------------------------------

class CommandObjectTargetStopHookAdd :
    public CommandObjectParsed,
    public IOHandlerDelegateMultiline
{
public:
    class CommandOptions : public Options
    {
    public:
        CommandOptions (CommandInterpreter &interpreter) :
            Options(interpreter),
            m_line_start(0),
            m_line_end (UINT_MAX),
            m_func_name_type_mask (eFunctionNameTypeAuto),
            m_sym_ctx_specified (false),
            m_thread_specified (false),
            m_use_one_liner (false),
            m_one_liner()
        {
        }

        ~CommandOptions() override = default;

        const OptionDefinition*
        GetDefinitions () override
        {
            return g_option_table;
        }

        Error
        SetOptionValue (uint32_t option_idx, const char *option_arg) override
        {
            Error error;
            const int short_option = m_getopt_table[option_idx].val;
            bool success;

            switch (short_option)
            {
                case 'c':
                    m_class_name = option_arg;
                    m_sym_ctx_specified = true;
                    break;

                case 'e':
                    m_line_end = StringConvert::ToUInt32 (option_arg, UINT_MAX, 0, &success);
                    if (!success)
                    {
                        error.SetErrorStringWithFormat ("invalid end line number: \"%s\"", option_arg);
                        break;
                    }
                    m_sym_ctx_specified = true;
                    break;

                case 'l':
                    m_line_start = StringConvert::ToUInt32 (option_arg, 0, 0, &success);
                    if (!success)
                    {
                        error.SetErrorStringWithFormat ("invalid start line number: \"%s\"", option_arg);
                        break;
                    }
                    m_sym_ctx_specified = true;
                    break;

                case 'i':
                    m_no_inlines = true;
                    break;

                case 'n':
                    m_function_name = option_arg;
                    m_func_name_type_mask |= eFunctionNameTypeAuto;
                    m_sym_ctx_specified = true;
                    break;

                case 'f':
                    m_file_name = option_arg;
                    m_sym_ctx_specified = true;
                    break;

                case 's':
                    m_module_name = option_arg;
                    m_sym_ctx_specified = true;
                    break;

                case 't' :
                    m_thread_id = StringConvert::ToUInt64(option_arg, LLDB_INVALID_THREAD_ID, 0);
                    if (m_thread_id == LLDB_INVALID_THREAD_ID)
                       error.SetErrorStringWithFormat ("invalid thread id string '%s'", option_arg);
                    m_thread_specified = true;
                    break;

                case 'T':
                    m_thread_name = option_arg;
                    m_thread_specified = true;
                    break;

                case 'q':
                    m_queue_name = option_arg;
                    m_thread_specified = true;
                    break;

                case 'x':
                    m_thread_index = StringConvert::ToUInt32(option_arg, UINT32_MAX, 0);
                    if (m_thread_id == UINT32_MAX)
                       error.SetErrorStringWithFormat ("invalid thread index string '%s'", option_arg);
                    m_thread_specified = true;
                    break;

                case 'o':
                    m_use_one_liner = true;
                    m_one_liner = option_arg;
                    break;

                default:
                    error.SetErrorStringWithFormat ("unrecognized option %c.", short_option);
                    break;
            }
            return error;
        }

        void
        OptionParsingStarting () override
        {
            m_class_name.clear();
            m_function_name.clear();
            m_line_start = 0;
            m_line_end = UINT_MAX;
            m_file_name.clear();
            m_module_name.clear();
            m_func_name_type_mask = eFunctionNameTypeAuto;
            m_thread_id = LLDB_INVALID_THREAD_ID;
            m_thread_index = UINT32_MAX;
            m_thread_name.clear();
            m_queue_name.clear();

            m_no_inlines = false;
            m_sym_ctx_specified = false;
            m_thread_specified = false;

            m_use_one_liner = false;
            m_one_liner.clear();
        }

        static OptionDefinition g_option_table[];

        std::string m_class_name;
        std::string m_function_name;
        uint32_t    m_line_start;
        uint32_t    m_line_end;
        std::string m_file_name;
        std::string m_module_name;
        uint32_t m_func_name_type_mask;  // A pick from lldb::FunctionNameType.
        lldb::tid_t m_thread_id;
        uint32_t m_thread_index;
        std::string m_thread_name;
        std::string m_queue_name;
        bool        m_sym_ctx_specified;
        bool        m_no_inlines;
        bool        m_thread_specified;
        // Instance variables to hold the values for one_liner options.
        bool m_use_one_liner;
        std::string m_one_liner;
    };

    CommandObjectTargetStopHookAdd (CommandInterpreter &interpreter) :
        CommandObjectParsed (interpreter,
                             "target stop-hook add",
                             "Add a hook to be executed when the target stops.",
                             "target stop-hook add"),
        IOHandlerDelegateMultiline ("DONE", IOHandlerDelegate::Completion::LLDBCommand),
        m_options (interpreter)
    {
    }

    ~CommandObjectTargetStopHookAdd() override = default;

    Options *
    GetOptions () override
    {
        return &m_options;
    }

protected:
    void
    IOHandlerActivated (IOHandler &io_handler) override
    {
        StreamFileSP output_sp(io_handler.GetOutputStreamFile());
        if (output_sp)
        {
            output_sp->PutCString("Enter your stop hook command(s).  Type 'DONE' to end.\n");
            output_sp->Flush();
        }
    }

    void
    IOHandlerInputComplete (IOHandler &io_handler, std::string &line) override
    {
        if (m_stop_hook_sp)
        {
            if (line.empty())
            {
                StreamFileSP error_sp(io_handler.GetErrorStreamFile());
                if (error_sp)
                {
                    error_sp->Printf("error: stop hook #%" PRIu64 " aborted, no commands.\n", m_stop_hook_sp->GetID());
                    error_sp->Flush();
                }
                Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
                if (target)
                    target->RemoveStopHookByID(m_stop_hook_sp->GetID());
            }
            else
            {
                m_stop_hook_sp->GetCommandPointer()->SplitIntoLines(line);
                StreamFileSP output_sp(io_handler.GetOutputStreamFile());
                if (output_sp)
                {
                    output_sp->Printf("Stop hook #%" PRIu64 " added.\n", m_stop_hook_sp->GetID());
                    output_sp->Flush();
                }
            }
            m_stop_hook_sp.reset();
        }
        io_handler.SetIsDone(true);
    }

    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        m_stop_hook_sp.reset();

        Target *target = GetSelectedOrDummyTarget();
        if (target)
        {
            Target::StopHookSP new_hook_sp = target->CreateStopHook();

            //  First step, make the specifier.
            std::unique_ptr<SymbolContextSpecifier> specifier_ap;
            if (m_options.m_sym_ctx_specified)
            {
                specifier_ap.reset(new SymbolContextSpecifier(m_interpreter.GetDebugger().GetSelectedTarget()));

                if (!m_options.m_module_name.empty())
                {
                    specifier_ap->AddSpecification (m_options.m_module_name.c_str(), SymbolContextSpecifier::eModuleSpecified);
                }

                if (!m_options.m_class_name.empty())
                {
                    specifier_ap->AddSpecification (m_options.m_class_name.c_str(), SymbolContextSpecifier::eClassOrNamespaceSpecified);
                }

                if (!m_options.m_file_name.empty())
                {
                    specifier_ap->AddSpecification (m_options.m_file_name.c_str(), SymbolContextSpecifier::eFileSpecified);
                }

                if (m_options.m_line_start != 0)
                {
                    specifier_ap->AddLineSpecification (m_options.m_line_start, SymbolContextSpecifier::eLineStartSpecified);
                }

                if (m_options.m_line_end != UINT_MAX)
                {
                    specifier_ap->AddLineSpecification (m_options.m_line_end, SymbolContextSpecifier::eLineEndSpecified);
                }

                if (!m_options.m_function_name.empty())
                {
                    specifier_ap->AddSpecification (m_options.m_function_name.c_str(), SymbolContextSpecifier::eFunctionSpecified);
                }
            }

            if (specifier_ap)
                new_hook_sp->SetSpecifier (specifier_ap.release());

            // Next see if any of the thread options have been entered:

            if (m_options.m_thread_specified)
            {
                ThreadSpec *thread_spec = new ThreadSpec();

                if (m_options.m_thread_id != LLDB_INVALID_THREAD_ID)
                {
                    thread_spec->SetTID (m_options.m_thread_id);
                }

                if (m_options.m_thread_index != UINT32_MAX)
                    thread_spec->SetIndex (m_options.m_thread_index);

                if (!m_options.m_thread_name.empty())
                    thread_spec->SetName (m_options.m_thread_name.c_str());

                if (!m_options.m_queue_name.empty())
                    thread_spec->SetQueueName (m_options.m_queue_name.c_str());

                new_hook_sp->SetThreadSpecifier (thread_spec);

            }
            if (m_options.m_use_one_liner)
            {
                // Use one-liner.
                new_hook_sp->GetCommandPointer()->AppendString (m_options.m_one_liner.c_str());
                result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n", new_hook_sp->GetID());
            }
            else
            {
                m_stop_hook_sp = new_hook_sp;
                m_interpreter.GetLLDBCommandsFromIOHandler("> ",   // Prompt
                                                           *this,  // IOHandlerDelegate
                                                           true,   // Run IOHandler in async mode
                                                           nullptr);  // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions

            }
            result.SetStatus (eReturnStatusSuccessFinishNoResult);
        }
        else
        {
            result.AppendError ("invalid target\n");
            result.SetStatus (eReturnStatusFailed);
        }

        return result.Succeeded();
    }

private:
    CommandOptions m_options;
    Target::StopHookSP m_stop_hook_sp;
};

OptionDefinition
CommandObjectTargetStopHookAdd::CommandOptions::g_option_table[] =
{
    { LLDB_OPT_SET_ALL, false, "one-liner", 'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeOneLiner,
        "Specify a one-line breakpoint command inline. Be sure to surround it with quotes." },
    { LLDB_OPT_SET_ALL, false, "shlib", 's', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eModuleCompletion, eArgTypeShlibName,
        "Set the module within which the stop-hook is to be run."},
    { LLDB_OPT_SET_ALL, false, "thread-index", 'x', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadIndex,
        "The stop hook is run only for the thread whose index matches this argument."},
    { LLDB_OPT_SET_ALL, false, "thread-id", 't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadID,
        "The stop hook is run only for the thread whose TID matches this argument."},
    { LLDB_OPT_SET_ALL, false, "thread-name", 'T', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadName,
        "The stop hook is run only for the thread whose thread name matches this argument."},
    { LLDB_OPT_SET_ALL, false, "queue-name", 'q', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeQueueName,
        "The stop hook is run only for threads in the queue whose name is given by this argument."},
    { LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eSourceFileCompletion, eArgTypeFilename,
        "Specify the source file within which the stop-hook is to be run." },
    { LLDB_OPT_SET_1, false, "start-line", 'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLineNum,
        "Set the start of the line range for which the stop-hook is to be run."},
    { LLDB_OPT_SET_1, false, "end-line", 'e', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLineNum,
        "Set the end of the line range for which the stop-hook is to be run."},
    { LLDB_OPT_SET_2, false, "classname", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeClassName,
        "Specify the class within which the stop-hook is to be run." },
    { LLDB_OPT_SET_3, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName,
        "Set the function name within which the stop hook will be run." },
    { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr }
};

#pragma mark CommandObjectTargetStopHookDelete

//-------------------------------------------------------------------------
// CommandObjectTargetStopHookDelete
//-------------------------------------------------------------------------

class CommandObjectTargetStopHookDelete : public CommandObjectParsed
{
public:
    CommandObjectTargetStopHookDelete (CommandInterpreter &interpreter) :
        CommandObjectParsed (interpreter,
                             "target stop-hook delete",
                             "Delete a stop-hook.",
                             "target stop-hook delete [<idx>]")
    {
    }

    ~CommandObjectTargetStopHookDelete() override = default;

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        Target *target = GetSelectedOrDummyTarget();
        if (target)
        {
            // FIXME: see if we can use the breakpoint id style parser?
            size_t num_args = command.GetArgumentCount();
            if (num_args == 0)
            {
                if (!m_interpreter.Confirm ("Delete all stop hooks?", true))
                {
                    result.SetStatus (eReturnStatusFailed);
                    return false;
                }
                else
                {
                    target->RemoveAllStopHooks();
                }
            }
            else
            {
                bool success;
                for (size_t i = 0; i < num_args; i++)
                {
                    lldb::user_id_t user_id = StringConvert::ToUInt32 (command.GetArgumentAtIndex(i), 0, 0, &success);
                    if (!success)
                    {
                        result.AppendErrorWithFormat ("invalid stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i));
                        result.SetStatus(eReturnStatusFailed);
                        return false;
                    }
                    success = target->RemoveStopHookByID (user_id);
                    if (!success)
                    {
                        result.AppendErrorWithFormat ("unknown stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i));
                        result.SetStatus(eReturnStatusFailed);
                        return false;
                    }
                }
            }
            result.SetStatus (eReturnStatusSuccessFinishNoResult);
        }
        else
        {
            result.AppendError ("invalid target\n");
            result.SetStatus (eReturnStatusFailed);
        }

        return result.Succeeded();
    }
};

#pragma mark CommandObjectTargetStopHookEnableDisable

//-------------------------------------------------------------------------
// CommandObjectTargetStopHookEnableDisable
//-------------------------------------------------------------------------

class CommandObjectTargetStopHookEnableDisable : public CommandObjectParsed
{
public:
    CommandObjectTargetStopHookEnableDisable (CommandInterpreter &interpreter, bool enable, const char *name, const char *help, const char *syntax) :
        CommandObjectParsed (interpreter,
                             name,
                             help,
                             syntax),
        m_enable (enable)
    {
    }

    ~CommandObjectTargetStopHookEnableDisable() override = default;

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        Target *target = GetSelectedOrDummyTarget();
        if (target)
        {
            // FIXME: see if we can use the breakpoint id style parser?
            size_t num_args = command.GetArgumentCount();
            bool success;

            if (num_args == 0)
            {
                target->SetAllStopHooksActiveState (m_enable);
            }
            else
            {
                for (size_t i = 0; i < num_args; i++)
                {
                    lldb::user_id_t user_id = StringConvert::ToUInt32 (command.GetArgumentAtIndex(i), 0, 0, &success);
                    if (!success)
                    {
                        result.AppendErrorWithFormat ("invalid stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i));
                        result.SetStatus(eReturnStatusFailed);
                        return false;
                    }
                    success = target->SetStopHookActiveStateByID (user_id, m_enable);
                    if (!success)
                    {
                        result.AppendErrorWithFormat ("unknown stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i));
                        result.SetStatus(eReturnStatusFailed);
                        return false;
                    }
                }
            }
            result.SetStatus (eReturnStatusSuccessFinishNoResult);
        }
        else
        {
            result.AppendError ("invalid target\n");
            result.SetStatus (eReturnStatusFailed);
        }
        return result.Succeeded();
    }
private:
    bool m_enable;
};

#pragma mark CommandObjectTargetStopHookList

//-------------------------------------------------------------------------
// CommandObjectTargetStopHookList
//-------------------------------------------------------------------------

class CommandObjectTargetStopHookList : public CommandObjectParsed
{
public:
    CommandObjectTargetStopHookList (CommandInterpreter &interpreter) :
        CommandObjectParsed (interpreter,
                             "target stop-hook list",
                             "List all stop-hooks.",
                             "target stop-hook list [<type>]")
    {
    }

    ~CommandObjectTargetStopHookList() override = default;

protected:
    bool
    DoExecute (Args& command, CommandReturnObject &result) override
    {
        Target *target = GetSelectedOrDummyTarget();
        if (!target)
        {
            result.AppendError ("invalid target\n");
            result.SetStatus (eReturnStatusFailed);
            return result.Succeeded();
        }

        size_t num_hooks = target->GetNumStopHooks ();
        if (num_hooks == 0)
        {
            result.GetOutputStream().PutCString ("No stop hooks.\n");
        }
        else
        {
            for (size_t i = 0; i < num_hooks; i++)
            {
                Target::StopHookSP this_hook = target->GetStopHookAtIndex (i);
                if (i > 0)
                    result.GetOutputStream().PutCString ("\n");
                this_hook->GetDescription (&(result.GetOutputStream()), eDescriptionLevelFull);
            }
        }
        result.SetStatus (eReturnStatusSuccessFinishResult);
        return result.Succeeded();
    }
};

#pragma mark CommandObjectMultiwordTargetStopHooks

//-------------------------------------------------------------------------
// CommandObjectMultiwordTargetStopHooks
//-------------------------------------------------------------------------

class CommandObjectMultiwordTargetStopHooks : public CommandObjectMultiword
{
public:
    CommandObjectMultiwordTargetStopHooks(CommandInterpreter &interpreter)
        : CommandObjectMultiword(interpreter, "target stop-hook",
                                 "Commands for operating on debugger target stop-hooks.",
                                 "target stop-hook <subcommand> [<subcommand-options>]")
    {
        LoadSubCommand ("add",      CommandObjectSP (new CommandObjectTargetStopHookAdd (interpreter)));
        LoadSubCommand ("delete",   CommandObjectSP (new CommandObjectTargetStopHookDelete (interpreter)));
        LoadSubCommand ("disable",  CommandObjectSP (new CommandObjectTargetStopHookEnableDisable (interpreter, 
                                                                                                   false, 
                                                                                                   "target stop-hook disable [<id>]",
                                                                                                   "Disable a stop-hook.",
                                                                                                   "target stop-hook disable")));
        LoadSubCommand ("enable",   CommandObjectSP (new CommandObjectTargetStopHookEnableDisable (interpreter, 
                                                                                                   true, 
                                                                                                   "target stop-hook enable [<id>]",
                                                                                                   "Enable a stop-hook.",
                                                                                                   "target stop-hook enable")));
        LoadSubCommand ("list",     CommandObjectSP (new CommandObjectTargetStopHookList (interpreter)));
    }

    ~CommandObjectMultiwordTargetStopHooks() override = default;
};

#pragma mark CommandObjectMultiwordTarget

//-------------------------------------------------------------------------
// CommandObjectMultiwordTarget
//-------------------------------------------------------------------------

CommandObjectMultiwordTarget::CommandObjectMultiwordTarget(CommandInterpreter &interpreter)
    : CommandObjectMultiword(interpreter, "target", "Commands for operating on debugger targets.",
                             "target <subcommand> [<subcommand-options>]")
{
    LoadSubCommand ("create",    CommandObjectSP (new CommandObjectTargetCreate (interpreter)));
    LoadSubCommand ("delete",    CommandObjectSP (new CommandObjectTargetDelete (interpreter)));
    LoadSubCommand ("list",      CommandObjectSP (new CommandObjectTargetList   (interpreter)));
    LoadSubCommand ("select",    CommandObjectSP (new CommandObjectTargetSelect (interpreter)));
    LoadSubCommand ("stop-hook", CommandObjectSP (new CommandObjectMultiwordTargetStopHooks (interpreter)));
    LoadSubCommand ("modules",   CommandObjectSP (new CommandObjectTargetModules (interpreter)));
    LoadSubCommand ("symbols",   CommandObjectSP (new CommandObjectTargetSymbols (interpreter)));
    LoadSubCommand ("variable",  CommandObjectSP (new CommandObjectTargetVariable (interpreter)));
}

CommandObjectMultiwordTarget::~CommandObjectMultiwordTarget() = default;
