diff options
Diffstat (limited to 'lldb/source/Core/Debugger.cpp')
-rw-r--r-- | lldb/source/Core/Debugger.cpp | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp new file mode 100644 index 00000000000..c106d838fed --- /dev/null +++ b/lldb/source/Core/Debugger.cpp @@ -0,0 +1,434 @@ +//===-- 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/lldb-private.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Timer.h" + +#include "lldb/Target/TargetList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + + +using namespace lldb; +using namespace lldb_private; + +int Debugger::g_shared_debugger_refcount = 0; +bool Debugger::g_in_terminate = false; + +Debugger::DebuggerSP & +Debugger::GetDebuggerSP () +{ + static DebuggerSP g_shared_debugger_sp; + return g_shared_debugger_sp; +} + +void +Debugger::Initialize () +{ + g_shared_debugger_refcount++; + if (GetDebuggerSP().get() == NULL) + { + GetDebuggerSP().reset (new Debugger()); + lldb_private::Initialize(); + GetDebuggerSP()->GetCommandInterpreter().Initialize(); + } +} + +void +Debugger::Terminate () +{ + g_shared_debugger_refcount--; + if (g_shared_debugger_refcount == 0) + { + // Because Terminate is called also in the destructor, we need to make sure + // that none of the calls to GetSharedInstance leads to a call to Initialize, + // thus bumping the refcount back to 1 & causing Debugger::~Debugger to try to + // re-terminate. So we use g_in_terminate to indicate this condition. + // When we can require at least Initialize to be called, we won't have to do + // this since then the GetSharedInstance won't have to auto-call Initialize... + + g_in_terminate = true; + int num_targets = GetDebuggerSP()->GetTargetList().GetNumTargets(); + for (int i = 0; i < num_targets; i++) + { + ProcessSP process_sp(GetDebuggerSP()->GetTargetList().GetTargetAtIndex (i)->GetProcessSP()); + if (process_sp) + process_sp->Destroy(); + } + GetDebuggerSP()->DisconnectInput(); + lldb_private::WillTerminate(); + GetDebuggerSP().reset(); + } +} + +Debugger & +Debugger::GetSharedInstance() +{ + // Don't worry about thread race conditions with the code below as + // lldb_private::Initialize(); does this in a thread safe way. I just + // want to avoid having to lock and unlock a mutex in + // lldb_private::Initialize(); every time we want to access the + // Debugger shared instance. + + // FIXME: We intend to require clients to call Initialize by hand (since they + // will also have to call Terminate by hand.) But for now it is not clear where + // we can reliably call these in JH. So the present version initializes on first use + // here, and terminates in the destructor. + if (g_shared_debugger_refcount == 0 && !g_in_terminate) + Initialize(); + + assert(GetDebuggerSP().get()!= NULL); + return *(GetDebuggerSP().get()); +} + +Debugger::Debugger () : + m_input_comm("debugger.input"), + m_input_file (), + m_output_file (), + m_error_file (), + m_async_execution (true), + m_target_list (), + m_listener ("lldb.Debugger"), + m_source_manager (), + m_command_interpreter (eScriptLanguageDefault, false, &m_listener, m_source_manager), + m_input_readers (), + m_input_reader_data () +{ +} + +Debugger::~Debugger () +{ + // FIXME: + // Remove this once this version of lldb has made its way through a build. + Terminate(); +} + + +bool +Debugger::GetAsyncExecution () +{ + return m_async_execution; +} + +void +Debugger::SetAsyncExecution (bool async_execution) +{ + static bool value_has_been_set = false; + + if (!value_has_been_set) + { + value_has_been_set = true; + m_async_execution = async_execution; + m_command_interpreter.SetSynchronous (!async_execution); + } +} + +void +Debugger::DisconnectInput() +{ + m_input_comm.Clear (); +} + +void +Debugger::SetInputFileHandle (FILE *fh, bool tranfer_ownership) +{ + m_input_file.SetFileHandle (fh, tranfer_ownership); + if (m_input_file.GetFileHandle() == NULL) + m_input_file.SetFileHandle (stdin, false); + + // Disconnect from any old connection if we had one + m_input_comm.Disconnect (); + m_input_comm.SetConnection (new ConnectionFileDescriptor (::fileno (GetInputFileHandle()), true)); + m_input_comm.SetReadThreadBytesReceivedCallback (Debugger::DispatchInputCallback, this); + + Error error; + if (m_input_comm.StartReadThread (&error) == false) + { + FILE *err_fh = GetErrorFileHandle(); + if (err_fh) + { + ::fprintf (err_fh, "error: failed to main input read thread: %s", error.AsCString() ? error.AsCString() : "unkown error"); + exit(1); + } + } + +} + +FILE * +Debugger::GetInputFileHandle () +{ + return m_input_file.GetFileHandle(); +} + + +void +Debugger::SetOutputFileHandle (FILE *fh, bool tranfer_ownership) +{ + m_output_file.SetFileHandle (fh, tranfer_ownership); + if (m_output_file.GetFileHandle() == NULL) + m_output_file.SetFileHandle (stdin, false); +} + +FILE * +Debugger::GetOutputFileHandle () +{ + return m_output_file.GetFileHandle(); +} + +void +Debugger::SetErrorFileHandle (FILE *fh, bool tranfer_ownership) +{ + m_error_file.SetFileHandle (fh, tranfer_ownership); + if (m_error_file.GetFileHandle() == NULL) + m_error_file.SetFileHandle (stdin, false); +} + + +FILE * +Debugger::GetErrorFileHandle () +{ + return m_error_file.GetFileHandle(); +} + +CommandInterpreter & +Debugger::GetCommandInterpreter () +{ + return m_command_interpreter; +} + +Listener & +Debugger::GetListener () +{ + return m_listener; +} + + +TargetSP +Debugger::GetCurrentTarget () +{ + return m_target_list.GetCurrentTarget (); +} + +ExecutionContext +Debugger::GetCurrentExecutionContext () +{ + ExecutionContext exe_ctx; + exe_ctx.Clear(); + + lldb::TargetSP target_sp = GetCurrentTarget(); + exe_ctx.target = target_sp.get(); + + if (target_sp) + { + exe_ctx.process = target_sp->GetProcessSP().get(); + if (exe_ctx.process && exe_ctx.process->IsRunning() == false) + { + exe_ctx.thread = exe_ctx.process->GetThreadList().GetCurrentThread().get(); + if (exe_ctx.thread == NULL) + exe_ctx.thread = exe_ctx.process->GetThreadList().GetThreadAtIndex(0).get(); + if (exe_ctx.thread) + { + exe_ctx.frame = exe_ctx.thread->GetCurrentFrame().get(); + if (exe_ctx.frame == NULL) + exe_ctx.frame = exe_ctx.thread->GetStackFrameAtIndex (0).get(); + } + } + } + return exe_ctx; + +} + +SourceManager & +Debugger::GetSourceManager () +{ + return m_source_manager; +} + + +TargetList& +Debugger::GetTargetList () +{ + return m_target_list; +} + +void +Debugger::DispatchInputCallback (void *baton, const void *bytes, size_t bytes_len) +{ + ((Debugger *)baton)->DispatchInput ((char *)bytes, bytes_len); +} + + +void +Debugger::DispatchInput (const char *bytes, size_t bytes_len) +{ + if (bytes == NULL || bytes_len == 0) + return; + + // TODO: implement the STDIO to the process as an input reader... + TargetSP target = GetCurrentTarget(); + if (target.get() != NULL) + { + ProcessSP process_sp = target->GetProcessSP(); + if (process_sp.get() != NULL + && StateIsRunningState (process_sp->GetState())) + { + Error error; + if (process_sp->PutSTDIN (bytes, bytes_len, error) == bytes_len) + return; + } + } + + WriteToDefaultReader (bytes, bytes_len); +} + +void +Debugger::WriteToDefaultReader (const char *bytes, size_t bytes_len) +{ + if (bytes && bytes_len) + m_input_reader_data.append (bytes, bytes_len); + + if (m_input_reader_data.empty()) + return; + + while (!m_input_readers.empty() && !m_input_reader_data.empty()) + { + while (CheckIfTopInputReaderIsDone ()) + /* Do nothing. */; + + // Get the input reader from the top of the stack + InputReaderSP reader_sp(m_input_readers.top()); + + if (!reader_sp) + break; + + size_t bytes_handled = reader_sp->HandleRawBytes (m_input_reader_data.data(), + m_input_reader_data.size()); + if (bytes_handled) + { + m_input_reader_data.erase (0, bytes_handled); + } + else + { + // No bytes were handled, we might not have reached our + // granularity, just return and wait for more data + break; + } + } + + // Flush out any input readers that are donesvn + while (CheckIfTopInputReaderIsDone ()) + /* Do nothing. */; + +} + +void +Debugger::PushInputReader (const InputReaderSP& reader_sp) +{ + if (!reader_sp) + return; + if (!m_input_readers.empty()) + { + // Deactivate the old top reader + InputReaderSP top_reader_sp (m_input_readers.top()); + if (top_reader_sp) + top_reader_sp->Notify (eInputReaderDeactivate); + } + m_input_readers.push (reader_sp); + reader_sp->Notify (eInputReaderActivate); + ActivateInputReader (reader_sp); +} + +bool +Debugger::PopInputReader (const lldb::InputReaderSP& pop_reader_sp) +{ + bool result = false; + + // The reader on the stop of the stack is done, so let the next + // read on the stack referesh its prompt and if there is one... + if (!m_input_readers.empty()) + { + InputReaderSP reader_sp(m_input_readers.top()); + + if (!pop_reader_sp || pop_reader_sp.get() == reader_sp.get()) + { + m_input_readers.pop (); + reader_sp->Notify (eInputReaderDeactivate); + reader_sp->Notify (eInputReaderDone); + result = true; + + if (!m_input_readers.empty()) + { + reader_sp = m_input_readers.top(); + if (reader_sp) + { + ActivateInputReader (reader_sp); + reader_sp->Notify (eInputReaderReactivate); + } + } + } + } + return result; +} + +bool +Debugger::CheckIfTopInputReaderIsDone () +{ + bool result = false; + if (!m_input_readers.empty()) + { + InputReaderSP reader_sp(m_input_readers.top()); + + if (reader_sp && reader_sp->IsDone()) + { + result = true; + PopInputReader (reader_sp); + } + } + return result; +} + +void +Debugger::ActivateInputReader (const InputReaderSP &reader_sp) +{ + FILE *in_fh = GetInputFileHandle(); + + if (in_fh) + { + struct termios in_fh_termios; + int in_fd = fileno (in_fh); + if (::tcgetattr(in_fd, &in_fh_termios) == 0) + { + if (reader_sp->GetEcho()) + in_fh_termios.c_lflag |= ECHO; // Turn on echoing + else + in_fh_termios.c_lflag &= ~ECHO; // Turn off echoing + + switch (reader_sp->GetGranularity()) + { + case eInputReaderGranularityByte: + case eInputReaderGranularityWord: + in_fh_termios.c_lflag &= ~ICANON; // Get one char at a time + break; + + case eInputReaderGranularityLine: + case eInputReaderGranularityAll: + in_fh_termios.c_lflag |= ICANON; // Get lines at a time + break; + + default: + break; + } + ::tcsetattr (in_fd, TCSANOW, &in_fh_termios); + } + } +} |