carb/profiler/ProfilerUtils.h
File members: carb/profiler/ProfilerUtils.h
// Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//
#pragma once
#include "IProfiler.h"
#include "../cpp/Atomic.h"
#include "../settings/ISettings.h"
#include "../InterfaceUtils.h"
namespace carb
{
namespace profiler
{
namespace detail
{
struct String2
{
carb::profiler::StaticStringType first;
carb::profiler::StaticStringType second;
constexpr String2(StaticStringType first, StaticStringType second) noexcept : first(first), second(second)
{
}
};
constexpr String2 makeString2(StaticStringType first, StaticStringType second) noexcept
{
return String2{ first, second };
}
struct String3
{
carb::profiler::StaticStringType first;
carb::profiler::StaticStringType second;
carb::profiler::StaticStringType third;
constexpr String3(StaticStringType first, StaticStringType second, StaticStringType third) noexcept
: first(first), second(second), third(third)
{
}
};
constexpr String3 makeString3(StaticStringType first, StaticStringType second, StaticStringType third) noexcept
{
return String3{ first, second, third };
}
} // namespace detail
class Channel final
{
uint64_t m_mask;
bool m_enabled;
const char* m_name;
Channel* m_next;
struct ModuleData
{
Channel* head{ nullptr };
LoadHookHandle onSettingsLoadHandle{ kInvalidLoadHook };
dictionary::SubscriptionId* changeSubscription{ nullptr };
#if CARB_ASSERT_ENABLED
~ModuleData()
{
// If these weren't unregistered we could crash later
CARB_ASSERT(onSettingsLoadHandle == kInvalidLoadHook);
CARB_ASSERT(changeSubscription == nullptr);
}
#endif
};
static ModuleData& moduleData()
{
static ModuleData s_moduleData;
return s_moduleData;
}
static void onSettingsLoad(const PluginDesc&, void*)
{
// DO NOT USE getCachedInterface here! This is called by a load hook, which can be triggered by
// getCachedInterface in this module. This means if we were to recursively call getCachedInterface() here, we
// could hang indefinitely as this thread is the thread responsible for loading the cached interface.
if (loadSettings(getFramework()->tryAcquireInterface<settings::ISettings>(), true))
{
g_carbFramework->removeLoadHook(moduleData().onSettingsLoadHandle);
moduleData().onSettingsLoadHandle = kInvalidLoadHook;
}
}
static void onSettingsUnload(void*, void*)
{
// Settings was unloaded. Make sure we no longer have a subscription callback.
moduleData().changeSubscription = nullptr;
}
static void onSettingsChange(const dictionary::Item*,
const dictionary::Item* changedItem,
dictionary::ChangeEventType eventType,
void*)
{
if (eventType == dictionary::ChangeEventType::eDestroyed)
return;
auto dict = getCachedInterface<dictionary::IDictionary>();
// Only care about elements that can change at runtime.
const char* name = dict->getItemName(changedItem);
if (strcmp(name, "enabled") != 0 && strcmp(name, "mask") != 0)
return;
loadSettings(
getCachedInterface<settings::ISettings>(), false, dict->getItemName(dict->getItemParent(changedItem)));
}
static bool loadSettings(settings::ISettings* settings, bool initial, const char* channelName = nullptr)
{
// Only proceed if settings is already initialized
if (!settings)
return false;
auto dict = carb::getCachedInterface<dictionary::IDictionary>();
if (!dict)
return false;
auto root = settings->getSettingsDictionary("/profiler/channels");
if (root)
{
for (Channel* c = moduleData().head; c; c = c->m_next)
{
if (channelName && strcmp(c->m_name, channelName) != 0)
continue;
auto channelRoot = dict->getItem(root, c->m_name);
if (!channelRoot)
continue;
auto enabled = dict->getItem(channelRoot, "enabled");
if (enabled)
{
c->setEnabled(dict->getAsBool(enabled));
}
auto mask = dict->getItem(channelRoot, "mask");
if (mask)
{
c->setMask(uint64_t(dict->getAsInt64(mask)));
}
}
}
// Register a change subscription on initial setup if we have any channels.
if (initial && !moduleData().changeSubscription && moduleData().head)
{
moduleData().changeSubscription =
settings->subscribeToTreeChangeEvents("/profiler/channels", onSettingsChange, nullptr);
::g_carbFramework->addReleaseHook(settings, onSettingsUnload, nullptr);
}
return true;
}
public:
Channel(uint64_t mask, bool enabled, const char* name) : m_mask(mask), m_enabled(enabled), m_name(name)
{
// Add ourselves to the list of channels for this module
auto& head = moduleData().head;
m_next = head;
head = this;
}
const char* getName() const noexcept
{
return m_name;
}
uint64_t getMask() const noexcept
{
return m_mask;
}
void setMask(uint64_t mask) noexcept
{
cpp::atomic_ref<uint64_t>(m_mask).store(mask, std::memory_order_release);
}
bool isEnabled() const noexcept
{
return m_enabled;
}
void setEnabled(bool enabled) noexcept
{
cpp::atomic_ref<bool>(m_enabled).store(enabled, std::memory_order_release);
}
static void onProfilerRegistered()
{
// example-begin acquire-without-init
// Don't try to load settings, but if it's already available we will load settings from it.
auto settings = g_carbFramework->tryAcquireExistingInterface<settings::ISettings>();
// example-end acquire-without-init
if (!loadSettings(settings, true))
{
// If settings isn't available, wait for it to load.
moduleData().onSettingsLoadHandle =
g_carbFramework->addLoadHook<settings::ISettings>(nullptr, onSettingsLoad, nullptr);
}
}
static void onProfilerUnregistered()
{
if (moduleData().onSettingsLoadHandle != kInvalidLoadHook)
{
g_carbFramework->removeLoadHook(moduleData().onSettingsLoadHandle);
moduleData().onSettingsLoadHandle = kInvalidLoadHook;
}
if (moduleData().changeSubscription)
{
// Don't re-initialize settings if it's already been unloaded (though in this case we should've gotten a
// callback)
auto settings = g_carbFramework->tryAcquireExistingInterface<settings::ISettings>();
CARB_ASSERT(settings);
if (settings)
{
settings->unsubscribeToChangeEvents(moduleData().changeSubscription);
g_carbFramework->removeReleaseHook(settings, onSettingsUnload, nullptr);
}
moduleData().changeSubscription = nullptr;
}
}
};
class ProfileZoneStatic final
{
const uint64_t m_mask;
ZoneId m_zoneId;
public:
ProfileZoneStatic(const uint64_t mask, const ::carb::profiler::detail::String3& tup, int line) : m_mask(mask)
{
auto profiler = g_carbProfiler.load(std::memory_order_acquire);
if (profiler && ((mask ? mask : kCaptureMaskDefault) & g_carbProfilerMask.load(std::memory_order_relaxed)))
m_zoneId = profiler->beginStatic(m_mask, tup.first, tup.second, line, tup.third);
else
m_zoneId = kNoZoneId;
}
ProfileZoneStatic(const Channel& channel, const ::carb::profiler::detail::String3& tup, int line)
: m_mask(channel.getMask())
{
auto profiler = g_carbProfiler.load(std::memory_order_acquire);
if (profiler && channel.isEnabled())
m_zoneId = profiler->beginStatic(m_mask, tup.first, tup.second, line, tup.third);
else
m_zoneId = kNoZoneId;
}
~ProfileZoneStatic()
{
auto profiler = g_carbProfiler.load(std::memory_order_acquire);
if (profiler && m_zoneId != kNoZoneId)
profiler->endEx(m_mask, m_zoneId);
}
};
class ProfileZoneDynamic final
{
const uint64_t m_mask;
ZoneId m_zoneId;
public:
template <typename... Args>
ProfileZoneDynamic(
const uint64_t mask, const ::carb::profiler::detail::String2& tup, int line, const char* nameFmt, Args&&... args)
: m_mask(mask)
{
auto profiler = g_carbProfiler.load(std::memory_order_acquire);
if (profiler && ((mask ? mask : kCaptureMaskDefault) & g_carbProfilerMask.load(std::memory_order_relaxed)))
m_zoneId = profiler->beginDynamic(m_mask, tup.first, tup.second, line, nameFmt, std::forward<Args>(args)...);
else
m_zoneId = kNoZoneId;
}
template <typename... Args>
ProfileZoneDynamic(const Channel& channel,
const ::carb::profiler::detail::String2& tup,
int line,
const char* nameFmt,
Args&&... args)
: m_mask(channel.getMask())
{
auto profiler = g_carbProfiler.load(std::memory_order_acquire);
if (profiler && channel.isEnabled())
m_zoneId = profiler->beginDynamic(m_mask, tup.first, tup.second, line, nameFmt, std::forward<Args>(args)...);
else
m_zoneId = kNoZoneId;
}
~ProfileZoneDynamic()
{
auto profiler = g_carbProfiler.load(std::memory_order_acquire);
if (profiler && m_zoneId != kNoZoneId)
profiler->endEx(m_mask, m_zoneId);
}
};
} // namespace profiler
} // namespace carb