Data formatters now cache themselves. This commit provides a new formatter cache mechanism. Upon resolving a formatter (summary or synthetic), LLDB remembers the resolution for later faster retrieval. Also moved the data formatters subsystem from the core to its own group and folder for easier management, and done some code reorganization. The ObjC runtime v1 now returns a class name if asked for the dynamic type of an object. This is required for formatters caching to work with the v1 runtime. Lastly, this commit disposes of the old hack where ValueObjects had to remember whether they were queried for formatters with their static or dynamic type. Now the ValueObjectDynamicValue class works well enough that we can use its dynamic value setting for the same purpose. llvm-svn: 173728
251 lines
6.2 KiB
C++
251 lines
6.2 KiB
C++
//===-- Timer.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/Timer.h"
|
|
|
|
#include <map>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
|
|
#include "lldb/Core/Stream.h"
|
|
#include "lldb/Host/Mutex.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
using namespace lldb_private;
|
|
|
|
#define TIMER_INDENT_AMOUNT 2
|
|
static bool g_quiet = true;
|
|
uint32_t Timer::g_depth = 0;
|
|
uint32_t Timer::g_display_depth = 0;
|
|
FILE * Timer::g_file = NULL;
|
|
typedef std::vector<Timer *> TimerStack;
|
|
typedef std::map<const char *, uint64_t> TimerCategoryMap;
|
|
static pthread_key_t g_key;
|
|
|
|
static Mutex &
|
|
GetCategoryMutex()
|
|
{
|
|
static Mutex g_category_mutex(Mutex::eMutexTypeNormal);
|
|
return g_category_mutex;
|
|
}
|
|
|
|
static TimerCategoryMap &
|
|
GetCategoryMap()
|
|
{
|
|
static TimerCategoryMap g_category_map;
|
|
return g_category_map;
|
|
}
|
|
|
|
|
|
static TimerStack *
|
|
GetTimerStackForCurrentThread ()
|
|
{
|
|
void *timer_stack = ::pthread_getspecific (g_key);
|
|
if (timer_stack == NULL)
|
|
{
|
|
::pthread_setspecific (g_key, new TimerStack);
|
|
timer_stack = ::pthread_getspecific (g_key);
|
|
}
|
|
return (TimerStack *)timer_stack;
|
|
}
|
|
|
|
void
|
|
ThreadSpecificCleanup (void *p)
|
|
{
|
|
delete (TimerStack *)p;
|
|
}
|
|
|
|
void
|
|
Timer::SetQuiet (bool value)
|
|
{
|
|
g_quiet = value;
|
|
}
|
|
|
|
void
|
|
Timer::Initialize ()
|
|
{
|
|
Timer::g_file = stdout;
|
|
::pthread_key_create (&g_key, ThreadSpecificCleanup);
|
|
|
|
}
|
|
|
|
Timer::Timer (const char *category, const char *format, ...) :
|
|
m_category (category),
|
|
m_total_start (),
|
|
m_timer_start (),
|
|
m_total_ticks (0),
|
|
m_timer_ticks (0)
|
|
{
|
|
if (g_depth++ < g_display_depth)
|
|
{
|
|
if (g_quiet == false)
|
|
{
|
|
// Indent
|
|
::fprintf (g_file, "%*s", g_depth * TIMER_INDENT_AMOUNT, "");
|
|
// Print formatted string
|
|
va_list args;
|
|
va_start (args, format);
|
|
::vfprintf (g_file, format, args);
|
|
va_end (args);
|
|
|
|
// Newline
|
|
::fprintf (g_file, "\n");
|
|
}
|
|
TimeValue start_time(TimeValue::Now());
|
|
m_total_start = start_time;
|
|
m_timer_start = start_time;
|
|
TimerStack *stack = GetTimerStackForCurrentThread ();
|
|
if (stack)
|
|
{
|
|
if (stack->empty() == false)
|
|
stack->back()->ChildStarted (start_time);
|
|
stack->push_back(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Timer::~Timer()
|
|
{
|
|
if (m_total_start.IsValid())
|
|
{
|
|
TimeValue stop_time = TimeValue::Now();
|
|
if (m_total_start.IsValid())
|
|
{
|
|
m_total_ticks += (stop_time - m_total_start);
|
|
m_total_start.Clear();
|
|
}
|
|
if (m_timer_start.IsValid())
|
|
{
|
|
m_timer_ticks += (stop_time - m_timer_start);
|
|
m_timer_start.Clear();
|
|
}
|
|
|
|
TimerStack *stack = GetTimerStackForCurrentThread ();
|
|
if (stack)
|
|
{
|
|
assert (stack->back() == this);
|
|
stack->pop_back();
|
|
if (stack->empty() == false)
|
|
stack->back()->ChildStopped(stop_time);
|
|
}
|
|
|
|
const uint64_t total_nsec_uint = GetTotalElapsedNanoSeconds();
|
|
const uint64_t timer_nsec_uint = GetTimerElapsedNanoSeconds();
|
|
const double total_nsec = total_nsec_uint;
|
|
const double timer_nsec = timer_nsec_uint;
|
|
|
|
if (g_quiet == false)
|
|
{
|
|
|
|
::fprintf (g_file,
|
|
"%*s%.9f sec (%.9f sec)\n",
|
|
(g_depth - 1) *TIMER_INDENT_AMOUNT, "",
|
|
total_nsec / 1000000000.0,
|
|
timer_nsec / 1000000000.0);
|
|
}
|
|
|
|
// Keep total results for each category so we can dump results.
|
|
Mutex::Locker locker (GetCategoryMutex());
|
|
TimerCategoryMap &category_map = GetCategoryMap();
|
|
category_map[m_category] += timer_nsec_uint;
|
|
}
|
|
if (g_depth > 0)
|
|
--g_depth;
|
|
}
|
|
|
|
uint64_t
|
|
Timer::GetTotalElapsedNanoSeconds()
|
|
{
|
|
uint64_t total_ticks = m_total_ticks;
|
|
|
|
// If we are currently running, we need to add the current
|
|
// elapsed time of the running timer...
|
|
if (m_total_start.IsValid())
|
|
total_ticks += (TimeValue::Now() - m_total_start);
|
|
|
|
return total_ticks;
|
|
}
|
|
|
|
uint64_t
|
|
Timer::GetTimerElapsedNanoSeconds()
|
|
{
|
|
uint64_t timer_ticks = m_timer_ticks;
|
|
|
|
// If we are currently running, we need to add the current
|
|
// elapsed time of the running timer...
|
|
if (m_timer_start.IsValid())
|
|
timer_ticks += (TimeValue::Now() - m_timer_start);
|
|
|
|
return timer_ticks;
|
|
}
|
|
|
|
void
|
|
Timer::ChildStarted (const TimeValue& start_time)
|
|
{
|
|
if (m_timer_start.IsValid())
|
|
{
|
|
m_timer_ticks += (start_time - m_timer_start);
|
|
m_timer_start.Clear();
|
|
}
|
|
}
|
|
|
|
void
|
|
Timer::ChildStopped (const TimeValue& stop_time)
|
|
{
|
|
if (!m_timer_start.IsValid())
|
|
m_timer_start = stop_time;
|
|
}
|
|
|
|
void
|
|
Timer::SetDisplayDepth (uint32_t depth)
|
|
{
|
|
g_display_depth = depth;
|
|
}
|
|
|
|
|
|
/* binary function predicate:
|
|
* - returns whether a person is less than another person
|
|
*/
|
|
static bool
|
|
CategoryMapIteratorSortCriterion (const TimerCategoryMap::const_iterator& lhs, const TimerCategoryMap::const_iterator& rhs)
|
|
{
|
|
return lhs->second > rhs->second;
|
|
}
|
|
|
|
|
|
void
|
|
Timer::ResetCategoryTimes ()
|
|
{
|
|
Mutex::Locker locker (GetCategoryMutex());
|
|
TimerCategoryMap &category_map = GetCategoryMap();
|
|
category_map.clear();
|
|
}
|
|
|
|
void
|
|
Timer::DumpCategoryTimes (Stream *s)
|
|
{
|
|
Mutex::Locker locker (GetCategoryMutex());
|
|
TimerCategoryMap &category_map = GetCategoryMap();
|
|
std::vector<TimerCategoryMap::const_iterator> sorted_iterators;
|
|
TimerCategoryMap::const_iterator pos, end = category_map.end();
|
|
for (pos = category_map.begin(); pos != end; ++pos)
|
|
{
|
|
sorted_iterators.push_back (pos);
|
|
}
|
|
std::sort (sorted_iterators.begin(), sorted_iterators.end(), CategoryMapIteratorSortCriterion);
|
|
|
|
const size_t count = sorted_iterators.size();
|
|
for (size_t i=0; i<count; ++i)
|
|
{
|
|
const double timer_nsec = sorted_iterators[i]->second;
|
|
s->Printf("%.9f sec for %s\n", timer_nsec / 1000000000.0, sorted_iterators[i]->first);
|
|
}
|
|
}
|