Skip to content

Commit 7021d51

Browse files
Olivia-liufacebook-github-bot
authored andcommitted
Intermediate output logging enablement - Runtime APIs (#3822)
Summary: Pull Request resolved: #3822 This diff creates and implements the APIs that log intermediate outputs from delegates into etdump file. Design doc: https://docs.google.com/document/d/1qGHsgd-roqtxPz4CrUlqGrKaAtbaf9bArziMuwHD0So/edit bypass-github-export-checks bypass-github-pytorch-ci-checks bypass-github-executorch-ci-checks Reviewed By: tarun292 Differential Revision: D56941407 fbshipit-source-id: df985bba0e7ada6917afed5ba305d2a43e49d06b
1 parent 7047162 commit 7021d51

File tree

7 files changed

+503
-0
lines changed

7 files changed

+503
-0
lines changed

runtime/core/event_tracer.h

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9+
#include <executorch/runtime/core/array_ref.h>
910
#include <executorch/runtime/core/evalue.h>
1011
#include <executorch/runtime/platform/platform.h>
1112
#include <stdlib.h>
@@ -256,6 +257,106 @@ class EventTracer {
256257
const EValue& evalue,
257258
LoggedEValueType evalue_type) = 0;
258259

260+
/**
261+
* Log an intermediate tensor output from a delegate.
262+
*
263+
* @param[in] name Human readable name for the delegate event. This name has
264+
* to be the same name that was passed in during the Debug delegate mapping
265+
* generation in the export/ahead-of-time process. If indices and not names
266+
* are used by this delegate to identify ops executed in the backend then
267+
* nullptr can be passed in. Users calling this interface do not need to keep
268+
* the memory pointed to by this pointer around. The string must be copied
269+
* over into internal memory during this call.
270+
* @param[in] delegate_debug_index The id of the delegate event. If string
271+
* based names are used by this delegate to identify ops executed in the
272+
* backend then kUnsetDebugHandle should be passed in here.
273+
* @param[in] output The tensor type output to be logged.
274+
*/
275+
virtual void log_intermediate_output_delegate(
276+
const char* name,
277+
DebugHandle delegate_debug_index,
278+
const Tensor& output) = 0;
279+
280+
/**
281+
* Log an intermediate tensor array output from a delegate.
282+
*
283+
* @param[in] name Human readable name for the delegate event. This name has
284+
* to be the same name that was passed in during the Debug delegate mapping
285+
* generation in the export/ahead-of-time process. If indices and not names
286+
* are used by this delegate to identify ops executed in the backend then
287+
* nullptr can be passed in. Users calling this interface do not need to keep
288+
* the memory pointed to by this pointer around. The string must be copied
289+
* over into internal memory during this call.
290+
* @param[in] delegate_debug_index The id of the delegate event. If string
291+
* based names are used by this delegate to identify ops executed in the
292+
* backend then kUnsetDebugHandle should be passed in here.
293+
* @param[in] output The tensor array type output to be logged.
294+
*/
295+
virtual void log_intermediate_output_delegate(
296+
const char* name,
297+
DebugHandle delegate_debug_index,
298+
const ArrayRef<Tensor> output) = 0;
299+
300+
/**
301+
* Log an intermediate int output from a delegate.
302+
*
303+
* @param[in] name Human readable name for the delegate event. This name has
304+
* to be the same name that was passed in during the Debug delegate mapping
305+
* generation in the export/ahead-of-time process. If indices and not names
306+
* are used by this delegate to identify ops executed in the backend then
307+
* nullptr can be passed in. Users calling this interface do not need to keep
308+
* the memory pointed to by this pointer around. The string must be copied
309+
* over into internal memory during this call.
310+
* @param[in] delegate_debug_index The id of the delegate event. If string
311+
* based names are used by this delegate to identify ops executed in the
312+
* backend then kUnsetDebugHandle should be passed in here.
313+
* @param[in] output The int type output to be logged.
314+
*/
315+
virtual void log_intermediate_output_delegate(
316+
const char* name,
317+
DebugHandle delegate_debug_index,
318+
const int& output) = 0;
319+
320+
/**
321+
* Log an intermediate bool output from a delegate.
322+
*
323+
* @param[in] name Human readable name for the delegate event. This name has
324+
* to be the same name that was passed in during the Debug delegate mapping
325+
* generation in the export/ahead-of-time process. If indices and not names
326+
* are used by this delegate to identify ops executed in the backend then
327+
* nullptr can be passed in. Users calling this interface do not need to keep
328+
* the memory pointed to by this pointer around. The string must be copied
329+
* over into internal memory during this call.
330+
* @param[in] delegate_debug_index The id of the delegate event. If string
331+
* based names are used by this delegate to identify ops executed in the
332+
* backend then kUnsetDebugHandle should be passed in here.
333+
* @param[in] output The bool type output to be logged.
334+
*/
335+
virtual void log_intermediate_output_delegate(
336+
const char* name,
337+
DebugHandle delegate_debug_index,
338+
const bool& output) = 0;
339+
340+
/**
341+
* Log an intermediate double output from a delegate.
342+
*
343+
* @param[in] name Human readable name for the delegate event. This name has
344+
* to be the same name that was passed in during the Debug delegate mapping
345+
* generation in the export/ahead-of-time process. If indices and not names
346+
* are used by this delegate to identify ops executed in the backend then
347+
* nullptr can be passed in. Users calling this interface do not need to keep
348+
* the memory pointed to by this pointer around. The string must be copied
349+
* over into internal memory during this call.
350+
* @param[in] delegate_debug_index The id of the delegate event. If string
351+
* based names are used by this delegate to identify ops executed in the
352+
* backend then kUnsetDebugHandle should be passed in here.
353+
* @param[in] output The double type output to be logged.
354+
*/
355+
virtual void log_intermediate_output_delegate(
356+
const char* name,
357+
DebugHandle delegate_debug_index,
358+
const double& output) = 0;
359+
259360
/**
260361
* Helper function to set the chain id ands debug handle. Users have two
261362
* options, the first is that they can directly pass in the chain id and debug

runtime/core/event_tracer_hooks_delegate.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,5 +145,47 @@ inline void event_tracer_log_profiling_delegate(
145145
#endif
146146
}
147147

148+
/**
149+
* This templated interfaces can be called in a loop etc. to log any number of
150+
* debug events that are part of this delegate. Supported values types are int,
151+
* bool, double, tensor and array of tensors. Can be left in production code as
152+
* these hooks compile conditionally.
153+
*
154+
* @param[in] event_tracer The event tracer instance that is doing the logging.
155+
* @param[in] name Human readable name for the delegate event. This name has
156+
* to be the same name that was passed in during the Debug delegate mapping
157+
* generation in the export/ahead-of-time process. If indices and not names
158+
* are used by this delegate to identify ops executed in the backend then
159+
* nullptr can be passed in. Users calling this interface do not need to keep
160+
* the memory pointed to by this pointer around. The string must
161+
* be copied over into internal memory during this call.
162+
* @param[in] delegate_debug_id The id of the delegate event. If string
163+
* based names are used by this delegate to identify ops executed in the
164+
* backend then -1 should be passed in here.
165+
* @param[in] output The output to be logged.
166+
*/
167+
template <typename T>
168+
inline void event_tracer_log_output_delegate(
169+
EventTracer* event_tracer,
170+
const char* name,
171+
DebugHandle delegate_debug_id,
172+
const T& output) {
173+
#ifdef ET_EVENT_TRACER_ENABLED
174+
if (event_tracer) {
175+
static_assert(
176+
std::is_same<T, int>::value || std::is_same<T, bool>::value ||
177+
std::is_same<T, double>::value || std::is_same<T, Tensor>::value ||
178+
std::is_same<T, ArrayRef<Tensor>>::value,
179+
"Unsupported type for intermediate output");
180+
event_tracer->log_intermediate_output_delegate(
181+
name, delegate_debug_id, output);
182+
}
183+
#else //! ET_EVENT_TRACER_ENABLED
184+
(void)name;
185+
(void)delegate_debug_id;
186+
(void)output;
187+
#endif
188+
}
189+
148190
} // namespace executor
149191
} // namespace torch

runtime/core/test/event_tracer_test.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,51 @@ class DummyEventTracer : public EventTracer {
8888
(void)metadata_len;
8989
}
9090

91+
void log_intermediate_output_delegate(
92+
const char* name,
93+
DebugHandle delegate_debug_index,
94+
const Tensor& output) override {
95+
(void)name;
96+
(void)delegate_debug_index;
97+
(void)output;
98+
}
99+
100+
void log_intermediate_output_delegate(
101+
const char* name,
102+
DebugHandle delegate_debug_index,
103+
const ArrayRef<Tensor> output) override {
104+
(void)name;
105+
(void)delegate_debug_index;
106+
(void)output;
107+
}
108+
109+
void log_intermediate_output_delegate(
110+
const char* name,
111+
DebugHandle delegate_debug_index,
112+
const int& output) override {
113+
(void)name;
114+
(void)delegate_debug_index;
115+
(void)output;
116+
}
117+
118+
virtual void log_intermediate_output_delegate(
119+
const char* name,
120+
DebugHandle delegate_debug_index,
121+
const bool& output) override {
122+
(void)name;
123+
(void)delegate_debug_index;
124+
(void)output;
125+
}
126+
127+
virtual void log_intermediate_output_delegate(
128+
const char* name,
129+
DebugHandle delegate_debug_index,
130+
const double& output) override {
131+
(void)name;
132+
(void)delegate_debug_index;
133+
(void)output;
134+
}
135+
91136
void log_evalue(const EValue& evalue, LoggedEValueType evalue_type) override {
92137
logged_evalue_ = evalue;
93138
logged_evalue_type_ = evalue_type;

sdk/etdump/etdump_flatcc.cpp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,124 @@ void ETDumpGen::log_profiling_delegate(
288288
etdump_RunData_events_push_end(builder);
289289
}
290290

291+
void ETDumpGen::log_intermediate_output_delegate(
292+
const char* name,
293+
DebugHandle delegate_debug_index,
294+
const Tensor& output) {
295+
log_intermediate_output_delegate_helper(name, delegate_debug_index, output);
296+
}
297+
298+
void ETDumpGen::log_intermediate_output_delegate(
299+
const char* name,
300+
DebugHandle delegate_debug_index,
301+
const ArrayRef<Tensor> output) {
302+
log_intermediate_output_delegate_helper(name, delegate_debug_index, output);
303+
}
304+
305+
void ETDumpGen::log_intermediate_output_delegate(
306+
const char* name,
307+
DebugHandle delegate_debug_index,
308+
const int& output) {
309+
log_intermediate_output_delegate_helper(name, delegate_debug_index, output);
310+
}
311+
312+
void ETDumpGen::log_intermediate_output_delegate(
313+
const char* name,
314+
DebugHandle delegate_debug_index,
315+
const bool& output) {
316+
log_intermediate_output_delegate_helper(name, delegate_debug_index, output);
317+
}
318+
319+
void ETDumpGen::log_intermediate_output_delegate(
320+
const char* name,
321+
DebugHandle delegate_debug_index,
322+
const double& output) {
323+
log_intermediate_output_delegate_helper(name, delegate_debug_index, output);
324+
}
325+
326+
template <typename T>
327+
void ETDumpGen::log_intermediate_output_delegate_helper(
328+
const char* name,
329+
DebugHandle delegate_debug_index,
330+
const T& output) {
331+
ET_CHECK_MSG(
332+
(name == nullptr) ^ (delegate_debug_index == -1),
333+
"Only name or delegate_debug_index can be valid. Check DelegateMappingBuilder documentation for more details.");
334+
if (debug_buffer.empty()) {
335+
ET_CHECK_MSG(0, "Must pre-set debug buffer with set_debug_buffer()\n");
336+
return;
337+
}
338+
339+
check_ready_to_add_events();
340+
int64_t string_id = name != nullptr ? create_string_entry(name) : -1;
341+
342+
etdump_DebugEvent_start(builder);
343+
344+
etdump_DebugEvent_chain_index_add(builder, chain_id_);
345+
etdump_DebugEvent_instruction_id_add(builder, debug_handle_);
346+
if (string_id == -1) {
347+
etdump_DebugEvent_delegate_debug_id_int_add(builder, delegate_debug_index);
348+
} else {
349+
etdump_DebugEvent_delegate_debug_id_str_add(builder, string_id);
350+
}
351+
352+
// Check the type of `output` then call the corresponding logging functions
353+
if constexpr (std::is_same<T, Tensor>::value) {
354+
long offset = copy_tensor_to_debug_buffer(output);
355+
etdump_Tensor_ref_t tensor_ref = add_tensor_entry(builder, output, offset);
356+
357+
etdump_Value_start(builder);
358+
etdump_Value_val_add(builder, etdump_ValueType_Tensor);
359+
etdump_Value_tensor_add(builder, tensor_ref);
360+
361+
} else if constexpr (std::is_same<T, ArrayRef<Tensor>>::value) {
362+
etdump_Tensor_vec_start(builder);
363+
for (size_t i = 0; i < output.size(); ++i) {
364+
long offset = copy_tensor_to_debug_buffer(output[i]);
365+
etdump_Tensor_vec_push(
366+
builder, add_tensor_entry(builder, output[i], offset));
367+
}
368+
etdump_Tensor_vec_ref_t tensor_vec_ref = etdump_Tensor_vec_end(builder);
369+
etdump_TensorList_ref_t tensor_list_ref =
370+
etdump_TensorList_create(builder, tensor_vec_ref);
371+
372+
etdump_Value_start(builder);
373+
etdump_Value_val_add(builder, etdump_ValueType_TensorList);
374+
etdump_Value_tensor_list_add(builder, tensor_list_ref);
375+
} else if constexpr (std::is_same<T, int>::value) {
376+
auto int_ref = etdump_Int_create(builder, output);
377+
378+
etdump_Value_start(builder);
379+
etdump_Value_val_add(builder, etdump_ValueType_Int);
380+
etdump_Value_int_value_add(builder, int_ref);
381+
} else if constexpr (std::is_same<T, double>::value) {
382+
auto double_ref = etdump_Double_create(builder, output);
383+
384+
etdump_Value_start(builder);
385+
etdump_Value_double_value_add(builder, double_ref);
386+
etdump_Value_val_add(builder, etdump_ValueType_Double);
387+
} else if constexpr (std::is_same<T, bool>::value) {
388+
flatbuffers_bool_t flatbuffer_bool_val =
389+
output ? FLATBUFFERS_TRUE : FLATBUFFERS_FALSE;
390+
auto bool_ref = etdump_Bool_create(builder, flatbuffer_bool_val);
391+
392+
etdump_Value_start(builder);
393+
etdump_Value_bool_value_add(builder, bool_ref);
394+
etdump_Value_val_add(builder, etdump_ValueType_Bool);
395+
} else {
396+
ET_CHECK_MSG(0, "Unsupported output type for intermediate logging\n");
397+
}
398+
399+
auto value_ref = etdump_Value_end(builder);
400+
etdump_DebugEvent_debug_entry_add(builder, value_ref);
401+
402+
etdump_DebugEvent_ref_t debug_event = etdump_DebugEvent_end(builder);
403+
404+
etdump_RunData_events_push_start(builder);
405+
etdump_Event_debug_event_add(builder, debug_event);
406+
etdump_RunData_events_push_end(builder);
407+
}
408+
291409
void ETDumpGen::end_profiling(EventTracerEntry prof_entry) {
292410
et_timestamp_t end_time = et_pal_current_ticks();
293411
ET_CHECK_MSG(

0 commit comments

Comments
 (0)