Skip to content

Add macros and helper classes in event_tracer needed for profiling #294

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 185 additions & 0 deletions runtime/core/event_tracer_hooks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

#pragma once

#include <executorch/runtime/core/event_tracer.h>

/**
* @file
*
* This file contains the hooks that are inserted across various parts of the
* core runtime code to call into the EventTracer class for logging of profiling
* and debugging events. Any calls made to the EventTracer from the runtime must
* be made via these hooks.
* Users shouldn't directly add these hooks in their code and it's meant only
* for usage in ExecuTorch internal code.
*
* The benefit of defining these hooks is that we can easily control whether or
* not we want to compile in the EventTracer code based on the status of the
* ET_EVENT_TRACER_ENABLED flag.
*/

namespace torch {
namespace executor {
namespace internal {

/**
* This class enables scope based profiling where needed using RAII.
* Profiling will be started when the object is created and will end
* when the object goes out of scope.
*/
class EventTracerProfileScope final {
public:
EventTracerProfileScope(EventTracer* event_tracer, const char* name) {
event_tracer_ = event_tracer;
if (event_tracer_ == nullptr) {
return;
}
event_entry_ = event_tracer->start_profiling(name);
}

~EventTracerProfileScope() {
if (event_tracer_ == nullptr) {
return;
}
event_tracer_->end_profiling(event_entry_);
}

private:
EventTracer* event_tracer_;
EventTracerEntry event_entry_;
};

/**
* This class helps us set and then clear out the chain id and debug handle
* values stored in the event tracer class using RAII. This is typically called
* in the executor loop before entering the codegen layer to configure the chain
* id and debug handle of the current instruction being executed.
* After we return from the kernel execution we can then reset the chain id and
* debug handle to defaults when this object goes out of scope.
*/
class EventTracerProfileInstructionScope final {
public:
EventTracerProfileInstructionScope(
EventTracer* event_tracer,
ChainID chain_idx,
DebugHandle debug_handle) {
event_tracer_ = event_tracer;
if (event_tracer_ == nullptr) {
return;
}
event_tracer_->set_chain_debug_handle(chain_idx, debug_handle);
}

~EventTracerProfileInstructionScope() {
if (event_tracer_ == nullptr) {
return;
}
event_tracer_->set_chain_debug_handle(kUnsetChainId, kUnsetDebugHandle);
}

private:
EventTracer* event_tracer_;
};

/**
* Create a new event block with the specified name. Any events logged
* after this will be associated with this new event block.
*/
inline void event_tracer_create_event_block(
EventTracer* event_tracer,
char const* name) {
#ifdef ET_EVENT_TRACER_ENABLED
if (event_tracer) {
event_tracer->create_event_block(name);
}
#else //! ET_EVENT_TRACER_ENABLED
(void)event_tracer;
(void)name;
#endif
}

/**
* Explicitly mark the beginning of a new profiling event. This returns
* an instance of an EventTracerEntry object that the user needs to keep
* around and pass into the corresponding event_tracer_end_profiling_event
* call.
*/
inline EventTracerEntry event_tracer_begin_profiling_event(
EventTracer* event_tracer,
char const* name) {
EventTracerEntry event_tracer_entry;
#ifdef ET_EVENT_TRACER_ENABLED
if (event_tracer) {
event_tracer_entry = event_tracer->start_profiling(name);
}
#else //! ET_EVENT_TRACER_ENABLED
(void)event_tracer;
(void)name;
#endif
// There is no active tracer; this value will be ignored.
return event_tracer_entry;
}

/**
* Mark the end of a profiling event passing in the entry token
* returned by a previous call to ET_EVENT_TRACER_BEGIN_PROFILING_EVENT.
*/
inline void event_tracer_end_profiling_event(
EventTracer* event_tracer,
EventTracerEntry event) {
#ifdef ET_EVENT_TRACER_ENABLED
if (event_tracer) {
event_tracer->end_profiling(event);
}
#else //! ET_EVENT_TRACER_ENABLED
(void)event_tracer;
(void)event;
#endif
}

/**
* Start the tracking of the allocator represented by this name and returns
* an AllocatorID that will be used to track all subsequent allocations done by
* this allocator.
*/
inline AllocatorID event_tracer_track_allocator(
EventTracer* event_tracer,
const char* name) {
#ifdef ET_EVENT_TRACER_ENABLED
if (event_tracer) {
return event_tracer->track_allocator(name);
}
#else //! ET_EVENT_TRACER_ENABLED
(void)event_tracer;
(void)name;
#endif
// There is no active tracer; this value will be ignored.
return 0;
}

/// Log the allocation event done via the allocator represented by id.
inline void event_tracer_track_allocation(
EventTracer* event_tracer,
AllocatorID id,
size_t size) {
#ifdef ET_EVENT_TRACER_ENABLED
if (event_tracer) {
event_tracer->track_allocation(id, size);
}
#else //! ET_EVENT_TRACER_ENABLED
(void)event_tracer;
(void)id;
(void)size;
#endif
}

} // namespace internal
} // namespace executor
} // namespace torch
3 changes: 2 additions & 1 deletion runtime/core/targets.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def event_tracer_enabled():
def get_event_tracer_flags():
event_tracer_flags = []
if event_tracer_enabled():
event_tracer_flags += ["-DEVENT_TRACER_ENABLED"]
event_tracer_flags += ["-DET_EVENT_TRACER_ENABLED"]
return event_tracer_flags

def define_common_targets():
Expand All @@ -23,6 +23,7 @@ def define_common_targets():
"data_loader.h",
"error.h",
"event_tracer.h",
"event_tracer_hooks.h",
"freeable_buffer.h",
"function_ref.h",
"result.h",
Expand Down
92 changes: 92 additions & 0 deletions runtime/core/test/event_tracer_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

#include <gtest/gtest.h>

#include <executorch/runtime/core/event_tracer.h>
// Enable flag for test
#define ET_EVENT_TRACER_ENABLED
#include <executorch/runtime/core/event_tracer_hooks.h>

namespace torch {
namespace executor {

using namespace internal;

class DummyEventTracer : public EventTracer {
public:
DummyEventTracer() {}

~DummyEventTracer() override {}

void create_event_block(const char* name) override {
(void)name;
return;
}

EventTracerEntry start_profiling(
const char* name,
ChainID chain_id = kUnsetChainId,
DebugHandle debug_handle = kUnsetDebugHandle) override {
(void)name;
(void)chain_id;
(void)debug_handle;
return EventTracerEntry();
}

void end_profiling(EventTracerEntry prof_entry) override {
(void)prof_entry;
return;
}

void track_allocation(AllocatorID id, size_t size) override {
(void)id;
(void)size;
return;
}

AllocatorID track_allocator(const char* name) override {
(void)name;
return 0;
}
};

void RunSimpleTracerTest(EventTracer* event_tracer) {
event_tracer_create_event_block(event_tracer, "ExampleEvent");
event_tracer_create_event_block(event_tracer, "ExampleEvent");
EventTracerEntry event_entry =
event_tracer_begin_profiling_event(event_tracer, "ExampleEvent");
event_tracer_end_profiling_event(event_tracer, event_entry);
{
EventTracerProfileScope event_tracer_profile_scope(
event_tracer, "ExampleScope");
}
{
EventTracerProfileInstructionScope event_tracer_profile_instruction_scope(
event_tracer, 0, 1);
}
AllocatorID allocator_id =
event_tracer_track_allocator(event_tracer, "AllocatorName");
event_tracer_track_allocation(event_tracer, allocator_id, 64);
}

TEST(TestEventTracer, SimpleEventTracerTest) {
// Call all the EventTracer macro's with a valid pointer to an event tracer
// and also with a null pointer (to test that the null case works).
DummyEventTracer dummy;
std::vector<DummyEventTracer*> dummy_event_tracer_arr = {&dummy, nullptr};
for (size_t i = 0; i < dummy_event_tracer_arr.size(); i++) {
RunSimpleTracerTest(&dummy);
RunSimpleTracerTest(nullptr);
}
}

} // namespace executor
} // namespace torch
// TODO : (T163645377) Add more test coverage to log and verify events passed
// into DummyTracer.
10 changes: 10 additions & 0 deletions runtime/core/test/targets.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ def define_common_targets():
],
)

runtime.cxx_test(
name = "event_tracer_test",
srcs = [
"event_tracer_test.cpp",
],
deps = [
"//executorch/runtime/core:core",
],
)

runtime.cxx_test(
name = "freeable_buffer_test",
srcs = [
Expand Down