/* | |
* Copyright 2017 Google Inc. All Rights Reserved. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
#include <algorithm> | |
#include <string> | |
#include <vector> | |
#include "nb/rewindable_vector.h" | |
#include "nb/simple_profiler.h" | |
#include "nb/thread_local_object.h" | |
#include "starboard/atomic.h" | |
#include "starboard/once.h" | |
#include "starboard/string.h" | |
#include "starboard/time.h" | |
namespace nb { | |
namespace { | |
class SimpleProfilerManager { | |
public: | |
struct Entry { | |
Entry() : name(nullptr), time_delta(0), indent_value(0) {} | |
Entry(const char* n, SbTimeMonotonic dt, int ind) | |
: name(n), time_delta(dt), indent_value(ind) {} | |
Entry(const Entry& e) = default; | |
const char* name; | |
SbTimeMonotonic time_delta; | |
int indent_value; | |
}; | |
using MessageHandlerFunction = SimpleProfiler::MessageHandlerFunction; | |
using ClockFunction = SimpleProfiler::ClockFunction; | |
SimpleProfilerManager() | |
: default_enabled_(true), message_handler_(nullptr), | |
clock_function_(nullptr) {} | |
int BeginInstance(const char* name) { | |
Data& d = ThreadLocal(); | |
if (!d.enable_flag) { | |
return -1; | |
} | |
SbTimeMonotonic now = NowTime(); | |
// SbAtomicMemoryBarrier() to Keep order of operations so clock doesn't | |
// get sampled out of order. | |
SbAtomicMemoryBarrier(); | |
d.profiles.emplace_back(name, now, d.instance_count); | |
++d.instance_count; | |
return d.profiles.size() - 1; | |
} | |
void Output(const Entry& entry, std::stringstream* sstream) { | |
for (auto i = 0; i < entry.indent_value; ++i) { | |
(*sstream) << ' '; | |
} | |
(*sstream) << entry.name << ": " << entry.time_delta << "us\n"; | |
} | |
void FinishInstance(int index) { | |
Data& d = ThreadLocal(); | |
if (!d.enable_flag || index < 0) { | |
return; | |
} | |
--d.instance_count; | |
Entry& entry = d.profiles[static_cast<size_t>(index)]; | |
entry.time_delta = NowTime() - entry.time_delta; | |
// SbAtomicMemoryBarrier() to Keep order of operations so clock doesn't | |
// get sampled out of order. | |
SbAtomicMemoryBarrier(); | |
if (d.instance_count == 0) { | |
std::stringstream ss; | |
for (auto it = d.profiles.begin(); it != d.profiles.end(); ++it) { | |
Output(*it, &ss); | |
} | |
d.profiles.clear(); | |
HandleMessage(ss.str()); | |
} | |
} | |
bool ThreadLocalEnabled() { return ThreadLocal().enable_flag; } | |
void SetThreadLocalEnabled(bool value) { | |
Data& d = ThreadLocal(); | |
d.enable_flag = value; | |
d.enable_flag_set = true; | |
} | |
bool IsEnabled() const { | |
const Data& d = ThreadLocal(); | |
if (d.enable_flag_set) { | |
return d.enable_flag; | |
} | |
return default_enabled_; | |
} | |
void SetDefaultEnabled(bool value) { default_enabled_ = value; } | |
void SetMessageHandler(MessageHandlerFunction handler) { | |
message_handler_ = handler; | |
} | |
void SetClockFunction(ClockFunction fcn) { | |
clock_function_ = fcn; | |
} | |
SbTimeMonotonic NowTime() { | |
if (!clock_function_) { | |
return SbTimeGetMonotonicNow(); | |
} else { | |
return clock_function_(); | |
} | |
} | |
private: | |
struct Data { | |
bool enable_flag = true; | |
bool enable_flag_set = false; | |
int instance_count = 0; | |
std::vector<Entry> profiles; | |
}; | |
void HandleMessage(const std::string& str) { | |
if (message_handler_) { | |
message_handler_(str.c_str()); | |
} else { | |
SbLogRaw(str.c_str()); | |
} | |
} | |
Data& ThreadLocal() { return *thread_local_.GetOrCreate(); } | |
const Data& ThreadLocal() const { return *thread_local_.GetOrCreate(); } | |
mutable nb::ThreadLocalObject<Data> thread_local_; | |
bool default_enabled_; | |
MessageHandlerFunction message_handler_; | |
ClockFunction clock_function_; | |
}; | |
SB_ONCE_INITIALIZE_FUNCTION(SimpleProfilerManager, GetSimpleProfilerManager); | |
} // namespace | |
SimpleProfiler::EnableScope::EnableScope(bool enabled) { | |
SimpleProfilerManager* mgr = GetSimpleProfilerManager(); | |
prev_enabled_ = mgr->ThreadLocalEnabled(); | |
mgr->SetThreadLocalEnabled(enabled); | |
} | |
SimpleProfiler::EnableScope::~EnableScope() { | |
GetSimpleProfilerManager()->SetThreadLocalEnabled(prev_enabled_); | |
} | |
SimpleProfiler::SimpleProfiler(const char* name) { | |
momento_index_ = GetSimpleProfilerManager()->BeginInstance(name); | |
} | |
SimpleProfiler::~SimpleProfiler() { | |
GetSimpleProfilerManager()->FinishInstance(momento_index_); | |
} | |
void SimpleProfiler::SetEnabledByDefault(bool value) { | |
GetSimpleProfilerManager()->SetDefaultEnabled(value); | |
} | |
bool SimpleProfiler::IsEnabled() { | |
return GetSimpleProfilerManager()->IsEnabled(); | |
} | |
void SimpleProfiler::SetLoggingFunction(MessageHandlerFunction fcn) { | |
GetSimpleProfilerManager()->SetMessageHandler(fcn); | |
} | |
void SimpleProfiler::SetClockFunction(ClockFunction fcn) { | |
GetSimpleProfilerManager()->SetClockFunction(fcn); | |
} | |
} // namespace nb |