//===-- Log.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-python.h"
// C Includes
#include <pthread.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
// C++ Includes
#include <map>
#include <string>
// Other libraries and framework includes
// Project includes
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/TimeValue.h"
#include "lldb/Host/Mutex.h"
#include "lldb/Interpreter/Args.h"
using namespace lldb;
using namespace lldb_private;
Log::Log () :
m_stream_sp(),
m_options(0),
m_mask_bits(0)
{
}
Log::Log (const StreamSP &stream_sp) :
m_stream_sp(stream_sp),
m_options(0),
m_mask_bits(0)
{
}
Log::~Log ()
{
}
Flags &
Log::GetOptions()
{
return m_options;
}
const Flags &
Log::GetOptions() const
{
return m_options;
}
Flags &
Log::GetMask()
{
return m_mask_bits;
}
const Flags &
Log::GetMask() const
{
return m_mask_bits;
}
//----------------------------------------------------------------------
// All logging eventually boils down to this function call. If we have
// a callback registered, then we call the logging callback. If we have
// a valid file handle, we also log to the file.
//----------------------------------------------------------------------
void
Log::PrintfWithFlagsVarArg (uint32_t flags, const char *format, va_list args)
{
if (m_stream_sp)
{
static uint32_t g_sequence_id = 0;
StreamString header;
// Enabling the thread safe logging actually deadlocks right now.
// Need to fix this at some point.
// static Mutex g_LogThreadedMutex(Mutex::eMutexTypeRecursive);
// Mutex::Locker locker (g_LogThreadedMutex);
// Add a sequence ID if requested
if (m_options.Test (LLDB_LOG_OPTION_PREPEND_SEQUENCE))
header.Printf ("%u ", ++g_sequence_id);
// Timestamp if requested
if (m_options.Test (LLDB_LOG_OPTION_PREPEND_TIMESTAMP))
{
struct timeval tv = TimeValue::Now().GetAsTimeVal();
header.Printf ("%9ld.%6.6d ", tv.tv_sec, (int32_t)tv.tv_usec);
}
// Add the process and thread if requested
if (m_options.Test (LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD))
header.Printf ("[%4.4x/%4.4" PRIx64 "]: ", getpid(), Host::GetCurrentThreadID());
// Add the process and thread if requested
if (m_options.Test (LLDB_LOG_OPTION_PREPEND_THREAD_NAME))
{
std::string thread_name (Host::GetThreadName (getpid(), Host::GetCurrentThreadID()));
if (!thread_name.empty())
header.Printf ("%s ", thread_name.c_str());
}
header.PrintfVarArg (format, args);
m_stream_sp->Printf("%s\n", header.GetData());
if (m_options.Test (LLDB_LOG_OPTION_BACKTRACE))
Host::Backtrace (*m_stream_sp, 1024);
m_stream_sp->Flush();
}
}
void
Log::PutCString (const char *cstr)
{
Printf ("%s", cstr);
}
//----------------------------------------------------------------------
// Simple variable argument logging with flags.
//----------------------------------------------------------------------
void
Log::Printf(const char *format, ...)
{
va_list args;
va_start (args, format);
PrintfWithFlagsVarArg (0, format, args);
va_end (args);
}
void
Log::VAPrintf (const char *format, va_list args)
{
PrintfWithFlagsVarArg (0, format, args);
}
//----------------------------------------------------------------------
// Simple variable argument logging with flags.
//----------------------------------------------------------------------
void
Log::PrintfWithFlags (uint32_t flags, const char *format, ...)
{
va_list args;
va_start (args, format);
PrintfWithFlagsVarArg (flags, format, args);
va_end (args);
}
//----------------------------------------------------------------------
// Print debug strings if and only if the global debug option is set to
// a non-zero value.
//----------------------------------------------------------------------
void
Log::Debug (const char *format, ...)
{
if (GetOptions().Test(LLDB_LOG_OPTION_DEBUG))
{
va_list args;
va_start (args, format);
PrintfWithFlagsVarArg (LLDB_LOG_FLAG_DEBUG, format, args);
va_end (args);
}
}
//----------------------------------------------------------------------
// Print debug strings if and only if the global debug option is set to
// a non-zero value.
//----------------------------------------------------------------------
void
Log::DebugVerbose (const char *format, ...)
{
if (GetOptions().AllSet (LLDB_LOG_OPTION_DEBUG | LLDB_LOG_OPTION_VERBOSE))
{
va_list args;
va_start (args, format);
PrintfWithFlagsVarArg (LLDB_LOG_FLAG_DEBUG | LLDB_LOG_FLAG_VERBOSE, format, args);
va_end (args);
}
}
//----------------------------------------------------------------------
// Log only if all of the bits are set
//----------------------------------------------------------------------
void
Log::LogIf (uint32_t bits, const char *format, ...)
{
if (m_options.AllSet (bits))
{
va_list args;
va_start (args, format);
PrintfWithFlagsVarArg (0, format, args);
va_end (args);
}
}
//----------------------------------------------------------------------
// Printing of errors that are not fatal.
//----------------------------------------------------------------------
void
Log::Error (const char *format, ...)
{
char *arg_msg = NULL;
va_list args;
va_start (args, format);
::vasprintf (&arg_msg, format, args);
va_end (args);
if (arg_msg != NULL)
{
PrintfWithFlags (LLDB_LOG_FLAG_ERROR, "error: %s", arg_msg);
free (arg_msg);
}
}
//----------------------------------------------------------------------
// Printing of errors that ARE fatal. Exit with ERR exit code
// immediately.
//----------------------------------------------------------------------
void
Log::FatalError (int err, const char *format, ...)
{
char *arg_msg = NULL;
va_list args;
va_start (args, format);
::vasprintf (&arg_msg, format, args);
va_end (args);
if (arg_msg != NULL)
{
PrintfWithFlags (LLDB_LOG_FLAG_ERROR | LLDB_LOG_FLAG_FATAL, "error: %s", arg_msg);
::free (arg_msg);
}
::exit (err);
}
//----------------------------------------------------------------------
// Printing of warnings that are not fatal only if verbose mode is
// enabled.
//----------------------------------------------------------------------
void
Log::Verbose (const char *format, ...)
{
if (m_options.Test(LLDB_LOG_OPTION_VERBOSE))
{
va_list args;
va_start (args, format);
PrintfWithFlagsVarArg (LLDB_LOG_FLAG_VERBOSE, format, args);
va_end (args);
}
}
//----------------------------------------------------------------------
// Printing of warnings that are not fatal only if verbose mode is
// enabled.
//----------------------------------------------------------------------
void
Log::WarningVerbose (const char *format, ...)
{
if (m_options.Test(LLDB_LOG_OPTION_VERBOSE))
{
char *arg_msg = NULL;
va_list args;
va_start (args, format);
::vasprintf (&arg_msg, format, args);
va_end (args);
if (arg_msg != NULL)
{
PrintfWithFlags (LLDB_LOG_FLAG_WARNING | LLDB_LOG_FLAG_VERBOSE, "warning: %s", arg_msg);
free (arg_msg);
}
}
}
//----------------------------------------------------------------------
// Printing of warnings that are not fatal.
//----------------------------------------------------------------------
void
Log::Warning (const char *format, ...)
{
char *arg_msg = NULL;
va_list args;
va_start (args, format);
::vasprintf (&arg_msg, format, args);
va_end (args);
if (arg_msg != NULL)
{
PrintfWithFlags (LLDB_LOG_FLAG_WARNING, "warning: %s", arg_msg);
free (arg_msg);
}
}
typedef std::map <ConstString, Log::Callbacks> CallbackMap;
typedef CallbackMap::iterator CallbackMapIter;
typedef std::map <ConstString, LogChannelSP> LogChannelMap;
typedef LogChannelMap::iterator LogChannelMapIter;
// Surround our callback map with a singleton function so we don't have any
// global initializers.
static CallbackMap &
GetCallbackMap ()
{
static CallbackMap g_callback_map;
return g_callback_map;
}
static LogChannelMap &
GetChannelMap ()
{
static LogChannelMap g_channel_map;
return g_channel_map;
}
void
Log::RegisterLogChannel (const ConstString &channel, const Log::Callbacks &log_callbacks)
{
GetCallbackMap().insert(std::make_pair(channel, log_callbacks));
}
bool
Log::UnregisterLogChannel (const ConstString &channel)
{
return GetCallbackMap().erase(channel) != 0;
}
bool
Log::GetLogChannelCallbacks (const ConstString &channel, Log::Callbacks &log_callbacks)
{
CallbackMap &callback_map = GetCallbackMap ();
CallbackMapIter pos = callback_map.find(channel);
if (pos != callback_map.end())
{
log_callbacks = pos->second;
return true;
}
::memset (&log_callbacks, 0, sizeof(log_callbacks));
return false;
}
void
Log::EnableAllLogChannels
(
StreamSP &log_stream_sp,
uint32_t log_options,
const char **categories,
Stream *feedback_strm
)
{
CallbackMap &callback_map = GetCallbackMap ();
CallbackMapIter pos, end = callback_map.end();
for (pos = callback_map.begin(); pos != end; ++pos)
pos->second.enable (log_stream_sp, log_options, categories, feedback_strm);
LogChannelMap &channel_map = GetChannelMap ();
LogChannelMapIter channel_pos, channel_end = channel_map.end();
for (channel_pos = channel_map.begin(); channel_pos != channel_end; ++channel_pos)
{
channel_pos->second->Enable (log_stream_sp, log_options, feedback_strm, categories);
}
}
void
Log::AutoCompleteChannelName (const char *channel_name, StringList &matches)
{
LogChannelMap &map = GetChannelMap ();
LogChannelMapIter pos, end = map.end();
for (pos = map.begin(); pos != end; ++pos)
{
const char *pos_channel_name = pos->first.GetCString();
if (channel_name && channel_name[0])
{
if (NameMatches (channel_name, eNameMatchStartsWith, pos_channel_name))
{
matches.AppendString(pos_channel_name);
}
}
else
matches.AppendString(pos_channel_name);
}
}
void
Log::DisableAllLogChannels (Stream *feedback_strm)
{
CallbackMap &callback_map = GetCallbackMap ();
CallbackMapIter pos, end = callback_map.end();
const char *categories[1] = {NULL};
for (pos = callback_map.begin(); pos != end; ++pos)
pos->second.disable (categories, feedback_strm);
LogChannelMap &channel_map = GetChannelMap ();
LogChannelMapIter channel_pos, channel_end = channel_map.end();
for (channel_pos = channel_map.begin(); channel_pos != channel_end; ++channel_pos)
channel_pos->second->Disable (categories, feedback_strm);
}
void
Log::Initialize()
{
Log::Callbacks log_callbacks = { DisableLog, EnableLog, ListLogCategories };
Log::RegisterLogChannel (ConstString("lldb"), log_callbacks);
}
void
Log::Terminate ()
{
DisableAllLogChannels (NULL);
}
void
Log::ListAllLogChannels (Stream *strm)
{
CallbackMap &callback_map = GetCallbackMap ();
LogChannelMap &channel_map = GetChannelMap ();
if (callback_map.empty() && channel_map.empty())
{
strm->PutCString ("No logging channels are currently registered.\n");
return;
}
CallbackMapIter pos, end = callback_map.end();
for (pos = callback_map.begin(); pos != end; ++pos)
pos->second.list_categories (strm);
uint32_t idx = 0;
const char *name;
for (idx = 0; (name = PluginManager::GetLogChannelCreateNameAtIndex (idx)) != NULL; ++idx)
{
LogChannelSP log_channel_sp(LogChannel::FindPlugin (name));
if (log_channel_sp)
log_channel_sp->ListCategories (strm);
}
}
bool
Log::GetVerbose() const
{
// FIXME: This has to be centralized between the stream and the log...
if (m_options.Test(LLDB_LOG_OPTION_VERBOSE))
return true;
if (m_stream_sp)
return m_stream_sp->GetVerbose();
return false;
}
//------------------------------------------------------------------
// Returns true if the debug flag bit is set in this stream.
//------------------------------------------------------------------
bool
Log::GetDebug() const
{
if (m_stream_sp)
return m_stream_sp->GetDebug();
return false;
}
LogChannelSP
LogChannel::FindPlugin (const char *plugin_name)
{
LogChannelSP log_channel_sp;
LogChannelMap &channel_map = GetChannelMap ();
ConstString log_channel_name (plugin_name);
LogChannelMapIter pos = channel_map.find (log_channel_name);
if (pos == channel_map.end())
{
ConstString const_plugin_name (plugin_name);
LogChannelCreateInstance create_callback = PluginManager::GetLogChannelCreateCallbackForPluginName (const_plugin_name);
if (create_callback)
{
log_channel_sp.reset(create_callback());
if (log_channel_sp)
{
// Cache the one and only loaded instance of each log channel
// plug-in after it has been loaded once.
channel_map[log_channel_name] = log_channel_sp;
}
}
}
else
{
// We have already loaded an instance of this log channel class,
// so just return the cached instance.
log_channel_sp = pos->second;
}
return log_channel_sp;
}
LogChannel::LogChannel () :
m_log_ap ()
{
}
LogChannel::~LogChannel ()
{
}