blob: 6b313aaa94b23bb30bf8fbcadc15d6477ccc7719 [file] [log] [blame]
/*
* 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