mirror of
https://github.com/Gericom/teak-llvm.git
synced 2025-06-26 23:09:03 -04:00
435 lines
11 KiB
C++
435 lines
11 KiB
C++
//===-- 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);
|
|
}
|
|
}
|
|
}
|