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

#include "lldb/Core/Debugger.h"

// C Includes
// C++ Includes
#include <map>
#include <mutex>

// Other libraries and framework includes
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/DynamicLibrary.h"

// Project includes
#include "lldb/lldb-private.h"
#include "lldb/Core/FormatEntity.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginInterface.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/RegisterValue.h"
#include "lldb/Core/State.h"
#include "lldb/Core/StreamAsynchronousIO.h"
#include "lldb/Core/StreamCallback.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Core/StructuredData.h"
#include "lldb/Core/Timer.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Core/ValueObjectVariable.h"
#include "lldb/DataFormatters/DataVisualization.h"
#include "lldb/DataFormatters/FormatManager.h"
#include "lldb/DataFormatters/TypeSummary.h"
#include "lldb/Expression/REPL.h"
#include "lldb/Host/ConnectionFileDescriptor.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Host/Terminal.h"
#include "lldb/Host/ThreadLauncher.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/OptionValueProperties.h"
#include "lldb/Interpreter/OptionValueSInt64.h"
#include "lldb/Interpreter/OptionValueString.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/TargetList.h"
#include "lldb/Target/Language.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/StopInfo.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "lldb/Utility/AnsiTerminal.h"

using namespace lldb;
using namespace lldb_private;

static lldb::user_id_t g_unique_id = 1;
static size_t g_debugger_event_thread_stack_bytes = 8 * 1024 * 1024;

#pragma mark Static Functions

typedef std::vector<DebuggerSP> DebuggerList;
static std::recursive_mutex *g_debugger_list_mutex_ptr = nullptr; // NOTE: intentional leak to avoid issues with C++ destructor chain
static DebuggerList *g_debugger_list_ptr = nullptr; // NOTE: intentional leak to avoid issues with C++ destructor chain

OptionEnumValueElement
g_show_disassembly_enum_values[] =
{
    { Debugger::eStopDisassemblyTypeNever,          "never",            "Never show disassembly when displaying a stop context."},
    { Debugger::eStopDisassemblyTypeNoDebugInfo,    "no-debuginfo",     "Show disassembly when there is no debug information."},
    { Debugger::eStopDisassemblyTypeNoSource,       "no-source",        "Show disassembly when there is no source information, or the source file is missing when displaying a stop context."},
    { Debugger::eStopDisassemblyTypeAlways,         "always",           "Always show disassembly when displaying a stop context."},
    { 0, nullptr, nullptr }
};

OptionEnumValueElement
g_language_enumerators[] =
{
    { eScriptLanguageNone,      "none",     "Disable scripting languages."},
    { eScriptLanguagePython,    "python",   "Select python as the default scripting language."},
    { eScriptLanguageDefault,   "default",  "Select the lldb default as the default scripting language."},
    { 0, nullptr, nullptr }
};

#define MODULE_WITH_FUNC "{ ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}}"
#define FILE_AND_LINE "{ at ${line.file.basename}:${line.number}}"
#define IS_OPTIMIZED "{${function.is-optimized} [opt]}"

#define DEFAULT_THREAD_FORMAT "thread #${thread.index}: tid = ${thread.id%tid}"\
    "{, ${frame.pc}}"\
    MODULE_WITH_FUNC\
    FILE_AND_LINE\
    "{, name = '${thread.name}'}"\
    "{, queue = '${thread.queue}'}"\
    "{, activity = '${thread.info.activity.name}'}" \
    "{, ${thread.info.trace_messages} messages}" \
    "{, stop reason = ${thread.stop-reason}}"\
    "{\\nReturn value: ${thread.return-value}}"\
    "{\\nCompleted expression: ${thread.completed-expression}}"\
    "\\n"

#define DEFAULT_FRAME_FORMAT "frame #${frame.index}: ${frame.pc}"\
    MODULE_WITH_FUNC\
    FILE_AND_LINE\
    IS_OPTIMIZED\
    "\\n"

// Three parts to this disassembly format specification:
//   1. If this is a new function/symbol (no previous symbol/function), print
//      dylib`funcname:\n
//   2. If this is a symbol context change (different from previous symbol/function), print
//      dylib`funcname:\n
//   3. print 
//      address <+offset>: 
#define DEFAULT_DISASSEMBLY_FORMAT "{${function.initial-function}{${module.file.basename}`}{${function.name-without-args}}:\n}{${function.changed}\n{${module.file.basename}`}{${function.name-without-args}}:\n}{${current-pc-arrow} }${addr-file-or-load}{ <${function.concrete-only-addr-offset-no-padding}>}: "

// gdb's disassembly format can be emulated with
// ${current-pc-arrow}${addr-file-or-load}{ <${function.name-without-args}${function.concrete-only-addr-offset-no-padding}>}: 

// lldb's original format for disassembly would look like this format string -
// {${function.initial-function}{${module.file.basename}`}{${function.name-without-args}}:\n}{${function.changed}\n{${module.file.basename}`}{${function.name-without-args}}:\n}{${current-pc-arrow} }{${addr-file-or-load}}: 

static PropertyDefinition
g_properties[] =
{
{   "auto-confirm",             OptionValue::eTypeBoolean     , true, false, nullptr, nullptr, "If true all confirmation prompts will receive their default reply." },
{   "disassembly-format",       OptionValue::eTypeFormatEntity, true, 0    , DEFAULT_DISASSEMBLY_FORMAT, nullptr, "The default disassembly format string to use when disassembling instruction sequences." },
{   "frame-format",             OptionValue::eTypeFormatEntity, true, 0    , DEFAULT_FRAME_FORMAT, nullptr, "The default frame format string to use when displaying stack frame information for threads." },
{   "notify-void",              OptionValue::eTypeBoolean     , true, false, nullptr, nullptr, "Notify the user explicitly if an expression returns void (default: false)." },
{   "prompt",                   OptionValue::eTypeString      , true, OptionValueString::eOptionEncodeCharacterEscapeSequences, "(lldb) ", nullptr, "The debugger command line prompt displayed for the user." },
{   "script-lang",              OptionValue::eTypeEnum        , true, eScriptLanguagePython, nullptr, g_language_enumerators, "The script language to be used for evaluating user-written scripts." },
{   "stop-disassembly-count",   OptionValue::eTypeSInt64      , true, 4    , nullptr, nullptr, "The number of disassembly lines to show when displaying a stopped context." },
{   "stop-disassembly-display", OptionValue::eTypeEnum        , true, Debugger::eStopDisassemblyTypeNoDebugInfo, nullptr, g_show_disassembly_enum_values, "Control when to display disassembly when displaying a stopped context." },
{   "stop-line-count-after",    OptionValue::eTypeSInt64      , true, 3    , nullptr, nullptr, "The number of sources lines to display that come after the current source line when displaying a stopped context." },
{   "stop-line-count-before",   OptionValue::eTypeSInt64      , true, 3    , nullptr, nullptr, "The number of sources lines to display that come before the current source line when displaying a stopped context." },
{   "term-width",               OptionValue::eTypeSInt64      , true, 80   , nullptr, nullptr, "The maximum number of columns to use for displaying text." },
{   "thread-format",            OptionValue::eTypeFormatEntity, true, 0    , DEFAULT_THREAD_FORMAT, nullptr, "The default thread format string to use when displaying thread information." },
{   "use-external-editor",      OptionValue::eTypeBoolean     , true, false, nullptr, nullptr, "Whether to use an external editor or not." },
{   "use-color",                OptionValue::eTypeBoolean     , true, true , nullptr, nullptr, "Whether to use Ansi color codes or not." },
{   "auto-one-line-summaries",  OptionValue::eTypeBoolean     , true, true, nullptr, nullptr, "If true, LLDB will automatically display small structs in one-liner format (default: true)." },
{   "auto-indent",              OptionValue::eTypeBoolean     , true, true , nullptr, nullptr, "If true, LLDB will auto indent/outdent code. Currently only supported in the REPL (default: true)." },
{   "print-decls",              OptionValue::eTypeBoolean     , true, true , nullptr, nullptr, "If true, LLDB will print the values of variables declared in an expression. Currently only supported in the REPL (default: true)." },
{   "tab-size",                 OptionValue::eTypeUInt64      , true, 4    , nullptr, nullptr, "The tab size to use when indenting code in multi-line input mode (default: 4)." },
{   "escape-non-printables",    OptionValue::eTypeBoolean     , true, true, nullptr, nullptr, "If true, LLDB will automatically escape non-printable and escape characters when formatting strings." },
{   nullptr,                       OptionValue::eTypeInvalid     , true, 0    , nullptr, nullptr, nullptr }
};

enum
{
    ePropertyAutoConfirm = 0,
    ePropertyDisassemblyFormat,
    ePropertyFrameFormat,
    ePropertyNotiftVoid,
    ePropertyPrompt,
    ePropertyScriptLanguage,
    ePropertyStopDisassemblyCount,
    ePropertyStopDisassemblyDisplay,
    ePropertyStopLineCountAfter,
    ePropertyStopLineCountBefore,
    ePropertyTerminalWidth,
    ePropertyThreadFormat,
    ePropertyUseExternalEditor,
    ePropertyUseColor,
    ePropertyAutoOneLineSummaries,
    ePropertyAutoIndent,
    ePropertyPrintDecls,
    ePropertyTabSize,
    ePropertyEscapeNonPrintables
};

LoadPluginCallbackType Debugger::g_load_plugin_callback = nullptr;

Error
Debugger::SetPropertyValue (const ExecutionContext *exe_ctx,
                            VarSetOperationType op,
                            const char *property_path,
                            const char *value)
{
    bool is_load_script = strcmp(property_path,"target.load-script-from-symbol-file") == 0;
    bool is_escape_non_printables = strcmp(property_path, "escape-non-printables") == 0;
    TargetSP target_sp;
    LoadScriptFromSymFile load_script_old_value;
    if (is_load_script && exe_ctx->GetTargetSP())
    {
        target_sp = exe_ctx->GetTargetSP();
        load_script_old_value = target_sp->TargetProperties::GetLoadScriptFromSymbolFile();
    }
    Error error (Properties::SetPropertyValue (exe_ctx, op, property_path, value));
    if (error.Success())
    {
        // FIXME it would be nice to have "on-change" callbacks for properties
        if (strcmp(property_path, g_properties[ePropertyPrompt].name) == 0)
        {
            const char *new_prompt = GetPrompt();
            std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor());
            if (str.length())
                new_prompt = str.c_str();
            GetCommandInterpreter().UpdatePrompt(new_prompt);
            EventSP prompt_change_event_sp (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (new_prompt)));
            GetCommandInterpreter().BroadcastEvent (prompt_change_event_sp);
        }
        else if (strcmp(property_path, g_properties[ePropertyUseColor].name) == 0)
        {
			// use-color changed. Ping the prompt so it can reset the ansi terminal codes.
            SetPrompt (GetPrompt());
        }
        else if (is_load_script && target_sp && load_script_old_value == eLoadScriptFromSymFileWarn)
        {
            if (target_sp->TargetProperties::GetLoadScriptFromSymbolFile() == eLoadScriptFromSymFileTrue)
            {
                std::list<Error> errors;
                StreamString feedback_stream;
                if (!target_sp->LoadScriptingResources(errors,&feedback_stream))
                {
                    StreamFileSP stream_sp (GetErrorFile());
                    if (stream_sp)
                    {
                        for (auto error : errors)
                        {
                            stream_sp->Printf("%s\n",error.AsCString());
                        }
                        if (feedback_stream.GetSize())
                            stream_sp->Printf("%s",feedback_stream.GetData());
                    }
                }
            }
        }
        else if (is_escape_non_printables)
        {
            DataVisualization::ForceUpdate();
        }
    }
    return error;
}

bool
Debugger::GetAutoConfirm () const
{
    const uint32_t idx = ePropertyAutoConfirm;
    return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0);
}

const FormatEntity::Entry *
Debugger::GetDisassemblyFormat() const
{
    const uint32_t idx = ePropertyDisassemblyFormat;
    return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
}

const FormatEntity::Entry *
Debugger::GetFrameFormat() const
{
    const uint32_t idx = ePropertyFrameFormat;
    return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
}

bool
Debugger::GetNotifyVoid () const
{
    const uint32_t idx = ePropertyNotiftVoid;
    return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0);
}

const char *
Debugger::GetPrompt() const
{
    const uint32_t idx = ePropertyPrompt;
    return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, g_properties[idx].default_cstr_value);
}

void
Debugger::SetPrompt(const char *p)
{
    const uint32_t idx = ePropertyPrompt;
    m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, p);
    const char *new_prompt = GetPrompt();
    std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor());
    if (str.length())
        new_prompt = str.c_str();
    GetCommandInterpreter().UpdatePrompt(new_prompt);
}

const FormatEntity::Entry *
Debugger::GetThreadFormat() const
{
    const uint32_t idx = ePropertyThreadFormat;
    return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
}

lldb::ScriptLanguage
Debugger::GetScriptLanguage() const
{
    const uint32_t idx = ePropertyScriptLanguage;
    return (lldb::ScriptLanguage)m_collection_sp->GetPropertyAtIndexAsEnumeration(nullptr, idx, g_properties[idx].default_uint_value);
}

bool
Debugger::SetScriptLanguage (lldb::ScriptLanguage script_lang)
{
    const uint32_t idx = ePropertyScriptLanguage;
    return m_collection_sp->SetPropertyAtIndexAsEnumeration(nullptr, idx, script_lang);
}

uint32_t
Debugger::GetTerminalWidth () const
{
    const uint32_t idx = ePropertyTerminalWidth;
    return m_collection_sp->GetPropertyAtIndexAsSInt64(nullptr, idx, g_properties[idx].default_uint_value);
}

bool
Debugger::SetTerminalWidth (uint32_t term_width)
{
    const uint32_t idx = ePropertyTerminalWidth;
    return m_collection_sp->SetPropertyAtIndexAsSInt64(nullptr, idx, term_width);
}

bool
Debugger::GetUseExternalEditor () const
{
    const uint32_t idx = ePropertyUseExternalEditor;
    return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0);
}

bool
Debugger::SetUseExternalEditor (bool b)
{
    const uint32_t idx = ePropertyUseExternalEditor;
    return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
}

bool
Debugger::GetUseColor () const
{
    const uint32_t idx = ePropertyUseColor;
    return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0);
}

bool
Debugger::SetUseColor (bool b)
{
    const uint32_t idx = ePropertyUseColor;
    bool ret = m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
    SetPrompt (GetPrompt());
    return ret;
}

uint32_t
Debugger::GetStopSourceLineCount (bool before) const
{
    const uint32_t idx = before ? ePropertyStopLineCountBefore : ePropertyStopLineCountAfter;
    return m_collection_sp->GetPropertyAtIndexAsSInt64(nullptr, idx, g_properties[idx].default_uint_value);
}

Debugger::StopDisassemblyType
Debugger::GetStopDisassemblyDisplay () const
{
    const uint32_t idx = ePropertyStopDisassemblyDisplay;
    return (Debugger::StopDisassemblyType)m_collection_sp->GetPropertyAtIndexAsEnumeration(nullptr, idx, g_properties[idx].default_uint_value);
}

uint32_t
Debugger::GetDisassemblyLineCount () const
{
    const uint32_t idx = ePropertyStopDisassemblyCount;
    return m_collection_sp->GetPropertyAtIndexAsSInt64(nullptr, idx, g_properties[idx].default_uint_value);
}

bool
Debugger::GetAutoOneLineSummaries () const
{
    const uint32_t idx = ePropertyAutoOneLineSummaries;
    return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
}

bool
Debugger::GetEscapeNonPrintables () const
{
    const uint32_t idx = ePropertyEscapeNonPrintables;
    return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
}

bool
Debugger::GetAutoIndent () const
{
    const uint32_t idx = ePropertyAutoIndent;
    return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
}

bool
Debugger::SetAutoIndent (bool b)
{
    const uint32_t idx = ePropertyAutoIndent;
    return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
}

bool
Debugger::GetPrintDecls () const
{
    const uint32_t idx = ePropertyPrintDecls;
    return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
}

bool
Debugger::SetPrintDecls (bool b)
{
    const uint32_t idx = ePropertyPrintDecls;
    return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
}

uint32_t
Debugger::GetTabSize () const
{
    const uint32_t idx = ePropertyTabSize;
    return m_collection_sp->GetPropertyAtIndexAsUInt64(nullptr, idx, g_properties[idx].default_uint_value);
}

bool
Debugger::SetTabSize (uint32_t tab_size)
{
    const uint32_t idx = ePropertyTabSize;
    return m_collection_sp->SetPropertyAtIndexAsUInt64(nullptr, idx, tab_size);
}

#pragma mark Debugger

//const DebuggerPropertiesSP &
//Debugger::GetSettings() const
//{
//    return m_properties_sp;
//}
//

void
Debugger::Initialize(LoadPluginCallbackType load_plugin_callback)
{
    assert(g_debugger_list_ptr == nullptr && "Debugger::Initialize called more than once!");
    g_debugger_list_mutex_ptr = new std::recursive_mutex();
    g_debugger_list_ptr = new DebuggerList();
    g_load_plugin_callback = load_plugin_callback;
}

void
Debugger::Terminate ()
{
    assert(g_debugger_list_ptr && "Debugger::Terminate called without a matching Debugger::Initialize!");

    if (g_debugger_list_ptr && g_debugger_list_mutex_ptr)
    {
        // Clear our master list of debugger objects
        {
            std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
            for (const auto& debugger: *g_debugger_list_ptr)
                debugger->Clear();
            g_debugger_list_ptr->clear();
        }
    }
}

void
Debugger::SettingsInitialize ()
{
    Target::SettingsInitialize ();
}

void
Debugger::SettingsTerminate ()
{
    Target::SettingsTerminate ();
}

bool
Debugger::LoadPlugin (const FileSpec& spec, Error& error)
{
    if (g_load_plugin_callback)
    {
        llvm::sys::DynamicLibrary dynlib = g_load_plugin_callback (shared_from_this(), spec, error);
        if (dynlib.isValid())
        {
            m_loaded_plugins.push_back(dynlib);
            return true;
        }
    }
    else
    {
        // The g_load_plugin_callback is registered in SBDebugger::Initialize()
        // and if the public API layer isn't available (code is linking against
        // all of the internal LLDB static libraries), then we can't load plugins
        error.SetErrorString("Public API layer is not available");
    }
    return false;
}

static FileSpec::EnumerateDirectoryResult
LoadPluginCallback(void *baton,
                   FileSpec::FileType file_type,
                   const FileSpec &file_spec)
{
    Error error;
    
    static ConstString g_dylibext("dylib");
    static ConstString g_solibext("so");
    
    if (!baton)
        return FileSpec::eEnumerateDirectoryResultQuit;
    
    Debugger *debugger = (Debugger*)baton;
    
    // If we have a regular file, a symbolic link or unknown file type, try
    // and process the file. We must handle unknown as sometimes the directory
    // enumeration might be enumerating a file system that doesn't have correct
    // file type information.
    if (file_type == FileSpec::eFileTypeRegular         ||
        file_type == FileSpec::eFileTypeSymbolicLink    ||
        file_type == FileSpec::eFileTypeUnknown          )
    {
        FileSpec plugin_file_spec (file_spec);
        plugin_file_spec.ResolvePath ();
        
        if (plugin_file_spec.GetFileNameExtension() != g_dylibext &&
            plugin_file_spec.GetFileNameExtension() != g_solibext)
        {
            return FileSpec::eEnumerateDirectoryResultNext;
        }

        Error plugin_load_error;
        debugger->LoadPlugin (plugin_file_spec, plugin_load_error);
        
        return FileSpec::eEnumerateDirectoryResultNext;
    }
    else if (file_type == FileSpec::eFileTypeUnknown     ||
        file_type == FileSpec::eFileTypeDirectory   ||
        file_type == FileSpec::eFileTypeSymbolicLink )
    {
        // Try and recurse into anything that a directory or symbolic link.
        // We must also do this for unknown as sometimes the directory enumeration
        // might be enumerating a file system that doesn't have correct file type
        // information.
        return FileSpec::eEnumerateDirectoryResultEnter;
    }
    
    return FileSpec::eEnumerateDirectoryResultNext;
}

void
Debugger::InstanceInitialize ()
{
    FileSpec dir_spec;
    const bool find_directories = true;
    const bool find_files = true;
    const bool find_other = true;
    char dir_path[PATH_MAX];
    if (HostInfo::GetLLDBPath(ePathTypeLLDBSystemPlugins, dir_spec))
    {
        if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path)))
        {
            FileSpec::EnumerateDirectory (dir_path,
                                          find_directories,
                                          find_files,
                                          find_other,
                                          LoadPluginCallback,
                                          this);
        }
    }

    if (HostInfo::GetLLDBPath(ePathTypeLLDBUserPlugins, dir_spec))
    {
        if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path)))
        {
            FileSpec::EnumerateDirectory (dir_path,
                                          find_directories,
                                          find_files,
                                          find_other,
                                          LoadPluginCallback,
                                          this);
        }
    }
    
    PluginManager::DebuggerInitialize (*this);
}

DebuggerSP
Debugger::CreateInstance (lldb::LogOutputCallback log_callback, void *baton)
{
    DebuggerSP debugger_sp (new Debugger(log_callback, baton));
    if (g_debugger_list_ptr && g_debugger_list_mutex_ptr)
    {
        std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
        g_debugger_list_ptr->push_back(debugger_sp);
    }
    debugger_sp->InstanceInitialize ();
    return debugger_sp;
}

void
Debugger::Destroy (DebuggerSP &debugger_sp)
{
    if (!debugger_sp)
        return;
        
    debugger_sp->Clear();

    if (g_debugger_list_ptr && g_debugger_list_mutex_ptr)
    {
        std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
        DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
        for (pos = g_debugger_list_ptr->begin (); pos != end; ++pos)
        {
            if ((*pos).get() == debugger_sp.get())
            {
                g_debugger_list_ptr->erase (pos);
                return;
            }
        }
    }
}

DebuggerSP
Debugger::FindDebuggerWithInstanceName (const ConstString &instance_name)
{
    DebuggerSP debugger_sp;
    if (g_debugger_list_ptr && g_debugger_list_mutex_ptr)
    {
        std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
        DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
        for (pos = g_debugger_list_ptr->begin (); pos != end; ++pos)
        {
            if ((*pos)->m_instance_name == instance_name)
            {
                debugger_sp = *pos;
                break;
            }
        }
    }
    return debugger_sp;
}

TargetSP
Debugger::FindTargetWithProcessID (lldb::pid_t pid)
{
    TargetSP target_sp;
    if (g_debugger_list_ptr && g_debugger_list_mutex_ptr)
    {
        std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
        DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
        for (pos = g_debugger_list_ptr->begin (); pos != end; ++pos)
        {
            target_sp = (*pos)->GetTargetList().FindTargetWithProcessID (pid);
            if (target_sp)
                break;
        }
    }
    return target_sp;
}

TargetSP
Debugger::FindTargetWithProcess (Process *process)
{
    TargetSP target_sp;
    if (g_debugger_list_ptr && g_debugger_list_mutex_ptr)
    {
        std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
        DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
        for (pos = g_debugger_list_ptr->begin (); pos != end; ++pos)
        {
            target_sp = (*pos)->GetTargetList().FindTargetWithProcess (process);
            if (target_sp)
                break;
        }
    }
    return target_sp;
}

Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton) :
    UserID(g_unique_id++),
    Properties(OptionValuePropertiesSP(new OptionValueProperties())),
    m_input_file_sp(new StreamFile(stdin, false)),
    m_output_file_sp(new StreamFile(stdout, false)),
    m_error_file_sp(new StreamFile(stderr, false)),
    m_broadcaster_manager_sp(BroadcasterManager::MakeBroadcasterManager()),
    m_terminal_state(),
    m_target_list(*this),
    m_platform_list(),
    m_listener_sp(Listener::MakeListener("lldb.Debugger")),
    m_source_manager_ap(),
    m_source_file_cache(),
    m_command_interpreter_ap(new CommandInterpreter(*this, eScriptLanguageDefault, false)),
    m_input_reader_stack(),
    m_instance_name(),
    m_loaded_plugins(),
    m_event_handler_thread(),
    m_io_handler_thread(),
    m_sync_broadcaster(nullptr, "lldb.debugger.sync"),
    m_forward_listener_sp(),
    m_clear_once()
{
    char instance_cstr[256];
    snprintf(instance_cstr, sizeof(instance_cstr), "debugger_%d", (int)GetID());
    m_instance_name.SetCString(instance_cstr);
    if (log_callback)
        m_log_callback_stream_sp.reset (new StreamCallback (log_callback, baton));
    m_command_interpreter_ap->Initialize ();
    // Always add our default platform to the platform list
    PlatformSP default_platform_sp (Platform::GetHostPlatform());
    assert(default_platform_sp);
    m_platform_list.Append (default_platform_sp, true);
    
    m_collection_sp->Initialize (g_properties);
    m_collection_sp->AppendProperty (ConstString("target"),
                                     ConstString("Settings specify to debugging targets."),
                                     true,
                                     Target::GetGlobalProperties()->GetValueProperties());
    m_collection_sp->AppendProperty (ConstString("platform"),
                                     ConstString("Platform settings."),
                                     true,
                                     Platform::GetGlobalPlatformProperties()->GetValueProperties());
    if (m_command_interpreter_ap)
    {
        m_collection_sp->AppendProperty (ConstString("interpreter"),
                                         ConstString("Settings specify to the debugger's command interpreter."),
                                         true,
                                         m_command_interpreter_ap->GetValueProperties());
    }
    OptionValueSInt64 *term_width = m_collection_sp->GetPropertyAtIndexAsOptionValueSInt64(nullptr, ePropertyTerminalWidth);
    term_width->SetMinimumValue(10);
    term_width->SetMaximumValue(1024);

    // Turn off use-color if this is a dumb terminal.
    const char *term = getenv ("TERM");
    if (term && !strcmp (term, "dumb"))
        SetUseColor (false);
}

Debugger::~Debugger ()
{
    Clear();
}

void
Debugger::Clear()
{
    //----------------------------------------------------------------------
    // Make sure we call this function only once. With the C++ global
    // destructor chain having a list of debuggers and with code that can be
    // running on other threads, we need to ensure this doesn't happen
    // multiple times.
    //
    // The following functions call Debugger::Clear():
    //     Debugger::~Debugger();
    //     static void Debugger::Destroy(lldb::DebuggerSP &debugger_sp);
    //     static void Debugger::Terminate();
    //----------------------------------------------------------------------
    std::call_once(m_clear_once, [this]() {
        ClearIOHandlers();
        StopIOHandlerThread();
        StopEventHandlerThread();
        m_listener_sp->Clear();
        int num_targets = m_target_list.GetNumTargets();
        for (int i = 0; i < num_targets; i++)
        {
            TargetSP target_sp (m_target_list.GetTargetAtIndex (i));
            if (target_sp)
            {
                ProcessSP process_sp (target_sp->GetProcessSP());
                if (process_sp)
                    process_sp->Finalize();
                target_sp->Destroy();
            }
        }
        m_broadcaster_manager_sp->Clear ();
        
        // Close the input file _before_ we close the input read communications class
        // as it does NOT own the input file, our m_input_file does.
        m_terminal_state.Clear();
        if (m_input_file_sp)
            m_input_file_sp->GetFile().Close ();
        
        m_command_interpreter_ap->Clear();
    });
}

bool
Debugger::GetCloseInputOnEOF () const
{
//    return m_input_comm.GetCloseOnEOF();
    return false;
}

void
Debugger::SetCloseInputOnEOF (bool b)
{
//    m_input_comm.SetCloseOnEOF(b);
}

bool
Debugger::GetAsyncExecution ()
{
    return !m_command_interpreter_ap->GetSynchronous();
}

void
Debugger::SetAsyncExecution (bool async_execution)
{
    m_command_interpreter_ap->SetSynchronous (!async_execution);
}

void
Debugger::SetInputFileHandle (FILE *fh, bool tranfer_ownership)
{
    if (m_input_file_sp)
        m_input_file_sp->GetFile().SetStream (fh, tranfer_ownership);
    else
        m_input_file_sp.reset (new StreamFile (fh, tranfer_ownership));

    File &in_file = m_input_file_sp->GetFile();
    if (!in_file.IsValid())
        in_file.SetStream (stdin, true);

    // Save away the terminal state if that is relevant, so that we can restore it in RestoreInputState.
    SaveInputTerminalState ();
}

void
Debugger::SetOutputFileHandle (FILE *fh, bool tranfer_ownership)
{
    if (m_output_file_sp)
        m_output_file_sp->GetFile().SetStream (fh, tranfer_ownership);
    else
        m_output_file_sp.reset (new StreamFile (fh, tranfer_ownership));
    
    File &out_file = m_output_file_sp->GetFile();
    if (!out_file.IsValid())
        out_file.SetStream (stdout, false);
    
    // do not create the ScriptInterpreter just for setting the output file handle
    // as the constructor will know how to do the right thing on its own
    const bool can_create = false;
    ScriptInterpreter* script_interpreter = GetCommandInterpreter().GetScriptInterpreter(can_create);
    if (script_interpreter)
        script_interpreter->ResetOutputFileHandle (fh);
}

void
Debugger::SetErrorFileHandle (FILE *fh, bool tranfer_ownership)
{
    if (m_error_file_sp)
        m_error_file_sp->GetFile().SetStream (fh, tranfer_ownership);
    else
        m_error_file_sp.reset (new StreamFile (fh, tranfer_ownership));
    
    File &err_file = m_error_file_sp->GetFile();
    if (!err_file.IsValid())
        err_file.SetStream (stderr, false);
}

void
Debugger::SaveInputTerminalState ()
{
    if (m_input_file_sp)
    {
        File &in_file = m_input_file_sp->GetFile();
        if (in_file.GetDescriptor() != File::kInvalidDescriptor)
            m_terminal_state.Save(in_file.GetDescriptor(), true);
    }
}

void
Debugger::RestoreInputTerminalState ()
{
    m_terminal_state.Restore();
}

ExecutionContext
Debugger::GetSelectedExecutionContext ()
{
    ExecutionContext exe_ctx;
    TargetSP target_sp(GetSelectedTarget());
    exe_ctx.SetTargetSP (target_sp);
    
    if (target_sp)
    {
        ProcessSP process_sp (target_sp->GetProcessSP());
        exe_ctx.SetProcessSP (process_sp);
        if (process_sp && !process_sp->IsRunning())
        {
            ThreadSP thread_sp (process_sp->GetThreadList().GetSelectedThread());
            if (thread_sp)
            {
                exe_ctx.SetThreadSP (thread_sp);
                exe_ctx.SetFrameSP (thread_sp->GetSelectedFrame());
                if (exe_ctx.GetFramePtr() == nullptr)
                    exe_ctx.SetFrameSP (thread_sp->GetStackFrameAtIndex (0));
            }
        }
    }
    return exe_ctx;
}

void
Debugger::DispatchInputInterrupt()
{
    std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
    IOHandlerSP reader_sp(m_input_reader_stack.Top());
    if (reader_sp)
        reader_sp->Interrupt();
}

void
Debugger::DispatchInputEndOfFile()
{
    std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
    IOHandlerSP reader_sp(m_input_reader_stack.Top());
    if (reader_sp)
        reader_sp->GotEOF();
}

void
Debugger::ClearIOHandlers()
{
    // The bottom input reader should be the main debugger input reader.  We do not want to close that one here.
    std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
    while (m_input_reader_stack.GetSize() > 1)
    {
        IOHandlerSP reader_sp(m_input_reader_stack.Top());
        if (reader_sp)
            PopIOHandler(reader_sp);
    }
}

void
Debugger::ExecuteIOHandlers()
{
    while (true)
    {
        IOHandlerSP reader_sp(m_input_reader_stack.Top());
        if (!reader_sp)
            break;

        reader_sp->Run();

        // Remove all input readers that are done from the top of the stack
        while (true)
        {
            IOHandlerSP top_reader_sp = m_input_reader_stack.Top();
            if (top_reader_sp && top_reader_sp->GetIsDone())
                PopIOHandler (top_reader_sp);
            else
                break;
        }
    }
    ClearIOHandlers();
}

bool
Debugger::IsTopIOHandler (const lldb::IOHandlerSP& reader_sp)
{
    return m_input_reader_stack.IsTop (reader_sp);
}

bool
Debugger::CheckTopIOHandlerTypes (IOHandler::Type top_type, IOHandler::Type second_top_type)
{
    return m_input_reader_stack.CheckTopIOHandlerTypes (top_type, second_top_type);
}

void
Debugger::PrintAsync (const char *s, size_t len, bool is_stdout)
{
    lldb::StreamFileSP stream = is_stdout ? GetOutputFile() : GetErrorFile();
    m_input_reader_stack.PrintAsync(stream.get(), s, len);
}

ConstString
Debugger::GetTopIOHandlerControlSequence(char ch)
{
    return m_input_reader_stack.GetTopIOHandlerControlSequence (ch);
}

const char *
Debugger::GetIOHandlerCommandPrefix()
{
    return m_input_reader_stack.GetTopIOHandlerCommandPrefix();
}

const char *
Debugger::GetIOHandlerHelpPrologue()
{
    return m_input_reader_stack.GetTopIOHandlerHelpPrologue();
}

void
Debugger::RunIOHandler (const IOHandlerSP& reader_sp)
{
    PushIOHandler (reader_sp);

    IOHandlerSP top_reader_sp = reader_sp;
    while (top_reader_sp)
    {
        top_reader_sp->Run();

        if (top_reader_sp.get() == reader_sp.get())
        {
            if (PopIOHandler (reader_sp))
                break;
        }

        while (true)
        {
            top_reader_sp = m_input_reader_stack.Top();
            if (top_reader_sp && top_reader_sp->GetIsDone())
                PopIOHandler (top_reader_sp);
            else
                break;
        }
    }
}

void
Debugger::AdoptTopIOHandlerFilesIfInvalid(StreamFileSP &in, StreamFileSP &out, StreamFileSP &err)
{
    // Before an IOHandler runs, it must have in/out/err streams.
    // This function is called when one ore more of the streams
    // are nullptr. We use the top input reader's in/out/err streams,
    // or fall back to the debugger file handles, or we fall back
    // onto stdin/stdout/stderr as a last resort.

    std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
    IOHandlerSP top_reader_sp(m_input_reader_stack.Top());
    // If no STDIN has been set, then set it appropriately
    if (!in)
    {
        if (top_reader_sp)
            in = top_reader_sp->GetInputStreamFile();
        else
            in = GetInputFile();

        // If there is nothing, use stdin
        if (!in)
            in = StreamFileSP(new StreamFile(stdin, false));
    }
    // If no STDOUT has been set, then set it appropriately
    if (!out)
    {
        if (top_reader_sp)
            out = top_reader_sp->GetOutputStreamFile();
        else
            out = GetOutputFile();

        // If there is nothing, use stdout
        if (!out)
            out = StreamFileSP(new StreamFile(stdout, false));
    }
    // If no STDERR has been set, then set it appropriately
    if (!err)
    {
        if (top_reader_sp)
            err = top_reader_sp->GetErrorStreamFile();
        else
            err = GetErrorFile();

        // If there is nothing, use stderr
        if (!err)
            err = StreamFileSP(new StreamFile(stdout, false));
    }
}

void
Debugger::PushIOHandler(const IOHandlerSP &reader_sp)
{
    if (!reader_sp)
        return;

    std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());

    // Get the current top input reader...
    IOHandlerSP top_reader_sp(m_input_reader_stack.Top());

    // Don't push the same IO handler twice...
    if (reader_sp == top_reader_sp)
        return;

    // Push our new input reader
    m_input_reader_stack.Push(reader_sp);
    reader_sp->Activate();

    // Interrupt the top input reader to it will exit its Run() function
    // and let this new input reader take over
    if (top_reader_sp)
    {
        top_reader_sp->Deactivate();
        top_reader_sp->Cancel();
    }
}

bool
Debugger::PopIOHandler(const IOHandlerSP &pop_reader_sp)
{
    if (!pop_reader_sp)
        return false;

    std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());

    // The reader on the stop of the stack is done, so let the next
    // read on the stack refresh its prompt and if there is one...
    if (m_input_reader_stack.IsEmpty())
        return false;

    IOHandlerSP reader_sp(m_input_reader_stack.Top());

    if (pop_reader_sp != reader_sp)
        return false;

    reader_sp->Deactivate();
    reader_sp->Cancel();
    m_input_reader_stack.Pop();

    reader_sp = m_input_reader_stack.Top();
    if (reader_sp)
        reader_sp->Activate();

    return true;
}

StreamSP
Debugger::GetAsyncOutputStream ()
{
    return StreamSP (new StreamAsynchronousIO (*this, true));
}

StreamSP
Debugger::GetAsyncErrorStream ()
{
    return StreamSP (new StreamAsynchronousIO (*this, false));
}    

size_t
Debugger::GetNumDebuggers()
{
    if (g_debugger_list_ptr && g_debugger_list_mutex_ptr)
    {
        std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
        return g_debugger_list_ptr->size();
    }
    return 0;
}

lldb::DebuggerSP
Debugger::GetDebuggerAtIndex (size_t index)
{
    DebuggerSP debugger_sp;
    
    if (g_debugger_list_ptr && g_debugger_list_mutex_ptr)
    {
        std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
        if (index < g_debugger_list_ptr->size())
            debugger_sp = g_debugger_list_ptr->at(index);
    }

    return debugger_sp;
}

DebuggerSP
Debugger::FindDebuggerWithID (lldb::user_id_t id)
{
    DebuggerSP debugger_sp;

    if (g_debugger_list_ptr && g_debugger_list_mutex_ptr)
    {
        std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
        DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
        for (pos = g_debugger_list_ptr->begin (); pos != end; ++pos)
        {
            if ((*pos)->GetID() == id)
            {
                debugger_sp = *pos;
                break;
            }
        }
    }
    return debugger_sp;
}

#if 0
static void
TestPromptFormats (StackFrame *frame)
{
    if (frame == nullptr)
        return;

    StreamString s;
    const char *prompt_format =         
    "{addr = '${addr}'\n}"
    "{addr-file-or-load = '${addr-file-or-load}'\n}"
    "{current-pc-arrow = '${current-pc-arrow}'\n}"
    "{process.id = '${process.id}'\n}"
    "{process.name = '${process.name}'\n}"
    "{process.file.basename = '${process.file.basename}'\n}"
    "{process.file.fullpath = '${process.file.fullpath}'\n}"
    "{thread.id = '${thread.id}'\n}"
    "{thread.index = '${thread.index}'\n}"
    "{thread.name = '${thread.name}'\n}"
    "{thread.queue = '${thread.queue}'\n}"
    "{thread.stop-reason = '${thread.stop-reason}'\n}"
    "{target.arch = '${target.arch}'\n}"
    "{module.file.basename = '${module.file.basename}'\n}"
    "{module.file.fullpath = '${module.file.fullpath}'\n}"
    "{file.basename = '${file.basename}'\n}"
    "{file.fullpath = '${file.fullpath}'\n}"
    "{frame.index = '${frame.index}'\n}"
    "{frame.pc = '${frame.pc}'\n}"
    "{frame.sp = '${frame.sp}'\n}"
    "{frame.fp = '${frame.fp}'\n}"
    "{frame.flags = '${frame.flags}'\n}"
    "{frame.reg.rdi = '${frame.reg.rdi}'\n}"
    "{frame.reg.rip = '${frame.reg.rip}'\n}"
    "{frame.reg.rsp = '${frame.reg.rsp}'\n}"
    "{frame.reg.rbp = '${frame.reg.rbp}'\n}"
    "{frame.reg.rflags = '${frame.reg.rflags}'\n}"
    "{frame.reg.xmm0 = '${frame.reg.xmm0}'\n}"
    "{frame.reg.carp = '${frame.reg.carp}'\n}"
    "{function.id = '${function.id}'\n}"
    "{function.changed = '${function.changed}'\n}"
    "{function.initial-function = '${function.initial-function}'\n}"
    "{function.name = '${function.name}'\n}"
    "{function.name-without-args = '${function.name-without-args}'\n}"
    "{function.name-with-args = '${function.name-with-args}'\n}"
    "{function.addr-offset = '${function.addr-offset}'\n}"
    "{function.concrete-only-addr-offset-no-padding = '${function.concrete-only-addr-offset-no-padding}'\n}"
    "{function.line-offset = '${function.line-offset}'\n}"
    "{function.pc-offset = '${function.pc-offset}'\n}"
    "{line.file.basename = '${line.file.basename}'\n}"
    "{line.file.fullpath = '${line.file.fullpath}'\n}"
    "{line.number = '${line.number}'\n}"
    "{line.start-addr = '${line.start-addr}'\n}"
    "{line.end-addr = '${line.end-addr}'\n}"
;

    SymbolContext sc (frame->GetSymbolContext(eSymbolContextEverything));
    ExecutionContext exe_ctx;
    frame->CalculateExecutionContext(exe_ctx);
    if (Debugger::FormatPrompt (prompt_format, &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s))
    {
        printf("%s\n", s.GetData());
    }
    else
    {
        printf ("what we got: %s\n", s.GetData());
    }
}
#endif

bool
Debugger::FormatDisassemblerAddress (const FormatEntity::Entry *format,
                                     const SymbolContext *sc,
                                     const SymbolContext *prev_sc,
                                     const ExecutionContext *exe_ctx,
                                     const Address *addr,
                                     Stream &s)
{
    FormatEntity::Entry format_entry;

    if (format == nullptr)
    {
        if (exe_ctx != nullptr && exe_ctx->HasTargetScope())
            format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat();
        if (format == nullptr)
        {
            FormatEntity::Parse("${addr}: ", format_entry);
            format = &format_entry;
        }
    }
    bool function_changed = false;
    bool initial_function = false;
    if (prev_sc && (prev_sc->function || prev_sc->symbol))
    {
        if (sc && (sc->function || sc->symbol))
        {
            if (prev_sc->symbol && sc->symbol)
            {
                if (!sc->symbol->Compare (prev_sc->symbol->GetName(), prev_sc->symbol->GetType()))
                {
                    function_changed = true;
                }
            }
            else if (prev_sc->function && sc->function)
            {
                if (prev_sc->function->GetMangled() != sc->function->GetMangled())
                {
                    function_changed = true;
                }
            }
        }
    }
    // The first context on a list of instructions will have a prev_sc that
    // has no Function or Symbol -- if SymbolContext had an IsValid() method, it
    // would return false.  But we do get a prev_sc pointer.
    if ((sc && (sc->function || sc->symbol))
        && prev_sc && (prev_sc->function == nullptr && prev_sc->symbol == nullptr))
    {
        initial_function = true;
    }
    return FormatEntity::Format(*format, s, sc, exe_ctx, addr, nullptr, function_changed, initial_function);
}

void
Debugger::SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton)
{
    // For simplicity's sake, I am not going to deal with how to close down any
    // open logging streams, I just redirect everything from here on out to the
    // callback.
    m_log_callback_stream_sp.reset (new StreamCallback (log_callback, baton));
}

bool
Debugger::EnableLog (const char *channel, const char **categories, const char *log_file, uint32_t log_options, Stream &error_stream)
{
    StreamSP log_stream_sp;
    if (m_log_callback_stream_sp)
    {
        log_stream_sp = m_log_callback_stream_sp;
        // For now when using the callback mode you always get thread & timestamp.
        log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_THREAD_NAME;
    }
    else if (log_file == nullptr || *log_file == '\0')
    {
        log_stream_sp = GetOutputFile();
    }
    else
    {
        LogStreamMap::iterator pos = m_log_streams.find(log_file);
        if (pos != m_log_streams.end())
            log_stream_sp = pos->second.lock();
        if (!log_stream_sp)
        {
            uint32_t options = File::eOpenOptionWrite | File::eOpenOptionCanCreate
                                | File::eOpenOptionCloseOnExec | File::eOpenOptionAppend;
            if (! (log_options & LLDB_LOG_OPTION_APPEND))
                options |= File::eOpenOptionTruncate;

            log_stream_sp.reset (new StreamFile (log_file, options));
            m_log_streams[log_file] = log_stream_sp;
        }
    }
    assert(log_stream_sp);
    
    if (log_options == 0)
        log_options = LLDB_LOG_OPTION_PREPEND_THREAD_NAME | LLDB_LOG_OPTION_THREADSAFE;
        
    return Log::EnableLogChannel(log_stream_sp, log_options, channel, categories, error_stream);
}

SourceManager &
Debugger::GetSourceManager ()
{
    if (!m_source_manager_ap)
        m_source_manager_ap.reset (new SourceManager (shared_from_this()));
    return *m_source_manager_ap;
}

// This function handles events that were broadcast by the process.
void
Debugger::HandleBreakpointEvent (const EventSP &event_sp)
{
    using namespace lldb;
    const uint32_t event_type = Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent (event_sp);
    
//    if (event_type & eBreakpointEventTypeAdded
//        || event_type & eBreakpointEventTypeRemoved
//        || event_type & eBreakpointEventTypeEnabled
//        || event_type & eBreakpointEventTypeDisabled
//        || event_type & eBreakpointEventTypeCommandChanged
//        || event_type & eBreakpointEventTypeConditionChanged
//        || event_type & eBreakpointEventTypeIgnoreChanged
//        || event_type & eBreakpointEventTypeLocationsResolved)
//    {
//        // Don't do anything about these events, since the breakpoint commands already echo these actions.
//    }
//    
    if (event_type & eBreakpointEventTypeLocationsAdded)
    {
        uint32_t num_new_locations = Breakpoint::BreakpointEventData::GetNumBreakpointLocationsFromEvent(event_sp);
        if (num_new_locations > 0)
        {
            BreakpointSP breakpoint = Breakpoint::BreakpointEventData::GetBreakpointFromEvent(event_sp);
            StreamSP output_sp (GetAsyncOutputStream());
            if (output_sp)
            {
                output_sp->Printf("%d location%s added to breakpoint %d\n",
                                  num_new_locations,
                                  num_new_locations == 1 ? "" : "s",
                                  breakpoint->GetID());
                output_sp->Flush();
            }
        }
    }
//    else if (event_type & eBreakpointEventTypeLocationsRemoved)
//    {
//        // These locations just get disabled, not sure it is worth spamming folks about this on the command line.
//    }
//    else if (event_type & eBreakpointEventTypeLocationsResolved)
//    {
//        // This might be an interesting thing to note, but I'm going to leave it quiet for now, it just looked noisy.
//    }
}

size_t
Debugger::GetProcessSTDOUT (Process *process, Stream *stream)
{
    size_t total_bytes = 0;
    if (stream == nullptr)
        stream = GetOutputFile().get();

    if (stream)
    {
        //  The process has stuff waiting for stdout; get it and write it out to the appropriate place.
        if (process == nullptr)
        {
            TargetSP target_sp = GetTargetList().GetSelectedTarget();
            if (target_sp)
                process = target_sp->GetProcessSP().get();
        }
        if (process)
        {
            Error error;
            size_t len;
            char stdio_buffer[1024];
            while ((len = process->GetSTDOUT (stdio_buffer, sizeof (stdio_buffer), error)) > 0)
            {
                stream->Write(stdio_buffer, len);
                total_bytes += len;
            }
        }
        stream->Flush();
    }
    return total_bytes;
}

size_t
Debugger::GetProcessSTDERR (Process *process, Stream *stream)
{
    size_t total_bytes = 0;
    if (stream == nullptr)
        stream = GetOutputFile().get();
    
    if (stream)
    {
        //  The process has stuff waiting for stderr; get it and write it out to the appropriate place.
        if (process == nullptr)
        {
            TargetSP target_sp = GetTargetList().GetSelectedTarget();
            if (target_sp)
                process = target_sp->GetProcessSP().get();
        }
        if (process)
        {
            Error error;
            size_t len;
            char stdio_buffer[1024];
            while ((len = process->GetSTDERR (stdio_buffer, sizeof (stdio_buffer), error)) > 0)
            {
                stream->Write(stdio_buffer, len);
                total_bytes += len;
            }
        }
        stream->Flush();
    }
    return total_bytes;
}


// This function handles events that were broadcast by the process.
void
Debugger::HandleProcessEvent (const EventSP &event_sp)
{
    using namespace lldb;
    const uint32_t event_type = event_sp->GetType();
    ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event_sp.get());

    StreamSP output_stream_sp = GetAsyncOutputStream();
    StreamSP error_stream_sp = GetAsyncErrorStream();
    const bool gui_enabled = IsForwardingEvents();

    if (!gui_enabled)
    {
        bool pop_process_io_handler = false;
        assert (process_sp);

        bool state_is_stopped = false;
        const bool got_state_changed = (event_type & Process::eBroadcastBitStateChanged) != 0;
        const bool got_stdout = (event_type & Process::eBroadcastBitSTDOUT) != 0;
        const bool got_stderr = (event_type & Process::eBroadcastBitSTDERR) != 0;
        if (got_state_changed)
        {
            StateType event_state = Process::ProcessEventData::GetStateFromEvent (event_sp.get());
            state_is_stopped = StateIsStoppedState(event_state, false);
        }

        // Display running state changes first before any STDIO
        if (got_state_changed && !state_is_stopped)
        {
            Process::HandleProcessStateChangedEvent (event_sp, output_stream_sp.get(), pop_process_io_handler);
        }

        // Now display and STDOUT
        if (got_stdout || got_state_changed)
        {
            GetProcessSTDOUT (process_sp.get(), output_stream_sp.get());
        }

        // Now display and STDERR
        if (got_stderr || got_state_changed)
        {
            GetProcessSTDERR (process_sp.get(), error_stream_sp.get());
        }

        // Now display any stopped state changes after any STDIO
        if (got_state_changed && state_is_stopped)
        {
            Process::HandleProcessStateChangedEvent (event_sp, output_stream_sp.get(), pop_process_io_handler);
        }

        output_stream_sp->Flush();
        error_stream_sp->Flush();

        if (pop_process_io_handler)
            process_sp->PopProcessIOHandler();
    }
}

void
Debugger::HandleThreadEvent (const EventSP &event_sp)
{
    // At present the only thread event we handle is the Frame Changed event,
    // and all we do for that is just reprint the thread status for that thread.
    using namespace lldb;
    const uint32_t event_type = event_sp->GetType();
    if (event_type == Thread::eBroadcastBitStackChanged   ||
        event_type == Thread::eBroadcastBitThreadSelected )
    {
        ThreadSP thread_sp (Thread::ThreadEventData::GetThreadFromEvent (event_sp.get()));
        if (thread_sp)
        {
            thread_sp->GetStatus(*GetAsyncOutputStream(), 0, 1, 1);
        }
    }
}

bool
Debugger::IsForwardingEvents ()
{
    return (bool)m_forward_listener_sp;
}

void
Debugger::EnableForwardEvents (const ListenerSP &listener_sp)
{
    m_forward_listener_sp = listener_sp;
}

void
Debugger::CancelForwardEvents (const ListenerSP &listener_sp)
{
    m_forward_listener_sp.reset();
}


void
Debugger::DefaultEventHandler()
{
    ListenerSP listener_sp(GetListener());
    ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
    ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
    ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
    BroadcastEventSpec target_event_spec (broadcaster_class_target,
                                          Target::eBroadcastBitBreakpointChanged);

    BroadcastEventSpec process_event_spec (broadcaster_class_process,
                                           Process::eBroadcastBitStateChanged   |
                                           Process::eBroadcastBitSTDOUT         |
                                           Process::eBroadcastBitSTDERR);

    BroadcastEventSpec thread_event_spec (broadcaster_class_thread,
                                          Thread::eBroadcastBitStackChanged     |
                                          Thread::eBroadcastBitThreadSelected   );
    
    listener_sp->StartListeningForEventSpec (m_broadcaster_manager_sp, target_event_spec);
    listener_sp->StartListeningForEventSpec (m_broadcaster_manager_sp, process_event_spec);
    listener_sp->StartListeningForEventSpec (m_broadcaster_manager_sp, thread_event_spec);
    listener_sp->StartListeningForEvents (m_command_interpreter_ap.get(),
                                      CommandInterpreter::eBroadcastBitQuitCommandReceived      |
                                      CommandInterpreter::eBroadcastBitAsynchronousOutputData   |
                                      CommandInterpreter::eBroadcastBitAsynchronousErrorData    );

    // Let the thread that spawned us know that we have started up and
    // that we are now listening to all required events so no events get missed
    m_sync_broadcaster.BroadcastEvent(eBroadcastBitEventThreadIsListening);

    bool done = false;
    while (!done)
    {
        EventSP event_sp;
        if (listener_sp->WaitForEvent(nullptr, event_sp))
        {
            if (event_sp)
            {
                Broadcaster *broadcaster = event_sp->GetBroadcaster();
                if (broadcaster)
                {
                    uint32_t event_type = event_sp->GetType();
                    ConstString broadcaster_class (broadcaster->GetBroadcasterClass());
                    if (broadcaster_class == broadcaster_class_process)
                    {
                        HandleProcessEvent (event_sp);
                    }
                    else if (broadcaster_class == broadcaster_class_target)
                    {
                        if (Breakpoint::BreakpointEventData::GetEventDataFromEvent(event_sp.get()))
                        {
                            HandleBreakpointEvent (event_sp);
                        }
                    }
                    else if (broadcaster_class == broadcaster_class_thread)
                    {
                        HandleThreadEvent (event_sp);
                    }
                    else if (broadcaster == m_command_interpreter_ap.get())
                    {
                        if (event_type & CommandInterpreter::eBroadcastBitQuitCommandReceived)
                        {
                            done = true;
                        }
                        else if (event_type & CommandInterpreter::eBroadcastBitAsynchronousErrorData)
                        {
                            const char *data = reinterpret_cast<const char *>(EventDataBytes::GetBytesFromEvent (event_sp.get()));
                            if (data && data[0])
                            {
                                StreamSP error_sp (GetAsyncErrorStream());
                                if (error_sp)
                                {
                                    error_sp->PutCString(data);
                                    error_sp->Flush();
                                }
                            }
                        }
                        else if (event_type & CommandInterpreter::eBroadcastBitAsynchronousOutputData)
                        {
                            const char *data = reinterpret_cast<const char *>(EventDataBytes::GetBytesFromEvent (event_sp.get()));
                            if (data && data[0])
                            {
                                StreamSP output_sp (GetAsyncOutputStream());
                                if (output_sp)
                                {
                                    output_sp->PutCString(data);
                                    output_sp->Flush();
                                }
                            }
                        }
                    }
                }
                
                if (m_forward_listener_sp)
                    m_forward_listener_sp->AddEvent(event_sp);
            }
        }
    }
}

lldb::thread_result_t
Debugger::EventHandlerThread (lldb::thread_arg_t arg)
{
    ((Debugger *)arg)->DefaultEventHandler();
    return NULL;
}

bool
Debugger::StartEventHandlerThread()
{
    if (!m_event_handler_thread.IsJoinable())
    {
        // We must synchronize with the DefaultEventHandler() thread to ensure
        // it is up and running and listening to events before we return from
        // this function. We do this by listening to events for the
        // eBroadcastBitEventThreadIsListening from the m_sync_broadcaster
        ListenerSP listener_sp(Listener::MakeListener("lldb.debugger.event-handler"));
        listener_sp->StartListeningForEvents(&m_sync_broadcaster, eBroadcastBitEventThreadIsListening);

        // Use larger 8MB stack for this thread
        m_event_handler_thread = ThreadLauncher::LaunchThread("lldb.debugger.event-handler",
                                                              EventHandlerThread,
                                                              this,
                                                              nullptr,
                                                              g_debugger_event_thread_stack_bytes);

        // Make sure DefaultEventHandler() is running and listening to events before we return
        // from this function. We are only listening for events of type
        // eBroadcastBitEventThreadIsListening so we don't need to check the event, we just need
        // to wait an infinite amount of time for it (nullptr timeout as the first parameter)
        lldb::EventSP event_sp;
        listener_sp->WaitForEvent(nullptr, event_sp);
    }
    return m_event_handler_thread.IsJoinable();
}

void
Debugger::StopEventHandlerThread()
{
    if (m_event_handler_thread.IsJoinable())
    {
        GetCommandInterpreter().BroadcastEvent(CommandInterpreter::eBroadcastBitQuitCommandReceived);
        m_event_handler_thread.Join(nullptr);
    }
}

lldb::thread_result_t
Debugger::IOHandlerThread (lldb::thread_arg_t arg)
{
    Debugger *debugger = (Debugger *)arg;
    debugger->ExecuteIOHandlers();
    debugger->StopEventHandlerThread();
    return NULL;
}

bool
Debugger::HasIOHandlerThread()
{
    return m_io_handler_thread.IsJoinable();
}

bool
Debugger::StartIOHandlerThread()
{
    if (!m_io_handler_thread.IsJoinable())
        m_io_handler_thread = ThreadLauncher::LaunchThread("lldb.debugger.io-handler",
                                                           IOHandlerThread,
                                                           this,
                                                           nullptr,
                                                           8*1024*1024); // Use larger 8MB stack for this thread
    return m_io_handler_thread.IsJoinable();
}

void
Debugger::StopIOHandlerThread()
{
    if (m_io_handler_thread.IsJoinable())
    {
        if (m_input_file_sp)
            m_input_file_sp->GetFile().Close();
        m_io_handler_thread.Join(nullptr);
    }
}

void
Debugger::JoinIOHandlerThread()
{
    if (HasIOHandlerThread())
    {
        thread_result_t result;
        m_io_handler_thread.Join(&result);
        m_io_handler_thread = LLDB_INVALID_HOST_THREAD;
    }
}

Target *
Debugger::GetDummyTarget()
{
    return m_target_list.GetDummyTarget (*this).get();
}

Target *
Debugger::GetSelectedOrDummyTarget(bool prefer_dummy)
{
    Target *target = nullptr;
    if (!prefer_dummy)
    {
        target = m_target_list.GetSelectedTarget().get();
        if (target)
            return target;
    }
    
    return GetDummyTarget();
}

Error
Debugger::RunREPL (LanguageType language, const char *repl_options)
{
    Error err;
    FileSpec repl_executable;
    
    if (language == eLanguageTypeUnknown)
    {
        std::set<LanguageType> repl_languages;
        
        Language::GetLanguagesSupportingREPLs(repl_languages);
        
        if (repl_languages.size() == 1)
        {
            language = *repl_languages.begin();
        }
        else if (repl_languages.empty())
        {
            err.SetErrorStringWithFormat("LLDB isn't configured with REPL support for any languages.");
            return err;
        }
        else
        {
            err.SetErrorStringWithFormat("Multiple possible REPL languages.  Please specify a language.");
            return err;
        }
    }

    Target *const target = nullptr; // passing in an empty target means the REPL must create one
    
    REPLSP repl_sp(REPL::Create(err, language, this, target, repl_options));

    if (!err.Success())
    {
        return err;
    }
    
    if (!repl_sp)
    {
        err.SetErrorStringWithFormat("couldn't find a REPL for %s", Language::GetNameForLanguageType(language));
        return err;
    }
    
    repl_sp->SetCompilerOptions(repl_options);
    repl_sp->RunLoop();
    
    return err;
}
