diff --git a/filament/backend/src/CommandStream.cpp b/filament/backend/src/CommandStream.cpp index 429cfa601b..bb2207be53 100644 --- a/filament/backend/src/CommandStream.cpp +++ b/filament/backend/src/CommandStream.cpp @@ -84,7 +84,7 @@ void CommandStream::execute(void* buffer) { Profiler profiler; - if constexpr (SYSTRACE_TAG) { + if (SYSTRACE_TAG) { if (UTILS_UNLIKELY(mUsePerformanceCounter)) { // we want to remove all this when tracing is completely disabled profiler.resetEvents(Profiler::EV_CPU_CYCLES | Profiler::EV_BPU_MISSES); @@ -100,7 +100,7 @@ void CommandStream::execute(void* buffer) { } }); - if constexpr (SYSTRACE_TAG) { + if (SYSTRACE_TAG) { if (UTILS_UNLIKELY(mUsePerformanceCounter)) { // we want to remove all this when tracing is completely disabled profiler.stop(); diff --git a/filament/backend/src/vulkan/VulkanConstants.h b/filament/backend/src/vulkan/VulkanConstants.h index c6524296ce..1a454d7069 100644 --- a/filament/backend/src/vulkan/VulkanConstants.h +++ b/filament/backend/src/vulkan/VulkanConstants.h @@ -152,12 +152,12 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION)); #elif FVK_ENABLED(FVK_DEBUG_SYSTRACE) #include - + #define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT() #define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker) #define FVK_SYSTRACE_END() SYSTRACE_NAME_END() - #define FVK_SYSTRACE_SCOPE() SYSTRACE_CALL() - #define FVK_PROFILE_MARKER(marker) SYSTRACE_CALL() + #define FVK_SYSTRACE_SCOPE() SYSTRACE_NAME(__func__) + #define FVK_PROFILE_MARKER(marker) FVK_SYSTRACE_SCOPE() #else #define FVK_SYSTRACE_CONTEXT() diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index e71d8fc897..ac9b6b502f 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -50,8 +50,6 @@ #include #include #include - -#define SYSTRACE_TAG SYSTRACE_TAG_GLTFIO #include #include diff --git a/libs/gltfio/src/ResourceLoader.cpp b/libs/gltfio/src/ResourceLoader.cpp index fb6b219294..b88a47b701 100644 --- a/libs/gltfio/src/ResourceLoader.cpp +++ b/libs/gltfio/src/ResourceLoader.cpp @@ -37,10 +37,7 @@ #include #include #include - -#define SYSTRACE_TAG SYSTRACE_TAG_GLTFIO #include - #include #include diff --git a/libs/gltfio/src/Utility.cpp b/libs/gltfio/src/Utility.cpp index 9273943c55..f875f71a4e 100644 --- a/libs/gltfio/src/Utility.cpp +++ b/libs/gltfio/src/Utility.cpp @@ -21,8 +21,6 @@ #include "GltfEnums.h" #include - -#define SYSTRACE_TAG SYSTRACE_TAG_GLTFIO #include #define CGLTF_IMPLEMENTATION diff --git a/libs/utils/include/utils/Systrace.h b/libs/utils/include/utils/Systrace.h index 02ff62217b..9f5a7f2a59 100644 --- a/libs/utils/include/utils/Systrace.h +++ b/libs/utils/include/utils/Systrace.h @@ -17,23 +17,24 @@ #ifndef TNT_UTILS_SYSTRACE_H #define TNT_UTILS_SYSTRACE_H -#define SYSTRACE_TAG_DISABLED (0) -#define SYSTRACE_TAG_FILAMENT (2) // don't change used in makefiles -#define SYSTRACE_TAG_JOBSYSTEM (3) -#define SYSTRACE_TAG_GLTFIO (4) + +#define SYSTRACE_TAG_NEVER (0) +#define SYSTRACE_TAG_ALWAYS (1<<0) +#define SYSTRACE_TAG_FILAMENT (1<<1) // don't change, used in makefiles +#define SYSTRACE_TAG_JOBSYSTEM (1<<2) /* - * The SYSTRACE_ macros use SYSTRACE_TAG as a category, which must be defined - * before this file is included. + * The SYSTRACE_ macros use SYSTRACE_TAG as a the TAG, which should be defined + * before this file is included. If not, the SYSTRACE_TAG_ALWAYS tag will be used. */ #ifndef SYSTRACE_TAG -# error SYSTRACE_TAG must be set to SYSTRACE_TAG_{DISABLED|FILAMENT|JOBSYSTEM} +#define SYSTRACE_TAG (SYSTRACE_TAG_ALWAYS) #endif // Systrace on Apple platforms is fragile and adds overhead, should only be enabled in dev builds. #ifndef FILAMENT_APPLE_SYSTRACE -# define FILAMENT_APPLE_SYSTRACE 0 +#define FILAMENT_APPLE_SYSTRACE 0 #endif #if defined(__ANDROID__) @@ -43,6 +44,7 @@ #else #define SYSTRACE_ENABLE() +#define SYSTRACE_DISABLE() #define SYSTRACE_CONTEXT() #define SYSTRACE_NAME(name) #define SYSTRACE_FRAME_ID(frame) @@ -54,6 +56,6 @@ #define SYSTRACE_VALUE32(name, val) #define SYSTRACE_VALUE64(name, val) -#endif +#endif // ANDROID #endif // TNT_UTILS_SYSTRACE_H diff --git a/libs/utils/include/utils/android/Systrace.h b/libs/utils/include/utils/android/Systrace.h index a2b74d76b1..760fe5be64 100644 --- a/libs/utils/include/utils/android/Systrace.h +++ b/libs/utils/include/utils/android/Systrace.h @@ -17,75 +17,226 @@ #ifndef TNT_UTILS_ANDROID_SYSTRACE_H #define TNT_UTILS_ANDROID_SYSTRACE_H -#include +#include #include +#include +#include -PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE(systrace, - perfetto::Category("filament"), - perfetto::Category("jobsystem"), - perfetto::Category("gltfio")); +#include -PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(systrace); +// enable tracing +#define SYSTRACE_ENABLE() ::utils::details::Systrace::enable(SYSTRACE_TAG) -#if SYSTRACE_TAG == SYSTRACE_TAG_FILAMENT -# define UTILS_PERFETTO_CATEGORY "filament" -#elif SYSTRACE_TAG == SYSTRACE_TAG_JOBSYSTEM -# define UTILS_PERFETTO_CATEGORY "jobsystem" -#elif SYSTRACE_TAG == SYSTRACE_TAG_GLTFIO -# define UTILS_PERFETTO_CATEGORY "gltfio" -#endif +// disable tracing +#define SYSTRACE_DISABLE() ::utils::details::Systrace::disable(SYSTRACE_TAG) -#if SYSTRACE_TAG == SYSTRACE_TAG_DISABLED -#define SYSTRACE_ENABLE() -#define SYSTRACE_CONTEXT() -#define SYSTRACE_NAME(name) -#define SYSTRACE_FRAME_ID(frame) -#define SYSTRACE_NAME_BEGIN(name) -#define SYSTRACE_NAME_END() -#define SYSTRACE_CALL() -#define SYSTRACE_ASYNC_BEGIN(name, cookie) -#define SYSTRACE_ASYNC_END(name, cookie) -#define SYSTRACE_VALUE32(name, val) -#define SYSTRACE_VALUE64(name, val) +/** + * Creates a Systrace context in the current scope. needed for calling all other systrace + * commands below. + */ +#define SYSTRACE_CONTEXT() ::utils::details::Systrace ___trctx(SYSTRACE_TAG) -#else -#define SYSTRACE_ENABLE() -#define SYSTRACE_CONTEXT() - -#define SYSTRACE_CALL() \ - auto constexpr FILAMENT_SYSTRACE_FUNCTION = perfetto::StaticString(__FUNCTION__); \ - TRACE_EVENT(UTILS_PERFETTO_CATEGORY, FILAMENT_SYSTRACE_FUNCTION) - -#define SYSTRACE_NAME(name) TRACE_EVENT(UTILS_PERFETTO_CATEGORY, nullptr, \ - [&](perfetto::EventContext ctx) { \ - ctx.event()->set_name(name); \ - }) - -#define SYSTRACE_NAME_BEGIN(name) TRACE_EVENT_BEGIN(UTILS_PERFETTO_CATEGORY, nullptr, \ - [&](perfetto::EventContext ctx) { \ - ctx.event()->set_name(name); \ - }) - -#define SYSTRACE_NAME_END() TRACE_EVENT_END(UTILS_PERFETTO_CATEGORY) - -#define SYSTRACE_ASYNC_BEGIN(name, cookie) \ - TRACE_EVENT_BEGIN(UTILS_PERFETTO_CATEGORY, name, perfetto::Track(cookie)) - -#define SYSTRACE_ASYNC_END(name, cookie) \ - TRACE_EVENT_END(UTILS_PERFETTO_CATEGORY, perfetto::Track(cookie)) +// SYSTRACE_NAME traces the beginning and end of the current scope. To trace +// the correct start and end times this macro should be declared first in the +// scope body. +// It also automatically creates a Systrace context +#define SYSTRACE_NAME(name) ::utils::details::ScopedTrace ___tracer(SYSTRACE_TAG, name) +// Denotes that a new frame has started processing. #define SYSTRACE_FRAME_ID(frame) \ - TRACE_EVENT_INSTANT(UTILS_PERFETTO_CATEGORY, "frame", "id", frame) + { /* scope for frame id trace */ \ + char buf[64]; \ + snprintf(buf, 64, "frame %u", frame); \ + SYSTRACE_NAME(buf); \ + } +// SYSTRACE_CALL is an SYSTRACE_NAME that uses the current function name. +#define SYSTRACE_CALL() SYSTRACE_NAME(__FUNCTION__) + +#define SYSTRACE_NAME_BEGIN(name) \ + ___trctx.traceBegin(SYSTRACE_TAG, name) + +#define SYSTRACE_NAME_END() \ + ___trctx.traceEnd(SYSTRACE_TAG) + + +/** + * Trace the beginning of an asynchronous event. Unlike ATRACE_BEGIN/ATRACE_END + * contexts, asynchronous events do not need to be nested. The name describes + * the event, and the cookie provides a unique identifier for distinguishing + * simultaneous events. The name and cookie used to begin an event must be + * used to end it. + */ +#define SYSTRACE_ASYNC_BEGIN(name, cookie) \ + ___trctx.asyncBegin(SYSTRACE_TAG, name, cookie) + +/** + * Trace the end of an asynchronous event. + * This should have a corresponding SYSTRACE_ASYNC_BEGIN. + */ +#define SYSTRACE_ASYNC_END(name, cookie) \ + ___trctx.asyncEnd(SYSTRACE_TAG, name, cookie) + +/** + * Traces an integer counter value. name is used to identify the counter. + * This can be used to track how a value changes over time. + */ #define SYSTRACE_VALUE32(name, val) \ - TRACE_COUNTER(UTILS_PERFETTO_CATEGORY, name, val) + ___trctx.value(SYSTRACE_TAG, name, int32_t(val)) #define SYSTRACE_VALUE64(name, val) \ - TRACE_COUNTER(UTILS_PERFETTO_CATEGORY, name, val) + ___trctx.value(SYSTRACE_TAG, name, int64_t(val)) -#endif // SYSTRACE_TAG == SYSTRACE_TAG_DISABLED +// ------------------------------------------------------------------------------------------------ +// No user serviceable code below... +// ------------------------------------------------------------------------------------------------ + +namespace utils { +namespace details { + +class UTILS_PUBLIC Systrace { + public: + + enum tags { + NEVER = SYSTRACE_TAG_NEVER, + ALWAYS = SYSTRACE_TAG_ALWAYS, + FILAMENT = SYSTRACE_TAG_FILAMENT, + JOBSYSTEM = SYSTRACE_TAG_JOBSYSTEM + // we could define more TAGS here, as we need them. + }; + + explicit Systrace(uint32_t tag) noexcept { + if (tag) init(tag); + } + + static void enable(uint32_t tags) noexcept; + static void disable(uint32_t tags) noexcept; + + + inline void traceBegin(uint32_t tag, const char* name) noexcept { + if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { + beginSection(this, name); + } + } + + inline void traceEnd(uint32_t tag) noexcept { + if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { + endSection(this); + } + } + + inline void asyncBegin(uint32_t tag, const char* name, int32_t cookie) noexcept { + if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { + beginAsyncSection(this, name, cookie); + } + } + + inline void asyncEnd(uint32_t tag, const char* name, int32_t cookie) noexcept { + if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { + endAsyncSection(this, name, cookie); + } + } + + inline void value(uint32_t tag, const char* name, int32_t value) noexcept { + if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { + setCounter(this, name, value); + } + } + + inline void value(uint32_t tag, const char* name, int64_t value) noexcept { + if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { + setCounter(this, name, value); + } + } + + private: + friend class ScopedTrace; + + // whether tracing is supported at all by the platform + + using ATrace_isEnabled_t = bool (*)(); + using ATrace_beginSection_t = void (*)(const char* sectionName); + using ATrace_endSection_t = void (*)(); + using ATrace_beginAsyncSection_t = void (*)(const char* sectionName, int32_t cookie); + using ATrace_endAsyncSection_t = void (*)(const char* sectionName, int32_t cookie); + using ATrace_setCounter_t = void (*)(const char* counterName, int64_t counterValue); + + struct GlobalState { + bool isTracingAvailable; + std::atomic isTracingEnabled; + int markerFd; + + ATrace_isEnabled_t ATrace_isEnabled; + ATrace_beginSection_t ATrace_beginSection; + ATrace_endSection_t ATrace_endSection; + ATrace_beginAsyncSection_t ATrace_beginAsyncSection; + ATrace_endAsyncSection_t ATrace_endAsyncSection; + ATrace_setCounter_t ATrace_setCounter; + + void (*beginSection)(Systrace* that, const char* name); + void (*endSection)(Systrace* that); + void (*beginAsyncSection)(Systrace* that, const char* name, int32_t cookie); + void (*endAsyncSection)(Systrace* that, const char* name, int32_t cookie); + void (*setCounter)(Systrace* that, const char* name, int64_t value); + }; + + static GlobalState sGlobalState; + + + // per-instance versions for better performance + ATrace_isEnabled_t ATrace_isEnabled; + ATrace_beginSection_t ATrace_beginSection; + ATrace_endSection_t ATrace_endSection; + ATrace_beginAsyncSection_t ATrace_beginAsyncSection; + ATrace_endAsyncSection_t ATrace_endAsyncSection; + ATrace_setCounter_t ATrace_setCounter; + + void (*beginSection)(Systrace* that, const char* name); + void (*endSection)(Systrace* that); + void (*beginAsyncSection)(Systrace* that, const char* name, int32_t cookie); + void (*endAsyncSection)(Systrace* that, const char* name, int32_t cookie); + void (*setCounter)(Systrace* that, const char* name, int64_t value); + + void init(uint32_t tag) noexcept; + + // cached values for faster access, no need to be initialized + bool mIsTracingEnabled; + int mMarkerFd = -1; + pid_t mPid; + + static void setup() noexcept; + static void init_once() noexcept; + static bool isTracingEnabled(uint32_t tag) noexcept; + + static void begin_body(int fd, int pid, const char* name) noexcept; + static void end_body(int fd, int pid) noexcept; + static void async_begin_body(int fd, int pid, const char* name, int32_t cookie) noexcept; + static void async_end_body(int fd, int pid, const char* name, int32_t cookie) noexcept; + static void int64_body(int fd, int pid, const char* name, int64_t value) noexcept; +}; + +// ------------------------------------------------------------------------------------------------ + +class UTILS_PUBLIC ScopedTrace { +public: + // we don't inline this because it's relatively heavy due to a global check + ScopedTrace(uint32_t tag, const char* name) noexcept: mTrace(tag), mTag(tag) { + mTrace.traceBegin(tag, name); + } + + inline ~ScopedTrace() noexcept { + mTrace.traceEnd(mTag); + } + +private: + Systrace mTrace; + const uint32_t mTag; +}; + +} // namespace details +} // namespace utils #endif // TNT_UTILS_ANDROID_SYSTRACE_H diff --git a/libs/utils/include/utils/darwin/Systrace.h b/libs/utils/include/utils/darwin/Systrace.h index 33de07962d..e1521cf705 100644 --- a/libs/utils/include/utils/darwin/Systrace.h +++ b/libs/utils/include/utils/darwin/Systrace.h @@ -29,25 +29,13 @@ #include #include -#if SYSTRACE_TAG == SYSTRACE_TAG_DISABLED - -#define SYSTRACE_ENABLE() -#define SYSTRACE_CONTEXT() -#define SYSTRACE_NAME(name) -#define SYSTRACE_FRAME_ID(frame) -#define SYSTRACE_NAME_BEGIN(name) -#define SYSTRACE_NAME_END() -#define SYSTRACE_CALL() -#define SYSTRACE_ASYNC_BEGIN(name, cookie) -#define SYSTRACE_ASYNC_END(name, cookie) -#define SYSTRACE_VALUE32(name, val) -#define SYSTRACE_VALUE64(name, val) - -#else - // enable tracing #define SYSTRACE_ENABLE() ::utils::details::Systrace::enable(SYSTRACE_TAG) +// disable tracing +#define SYSTRACE_DISABLE() ::utils::details::Systrace::disable(SYSTRACE_TAG) + + /** * Creates a Systrace context in the current scope. needed for calling all other systrace * commands below. @@ -105,8 +93,6 @@ extern thread_local std::stack ___tracerSections; #define SYSTRACE_VALUE64(name, val) \ ___tracer.value(SYSTRACE_TAG, name, int64_t(val)) -#endif // SYSTRACE_TAG == SYSTRACE_TAG_DISABLED - // ------------------------------------------------------------------------------------------------ // No user serviceable code below... // ------------------------------------------------------------------------------------------------ @@ -132,40 +118,50 @@ namespace utils { namespace details { class Systrace { -public: + public: + + enum tags { + NEVER = SYSTRACE_TAG_NEVER, + ALWAYS = SYSTRACE_TAG_ALWAYS, + FILAMENT = SYSTRACE_TAG_FILAMENT, + JOBSYSTEM = SYSTRACE_TAG_JOBSYSTEM + // we could define more TAGS here, as we need them. + }; + explicit Systrace(uint32_t tag) noexcept { if (tag) init(tag); } - static void enable(uint32_t tag) noexcept; + static void enable(uint32_t tags) noexcept; + static void disable(uint32_t tags) noexcept; - void traceBegin(uint32_t tag, const char* name) noexcept { + inline void traceBegin(uint32_t tag, const char* name) noexcept { if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { APPLE_SIGNPOST_EMIT(sGlobalState.systraceLog, OS_SIGNPOST_INTERVAL_BEGIN, OS_SIGNPOST_ID_EXCLUSIVE, name, name) } } - void traceEnd(uint32_t tag, const char* name) noexcept { + inline void traceEnd(uint32_t tag, const char* name) noexcept { if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { APPLE_SIGNPOST_EMIT(sGlobalState.systraceLog, OS_SIGNPOST_INTERVAL_END, OS_SIGNPOST_ID_EXCLUSIVE, name, "") } } - void asyncBegin(uint32_t tag, const char* name, int32_t cookie) noexcept { + inline void asyncBegin(uint32_t tag, const char* name, int32_t cookie) noexcept { if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { // TODO } } - void asyncEnd(uint32_t tag, const char* name, int32_t cookie) noexcept { + inline void asyncEnd(uint32_t tag, const char* name, int32_t cookie) noexcept { if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { // TODO } } - void value(uint32_t tag, const char* name, int32_t value) noexcept { + inline void value(uint32_t tag, const char* name, int32_t value) noexcept { if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { char buf[64]; snprintf(buf, 64, "%s - %d", name, value); @@ -174,7 +170,7 @@ public: } } - void value(uint32_t tag, const char* name, int64_t value) noexcept { + inline void value(uint32_t tag, const char* name, int64_t value) noexcept { if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { char buf[64]; snprintf(buf, 64, "%s - %lld", name, value); @@ -183,16 +179,16 @@ public: } } - void frameId(uint32_t tag, uint32_t frame) noexcept { + inline void frameId(uint32_t tag, uint32_t frame) noexcept { if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { - char buf[64]; - snprintf(buf, 64, "frame %u", frame); + char buf[64]; \ + snprintf(buf, 64, "frame %u", frame); \ APPLE_SIGNPOST_EMIT(sGlobalState.frameIdLog, OS_SIGNPOST_EVENT, OS_SIGNPOST_ID_EXCLUSIVE, "frame", buf) } } -private: + private: friend class ScopedTrace; struct GlobalState { @@ -217,25 +213,25 @@ private: // ------------------------------------------------------------------------------------------------ class ScopedTrace { -public: + public: // we don't inline this because it's relatively heavy due to a global check ScopedTrace(uint32_t tag, const char* name) noexcept : mTrace(tag), mName(name), mTag(tag) { mTrace.traceBegin(tag, name); } - ~ScopedTrace() noexcept { + inline ~ScopedTrace() noexcept { mTrace.traceEnd(mTag, mName); } - void value(uint32_t tag, const char* name, int32_t v) noexcept { + inline void value(uint32_t tag, const char* name, int32_t v) noexcept { mTrace.value(tag, name, v); } - void value(uint32_t tag, const char* name, int64_t v) noexcept { + inline void value(uint32_t tag, const char* name, int64_t v) noexcept { mTrace.value(tag, name, v); } -private: + private: Systrace mTrace; const char* mName; const uint32_t mTag; diff --git a/libs/utils/src/JobSystem.cpp b/libs/utils/src/JobSystem.cpp index 2651927dea..ae8bba5443 100644 --- a/libs/utils/src/JobSystem.cpp +++ b/libs/utils/src/JobSystem.cpp @@ -15,8 +15,10 @@ */ // Note: The overhead of SYSTRACE_TAG_JOBSYSTEM is not negligible especially with parallel_for(). -#define SYSTRACE_TAG SYSTRACE_TAG_DISABLED +#ifndef SYSTRACE_TAG //#define SYSTRACE_TAG SYSTRACE_TAG_JOBSYSTEM +#define SYSTRACE_TAG SYSTRACE_TAG_NEVER +#endif // when SYSTRACE_TAG_JOBSYSTEM is used, enables even heavier systraces #define HEAVY_SYSTRACE 0 diff --git a/libs/utils/src/android/Systrace.cpp b/libs/utils/src/android/Systrace.cpp index 3528312961..0b03d4951d 100644 --- a/libs/utils/src/android/Systrace.cpp +++ b/libs/utils/src/android/Systrace.cpp @@ -14,26 +14,206 @@ * limitations under the License. */ -#include -#include +#include +#include -#include +#include -PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE(systrace); +#include -namespace { +#include +#include +#include +#include -class SystraceStaticInitialization { -public: - SystraceStaticInitialization() { - perfetto::TracingInitArgs args; - args.backends |= perfetto::kSystemBackend; - perfetto::Tracing::Initialize(args); - systrace::TrackEvent::Register(); - } -}; +namespace utils { +namespace details { -UTILS_UNUSED SystraceStaticInitialization sSystraceStaticInitialization{}; +static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT; +template +static void loadSymbol(T*& pfn, const char *symbol) noexcept { + pfn = (T*)dlsym(RTLD_DEFAULT, symbol); } +Systrace::GlobalState Systrace::sGlobalState = {}; + +void Systrace::init_once() noexcept { + GlobalState& s = sGlobalState; + + s.markerFd = -1; + + // API 23 + loadSymbol(s.ATrace_isEnabled, "ATrace_isEnabled"); + loadSymbol(s.ATrace_beginSection, "ATrace_beginSection"); + loadSymbol(s.ATrace_endSection, "ATrace_endSection"); + // API 29 + loadSymbol(s.ATrace_beginAsyncSection, "ATrace_beginAsyncSection"); + loadSymbol(s.ATrace_endAsyncSection, "ATrace_endAsyncSection"); + loadSymbol(s.ATrace_setCounter, "ATrace_setCounter"); + + + const bool hasBasicAtrace = s.ATrace_isEnabled && + s.ATrace_beginSection && + s.ATrace_endSection; + + const bool hasFullATrace = hasBasicAtrace && + s.ATrace_beginAsyncSection && + s.ATrace_endAsyncSection && + s.ATrace_setCounter; + + if (!hasFullATrace) { + s.markerFd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC); + } + + if (hasBasicAtrace && !hasFullATrace) { + // no-op if we don't have all these + s.ATrace_beginAsyncSection = [](const char* sectionName, int32_t cookie){}; + s.ATrace_endAsyncSection = [](const char* sectionName, int32_t cookie){}; + s.ATrace_setCounter = [](const char* sectionName, int64_t counterValue){}; + } + + const bool hasLegacySystrace = s.markerFd != -1; + + if (hasLegacySystrace && !hasFullATrace) { + // use legacy + s.beginSection = [](Systrace* that, const char* name) { + begin_body(that->mMarkerFd, that->mPid, name); + }; + s.endSection = [](Systrace* that) { + end_body(that->mMarkerFd, that->mPid); + }; + s.beginAsyncSection = [](Systrace* that, const char* name, int32_t cookie) { + async_begin_body(that->mMarkerFd, that->mPid, name, cookie); + }; + s.endAsyncSection = [](Systrace* that, const char* name, int32_t cookie) { + async_end_body(that->mMarkerFd, that->mPid, name, cookie); + }; + s.setCounter = [](Systrace* that, const char* name, int64_t value) { + int64_body(that->mMarkerFd, that->mPid, name, value); + }; + } else if (hasBasicAtrace) { + // we have at least basic ATrace + s.beginSection = [](Systrace* that, const char* name) { + that->ATrace_beginSection(name); + }; + s.endSection = [](Systrace* that) { + that->ATrace_endSection(); + }; + s.beginAsyncSection = [](Systrace* that, const char* name, int32_t cookie) { + that->ATrace_beginAsyncSection(name, cookie); + }; + s.endAsyncSection = [](Systrace* that, const char* name, int32_t cookie) { + that->ATrace_endAsyncSection(name, cookie); + }; + s.setCounter = [](Systrace* that, const char* name, int64_t value) { + that->ATrace_setCounter(name, value); + }; + } + + s.isTracingAvailable = hasLegacySystrace || hasFullATrace || hasBasicAtrace; +} + +void Systrace::setup() noexcept { + pthread_once(&atrace_once_control, init_once); +} + +void Systrace::enable(uint32_t tags) noexcept { + setup(); + if (UTILS_LIKELY(sGlobalState.isTracingAvailable)) { + sGlobalState.isTracingEnabled.fetch_or(tags, std::memory_order_relaxed); + } +} + +void Systrace::disable(uint32_t tags) noexcept { + sGlobalState.isTracingEnabled.fetch_and(~tags, std::memory_order_relaxed); +} + +// unfortunately, this generates quite a bit of code because reading a global is not +// trivial. For this reason, we do not inline this method. +bool Systrace::isTracingEnabled(uint32_t tag) noexcept { + if (tag) { + setup(); + return bool((sGlobalState.isTracingEnabled.load(std::memory_order_relaxed) | SYSTRACE_TAG_ALWAYS) & tag); + } + return false; +} + +// ------------------------------------------------------------------------------------------------ + +void Systrace::init(uint32_t tag) noexcept { + // must be called first + mIsTracingEnabled = isTracingEnabled(tag); + + // cache static variables for better efficiency + GlobalState& s = sGlobalState; + ATrace_isEnabled = s.ATrace_isEnabled; + ATrace_beginSection = s.ATrace_beginSection; + ATrace_endSection = s.ATrace_endSection; + ATrace_beginAsyncSection = s.ATrace_beginAsyncSection; + ATrace_endAsyncSection = s.ATrace_endAsyncSection; + ATrace_setCounter = s.ATrace_setCounter; + + beginSection = s.beginSection; + endSection = s.endSection; + beginAsyncSection = s.beginAsyncSection; + endAsyncSection = s.endAsyncSection; + setCounter = s.setCounter; + + mMarkerFd = s.markerFd; + + mPid = getpid(); +} + +// ------------------------------------------------------------------------------------------------ + +/** + * Maximum size of a message that can be logged to the trace buffer. + * Note this message includes a tag, the pid, and the string given as the name. + * Names should be kept short to get the most use of the trace buffer. + */ +#define ATRACE_MESSAGE_LENGTH 512 + +#define WRITE_MSG(format_begin, format_end, pid, name, value) { \ + char buf[ATRACE_MESSAGE_LENGTH]; \ + int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \ + name, value); \ + if (len >= (int) sizeof(buf)) { \ + /* Given the sizeof(buf), and all of the current format buffers, \ + * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \ + int name_len = strlen(name) - (len - sizeof(buf)) - 1; \ + /* Truncate the name to make the message fit. */ \ + len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \ + name_len, name, value); \ + } \ + write(fd, buf, len); \ +} + +void Systrace::begin_body(int fd, int pid, const char* name) noexcept { + char buf[ATRACE_MESSAGE_LENGTH]; + ssize_t len = snprintf(buf, sizeof(buf), "B|%d|%s", pid, name); + if (len >= sizeof(buf)) { + len = sizeof(buf) - 1; + } + write(fd, buf, size_t(len)); +} + +void Systrace::end_body(int fd, int pid) noexcept { + const char END_TAG = 'E'; + write(fd, &END_TAG, 1); +} + +void Systrace::async_begin_body(int fd, int pid, const char* name, int32_t cookie) noexcept { + WRITE_MSG("S|%d|", "|%" PRId32, pid, name, cookie); +} + +void Systrace::async_end_body(int fd, int pid, const char* name, int32_t cookie) noexcept { + WRITE_MSG("F|%d|", "|%" PRId32, pid, name, cookie); +} + +void Systrace::int64_body(int fd, int pid, const char* name, int64_t value) noexcept { + WRITE_MSG("C|%d|", "|%" PRId64, pid, name, value); +} + +} // namespace details +} // namespace utils diff --git a/libs/utils/src/darwin/Systrace.cpp b/libs/utils/src/darwin/Systrace.cpp index 759ca25f83..523b7ebd02 100644 --- a/libs/utils/src/darwin/Systrace.cpp +++ b/libs/utils/src/darwin/Systrace.cpp @@ -14,17 +14,11 @@ * limitations under the License. */ -#include - -#ifndef FILAMENT_APPLE_SYSTRACE -# define FILAMENT_APPLE_SYSTRACE 0 -#endif +#include +#include #if FILAMENT_APPLE_SYSTRACE -#include -#include -#include #include static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT; @@ -47,24 +41,21 @@ void Systrace::setup() noexcept { pthread_once(&atrace_once_control, init_once); } -void Systrace::enable(uint32_t tag) noexcept { +void Systrace::enable(uint32_t tags) noexcept { setup(); - uint32_t const mask = 1 << tag; - sGlobalState.isTracingEnabled.fetch_or(mask, std::memory_order_relaxed); + sGlobalState.isTracingEnabled.fetch_or(tags, std::memory_order_relaxed); } -void Systrace::disable(uint32_t tag) noexcept { - uint32_t const mask = 1 << tag; - sGlobalState.isTracingEnabled.fetch_and(~mask, std::memory_order_relaxed); +void Systrace::disable(uint32_t tags) noexcept { + sGlobalState.isTracingEnabled.fetch_and(~tags, std::memory_order_relaxed); } -// Unfortunately, this generates quite a bit of code because reading a global is not +// unfortunately, this generates quite a bit of code because reading a global is not // trivial. For this reason, we do not inline this method. bool Systrace::isTracingEnabled(uint32_t tag) noexcept { if (tag) { setup(); - uint32_t const mask = 1 << tag; - return bool(sGlobalState.isTracingEnabled.load(std::memory_order_relaxed) & mask); + return bool((sGlobalState.isTracingEnabled.load(std::memory_order_relaxed) | SYSTRACE_TAG_ALWAYS) & tag); } return false; }