Skip to content

Commit 7a1b7f4

Browse files
tarun292facebook-github-bot
authored andcommitted
Add support for delegate profiling in event_tracer (#408)
Summary: Pull Request resolved: #408 This diff should most probably be the final event_tracer diff that adds the user-facing API's for delegate profiling. `event_tracer_hooks_delegate.h` can be included by delegate authors in their backend runtime code to get access to these interfaces that will enable them to do profiling logging currently and debugging logging in the future. - Made changes to `event_tracer.h` to add the hooks to the `EventTracer` class. - Added `event_tracer_hooks_delegate.h` that contains the public interfaces that delegate authors can call into. Reviewed By: dbort Differential Revision: D49360145 fbshipit-source-id: fef2800b2129d93098b8d3868da9007cbc100f4f
1 parent 68cb851 commit 7a1b7f4

File tree

5 files changed

+308
-3
lines changed

5 files changed

+308
-3
lines changed

runtime/core/event_tracer.h

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,19 @@ typedef uint32_t DebugHandle;
2828
constexpr ChainID kUnsetChainId = -1;
2929
constexpr DebugHandle kUnsetDebugHandle = 0;
3030

31+
/// Different types of delegate debug identifiers that are supported currently.
32+
enum class DelegateDebugIdType {
33+
/// Default value, indicates that it's not a delegate event.
34+
kNone,
35+
/// Indicates a delegate event logged using an integer delegate debug
36+
/// identifier.
37+
kInt,
38+
/// Indicates a delegate event logged using a string delegate debug
39+
/// identifier i.e. the delegate debug id is a pointer to a string table
40+
/// managed by the class implementing EventTracer functionality.
41+
kStr
42+
};
43+
3144
/**
3245
* This is the struct which should be returned when a profiling event is
3346
* started. This is used to uniquely identify that profiling event and will be
@@ -44,8 +57,16 @@ struct EventTracerEntry {
4457
DebugHandle debug_handle;
4558
/// The time at which this event was started to be tracked.
4659
et_timestamp_t start_time;
60+
/// When delegate_event_id_type != DelegateDebugIdType::kNone it indicates
61+
/// that event_id represents a delegate event. If delegate_event_id_type is:
62+
/// 1) kInt then event_id contains an integer delegate debug id.
63+
/// 2) kStr then event_id contains a string table index into a string table
64+
/// maintained by the class implementing EventTracer functionality that will
65+
/// give us the string identifier of this delegate event. For more details
66+
/// refer to the DelegateMappingBuilder library present in
67+
/// executorch/exir/backend/utils.py.
68+
DelegateDebugIdType delegate_event_id_type;
4769
};
48-
4970
/**
5071
* EventTracer is a class that users can inherit and implement to
5172
* log/serialize/stream etc. the profiling and debugging events that are
@@ -79,8 +100,9 @@ class EventTracer {
79100
* around. The string must be copied over into internal memory during this
80101
* call.
81102
* @param[in] chain_id The id of the chain to which this event belongs to. If
82-
* -1 is passed in the chain_id and debug_handle stored in the class
83-
* internally will be used.
103+
* kUnsetChainId is passed in the chain_id and kUnsetDebugHandle for
104+
* debug_handle then the values stored in the class internally for these
105+
* properties will be used.
84106
* @param[in] debug_handle Debug handle generated ahead-of-time during model
85107
* compilation.
86108
*
@@ -92,6 +114,73 @@ class EventTracer {
92114
ChainID chain_id = kUnsetChainId,
93115
DebugHandle debug_handle = kUnsetDebugHandle) = 0;
94116

117+
/**
118+
* Start the profiling of a delegate event. Similar to start_profiling it will
119+
* return an instance of EventTracerEntry that contains the details of this
120+
* event.
121+
*
122+
* @param[in] name Human readable name for the delegate event. This name has
123+
* to be the same name that was passed in during the Debug delegate mapping
124+
* generation in the export/ahead-of-time process. If indices and not names
125+
* are used by this delegate to identify ops executed in the backend then
126+
* nullptr can be passed in. Users calling this interface do not need to keep
127+
* the memory pointed to by this pointer around. The string must be copied
128+
* over into internal memory during this call.
129+
* @param[in] delegate_debug_index The id of the delegate event. If string
130+
* based names are used by this delegate to identify ops executed in the
131+
* backend then kUnsetDebugHandle should be passed in here.
132+
*/
133+
virtual EventTracerEntry start_profiling_delegate(
134+
const char* name,
135+
DebugHandle delegate_debug_index) = 0;
136+
137+
/**
138+
* Signal the end of the delegate profiling event contained in
139+
* event_tracer_entry. Users also have the option to log some some free-from
140+
* string based metadata along with this.
141+
*
142+
* @param[in] event_tracer_entry The EventTracerEntry returned by a call to
143+
* start_profiling_delegate().
144+
* @param[in] metadata Optional free-form metadata associated with the
145+
* delegate event. This should be a null terminated ASCII string. Users
146+
* calling this interface do not need to keep the memory pointed to by this
147+
* pointer around. The string must be copied over into internal memory during
148+
* this call.
149+
*/
150+
virtual void end_profiling_delegate(
151+
EventTracerEntry event_tracer_entry,
152+
const char* metadata = nullptr) = 0;
153+
154+
/**
155+
* Some delegates get access to the profiling details only after the complete
156+
* graph has been executed. This interface is to support such use cases. It
157+
* can be called in a loop etc. to log any number of profiling events that are
158+
* part of this delegate.
159+
*
160+
* @param[in] name Human readable name for the delegate event. This name has
161+
* to be the same name that was passed in during the Debug delegate mapping
162+
* generation in the export/ahead-of-time process. If indices and not names
163+
* are used by this delegate to identify ops executed in the backend then
164+
* nullptr can be passed in. Users calling this interface do not need to keep
165+
* the memory pointed to by this pointer around. The string must be copied
166+
* over into internal memory during this call.
167+
* @param[in] delegate_debug_index The id of the delegate event. If string
168+
* based names are used by this delegate to identify ops executed in the
169+
* backend then kUnsetDebugHandle should be passed in here.
170+
* @param[in] start_time The timestamp when the delegate event started.
171+
* @param[in] end_time The timestamp when the delegate event finished.
172+
* @param[in] metadata Optional data relevant to the execution that the user
173+
* wants to log along with this event. Pointer to metadata doesn't need to be
174+
* valid after the call to this function. This should be a null terminated
175+
* ASCII string.
176+
*/
177+
virtual void log_profiling_delegate(
178+
const char* name,
179+
DebugHandle delegate_debug_index,
180+
et_timestamp_t start_time,
181+
et_timestamp_t end_time,
182+
const char* metadata = nullptr) = 0;
183+
95184
/**
96185
* End the profiling of the event identified by prof_entry
97186
*
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#pragma once
10+
11+
#include <executorch/runtime/core/event_tracer.h>
12+
13+
/**
14+
* @file
15+
*
16+
* This file contains the hooks that can be used by runtime delegate backend
17+
* authors to log profiling and debugging events from backend code. In order to
18+
* use these hooks delegate authors would have needed to generate a delegate
19+
* debug identifier mapping using the DelegateMappingBuilder library present in
20+
* executorch/exir/backend/utils.py. The delegate debug identifiers generated by
21+
* that library are the ones that need to be passed to these hooks to log
22+
* events. Using any other identifiers will cause post-processing of the events
23+
* data to not properly link back to the nodes in the original lowered graph.
24+
*
25+
* The benefit of defining these hooks is that we can easily control whether or
26+
* not we want to compile in the EventTracer code based on the status of the
27+
* ET_EVENT_TRACER_ENABLED flag.
28+
*/
29+
30+
namespace torch {
31+
namespace executor {
32+
33+
/**
34+
* Start the profiling of a delegate event. Similar to start_profiling it will
35+
* return an instance of EventTracerEntry that contains the details of this
36+
* event. Can be left in production code as these hooks compile conditionally.
37+
*
38+
* @param[in] event_tracer The event tracer instance that is doing the logging.
39+
* @param[in] name Human readable name for the delegate event. This name has
40+
* to be the same name that was passed in during the Debug delegate mapping
41+
* generation in the export/ahead-of-time process. If indices and not names
42+
* are used by this delegate to identify ops executed in the backend then
43+
* nullptr can be passed in. Users calling this interface do not need to keep
44+
* the memory pointed to by this pointer around. The string must be copied over
45+
* into internal memory during this call.
46+
* @param[in] delegate_debug_id The id of the delegate event. If string
47+
* based names are used by this delegate to identify ops executed in the
48+
* backend then kUnsetDebugHandle should be passed in here.
49+
*/
50+
inline EventTracerEntry event_tracer_start_profiling_delegate(
51+
EventTracer* event_tracer,
52+
const char* name,
53+
DebugHandle delegate_debug_id) {
54+
#ifdef ET_EVENT_TRACER_ENABLED
55+
if (event_tracer) {
56+
return event_tracer->start_profiling_delegate(name, delegate_debug_id);
57+
}
58+
#else //! ET_EVENT_TRACER_ENABLED
59+
(void)name;
60+
(void)delegate_debug_id;
61+
#endif
62+
// There is no active tracer; this value will be ignored.
63+
return EventTracerEntry();
64+
}
65+
/**
66+
* Signal the end of the delegate profiling event contained in
67+
* event_tracer_entry. Users also have the option to log some some free-from
68+
* string based metadata along with this. Can be left in production code as
69+
* these hooks compile conditionally.
70+
*
71+
* @param[in] event_tracer The event tracer instance that is doing the logging.
72+
* @param[in] event_tracer_entry The EventTracerEntry returned by a call to
73+
* start_profiling_delegate().
74+
* @param[in] metadata Free-form metadata associated with the delegate event.
75+
* Users calling this interface do not need to keep the memory pointed to by
76+
* this pointer around. The string must be copied over into internal memory
77+
* during this call. The input string must also be null terminated.
78+
*/
79+
inline void event_tracer_end_profiling_delegate(
80+
EventTracer* event_tracer,
81+
EventTracerEntry event_tracer_entry,
82+
const char* metadata = nullptr) {
83+
#ifdef ET_EVENT_TRACER_ENABLED
84+
if (event_tracer) {
85+
event_tracer->end_profiling_delegate(event_tracer_entry, metadata);
86+
}
87+
#else //! ET_EVENT_TRACER_ENABLED
88+
(void)event_tracer_entry;
89+
(void)metadata;
90+
#endif
91+
}
92+
93+
/**
94+
* Some delegates get access to the profiling details only after the complete
95+
* graph has been executed. This interface is to support such use cases. It
96+
* can be called in a loop etc. to log any number of profiling events that are
97+
* part of this delegate. Can be left in production code as these hooks
98+
* compile conditionally.
99+
*
100+
* @param[in] event_tracer The event tracer instance that is doing the logging.
101+
* @param[in] name Human readable name for the delegate event. This name has
102+
* to be the same name that was passed in during the Debug delegate mapping
103+
* generation in the export/ahead-of-time process. If indices and not names
104+
* are used by this delegate to identify ops executed in the backend then
105+
* nullptr can be passed in. Users calling this interface do not need to keep
106+
* the memory pointed to by this pointer around. The string must
107+
* be copied over into internal memory during this call.
108+
* @param[in] delegate_debug_id The id of the delegate event. If string
109+
* based names are used by this delegate to identify ops executed in the
110+
* backend then -1 should be passed in here.
111+
* @param[in] start_time The timestamp when the delegate event started.
112+
* @param[in] end_time The timestamp when the delegate event finished.
113+
* @param[in] metadata Any extra data relevant to the execution that the user
114+
* wants to log along with this event. Pointer to metadata doesn't need to be
115+
* valid after the call to this function. Users calling this interface do not
116+
* need to keep the memory pointed to by this pointer around. The string must
117+
* be copied over into internal memory during this call. The input string must
118+
* also be a null terminated.
119+
*/
120+
inline void event_tracer_log_profiling_delegate(
121+
EventTracer* event_tracer,
122+
const char* name,
123+
DebugHandle delegate_debug_id,
124+
et_timestamp_t start_time,
125+
et_timestamp_t end_time,
126+
const char* metadata = nullptr) {
127+
#ifdef ET_EVENT_TRACER_ENABLED
128+
if (event_tracer) {
129+
event_tracer->log_profiling_delegate(
130+
name, delegate_debug_id, start_time, end_time, metadata);
131+
}
132+
#else //! ET_EVENT_TRACER_ENABLED
133+
(void)name;
134+
(void)delegate_debug_id;
135+
(void)start_time;
136+
(void)end_time;
137+
(void)metadata;
138+
#endif
139+
}
140+
141+
} // namespace executor
142+
} // namespace torch

runtime/core/targets.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def define_common_targets():
2424
"error.h",
2525
"event_tracer.h",
2626
"event_tracer_hooks.h",
27+
"event_tracer_hooks_delegate.h",
2728
"freeable_buffer.h",
2829
"function_ref.h",
2930
"result.h",

runtime/core/test/event_tracer_test.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// Enable flag for test
1313
#define ET_EVENT_TRACER_ENABLED
1414
#include <executorch/runtime/core/event_tracer_hooks.h>
15+
#include <executorch/runtime/core/event_tracer_hooks_delegate.h>
1516

1617
namespace torch {
1718
namespace executor {
@@ -54,8 +55,39 @@ class DummyEventTracer : public EventTracer {
5455
(void)name;
5556
return 0;
5657
}
58+
59+
EventTracerEntry start_profiling_delegate(
60+
const char* name,
61+
DebugHandle delegate_debug_id) override {
62+
(void)name;
63+
(void)delegate_debug_id;
64+
return EventTracerEntry();
65+
}
66+
67+
void end_profiling_delegate(
68+
EventTracerEntry event_tracer_entry,
69+
const char* metadata) override {
70+
(void)event_tracer_entry;
71+
(void)metadata;
72+
}
73+
74+
void log_profiling_delegate(
75+
const char* name,
76+
DebugHandle delegate_debug_id,
77+
et_timestamp_t start_time,
78+
et_timestamp_t end_time,
79+
const char* metadata = nullptr) override {
80+
(void)name;
81+
(void)delegate_debug_id;
82+
(void)start_time;
83+
(void)end_time;
84+
(void)metadata;
85+
}
5786
};
5887

88+
/**
89+
* Exercise all the event_tracer API's for a basic sanity check.
90+
*/
5991
void RunSimpleTracerTest(EventTracer* event_tracer) {
6092
event_tracer_create_event_block(event_tracer, "ExampleEvent");
6193
event_tracer_create_event_block(event_tracer, "ExampleEvent");
@@ -86,6 +118,33 @@ TEST(TestEventTracer, SimpleEventTracerTest) {
86118
}
87119
}
88120

121+
/**
122+
* Exercise all the event_tracer API's for delegates as a basic sanity check.
123+
*/
124+
void RunSimpleTracerTestDelegate(EventTracer* event_tracer) {
125+
EventTracerEntry event_tracer_entry = event_tracer_start_profiling_delegate(
126+
event_tracer, "test_event", kUnsetDebugHandle);
127+
event_tracer_end_profiling_delegate(
128+
event_tracer, event_tracer_entry, nullptr);
129+
event_tracer_start_profiling_delegate(event_tracer, nullptr, 1);
130+
event_tracer_end_profiling_delegate(
131+
event_tracer, event_tracer_entry, "test_metadata");
132+
event_tracer_log_profiling_delegate(
133+
event_tracer, "test_event", kUnsetDebugHandle, 0, 1, nullptr);
134+
event_tracer_log_profiling_delegate(event_tracer, nullptr, 1, 0, 1, nullptr);
135+
}
136+
137+
TEST(TestEventTracer, SimpleEventTracerTestDelegate) {
138+
// Call all the EventTracer macro's with a valid pointer to an event tracer
139+
// and also with a null pointer (to test that the null case works).
140+
DummyEventTracer dummy;
141+
std::vector<DummyEventTracer*> dummy_event_tracer_arr = {&dummy, nullptr};
142+
for (size_t i = 0; i < dummy_event_tracer_arr.size(); i++) {
143+
RunSimpleTracerTestDelegate(&dummy);
144+
RunSimpleTracerTestDelegate(nullptr);
145+
}
146+
}
147+
89148
} // namespace executor
90149
} // namespace torch
91150
// TODO : (T163645377) Add more test coverage to log and verify events passed

sdk/etdump/etdump_flatcc.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,20 @@ class ETDumpGen : public EventTracer {
4545
virtual void end_profiling(EventTracerEntry prof_entry) override;
4646
virtual void track_allocation(AllocatorID id, size_t size) override;
4747
virtual AllocatorID track_allocator(const char* name) override;
48+
virtual EventTracerEntry start_profiling_delegate(
49+
const char* name,
50+
DebugHandle delegate_debug_index) override {
51+
return EventTracerEntry();
52+
};
53+
virtual void end_profiling_delegate(
54+
EventTracerEntry event_tracer_entry,
55+
const char* metadata = nullptr) override{};
56+
virtual void log_profiling_delegate(
57+
const char* name,
58+
DebugHandle delegate_debug_index,
59+
et_timestamp_t start_time,
60+
et_timestamp_t end_time,
61+
const char* metadata = nullptr) override{};
4862
etdump_result get_etdump_data();
4963
size_t get_num_blocks();
5064

0 commit comments

Comments
 (0)