xref: /aosp_15_r20/external/executorch/runtime/core/event_tracer.h (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
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 #include <executorch/runtime/core/array_ref.h>
10 #include <executorch/runtime/core/evalue.h>
11 #include <executorch/runtime/platform/platform.h>
12 #include <stdlib.h>
13 #include <cstdint>
14 
15 #pragma once
16 
17 namespace executorch {
18 namespace runtime {
19 
20 /// Represents an allocator id returned by track_allocator.
21 typedef uint32_t AllocatorID;
22 /// Represents the chain id that will be passed in by the user during
23 /// event logging.
24 typedef int32_t ChainID;
25 /// Represents the debug handle that is generally associated with each
26 /// op executed in the runtime.
27 typedef uint32_t DebugHandle;
28 
29 /// Default id's for chain id and debug handle.
30 constexpr ChainID kUnsetChainId = -1;
31 constexpr DebugHandle kUnsetDebugHandle = 0;
32 // Default bundled input index to indicate that it hasn't been set yet.
33 constexpr int kUnsetBundledInputIndex = -1;
34 
35 /// Different types of delegate debug identifiers that are supported currently.
36 enum class DelegateDebugIdType {
37   /// Default value, indicates that it's not a delegate event.
38   kNone,
39   /// Indicates a delegate event logged using an integer delegate debug
40   /// identifier.
41   kInt,
42   /// Indicates a delegate event logged using a string delegate debug
43   /// identifier i.e. the delegate debug id is a pointer to a string table
44   /// managed by the class implementing EventTracer functionality.
45   kStr
46 };
47 
48 /// Indicates the type of the EValue that was logged. These values could be
49 /// serialized and should not be changed.
50 enum class LoggedEValueType {
51   /// Intermediate output from an operator.
52   kIntermediateOutput = 0,
53   /// Output at the program level. This is essentially the output
54   /// of the model.
55   kProgramOutput = 1,
56 };
57 
58 /// Indicates the level of event tracer debug logging. Verbosity of the logging
59 /// increases as we go down the enum list.
60 enum class EventTracerDebugLogLevel {
61   /// No logging.
62   kNoLogging,
63   /// When set to this only the program level outputs will be logged.
64   kProgramOutputs,
65   /// When set to this all intermediate outputs and program level outputs
66   /// will be logged.
67   kIntermediateOutputs,
68 };
69 
70 /**
71  * Indicates the level of profiling that should be enabled. Profiling
72  * events will be logged in increasing order of verbosity as we go down the
73  * enum list. Thus it is important to keep the enum values in the right order.
74  */
75 enum class EventTracerProfilingLevel {
76   /// No operator profiling.
77   kProfileMethodOnly,
78   /// All profiling events enabled.
79   kProfileAllEvents,
80 };
81 
82 /**
83  * This is the struct which should be returned when a profiling event is
84  * started. This is used to uniquely identify that profiling event and will be
85  * required to be passed into the end_profiling call to signal that the event
86  * identified by this struct has completed.
87  **/
88 struct EventTracerEntry {
89   /// An event id to uniquely identify this event that was generated during a
90   /// call to start the tracking of an event.
91   int64_t event_id;
92   /// The chain to which this event belongs to.
93   ChainID chain_id;
94   /// The debug handle corresponding to this event.
95   DebugHandle debug_handle;
96   /// The time at which this event was started to be tracked.
97   et_timestamp_t start_time;
98   /// When delegate_event_id_type != DelegateDebugIdType::kNone it indicates
99   /// that event_id represents a delegate event. If delegate_event_id_type is:
100   /// 1) kInt then event_id contains an integer delegate debug id.
101   /// 2) kStr then event_id contains a string table index into a string table
102   /// maintained by the class implementing EventTracer functionality that will
103   /// give us the string identifier of this delegate event. For more details
104   /// refer to the DelegateMappingBuilder library present in
105   /// executorch/exir/backend/utils.py.
106   DelegateDebugIdType delegate_event_id_type;
107 };
108 /**
109  * EventTracer is a class that users can inherit and implement to
110  * log/serialize/stream etc. the profiling and debugging events that are
111  * generated at runtime for a model. An example of this is the ETDump
112  * implementation in the devtools codebase that serializes these events to a
113  * flatbuffer.
114  */
115 class EventTracer {
116  public:
117   /**
118    * Start a new event block (can consist of profiling and/or debugging events.)
119    * identified by this name. A block is conceptually a set of events that we
120    * want to group together. e.g. all the events that occur during the call to
121    * execute() (i.e. model inference) could be categorized as a block.
122    *
123    * @param[in] name A human readable identifier for the event block. Users
124    * calling this interface do not need to keep the memory pointed to by this
125    * pointer around. The string must be copied over into internal memory during
126    * this call.
127    */
128   virtual void create_event_block(const char* name) = 0;
129 
130   /**
131    * Start the profiling of the event identified by name and debug_handle.
132    * The user can pass in a chain_id and debug_handle to this call, or leave
133    * them empty (default values) which would then result in the chain_id and
134    * debug handle stored within (set by set_chain_debug_handle) this class to be
135    * used.
136    * @param[in] name Human readable name for the profiling event. Users calling
137    * this interface do not need to keep the memory pointed to by this pointer
138    * around. The string must be copied over into internal memory during this
139    * call.
140    * @param[in] chain_id The id of the chain to which this event belongs to. If
141    * kUnsetChainId is passed in the chain_id and kUnsetDebugHandle for
142    * debug_handle then the values stored in the class internally for these
143    * properties will be used.
144    * @param[in] debug_handle Debug handle generated ahead-of-time during model
145    * compilation.
146    *
147    * @return Returns an instance of EventTracerEntry which should be passed back
148    * into the end_profiling() call.
149    */
150   virtual EventTracerEntry start_profiling(
151       const char* name,
152       ChainID chain_id = kUnsetChainId,
153       DebugHandle debug_handle = kUnsetDebugHandle) = 0;
154 
155   /**
156    * Start the profiling of a delegate event. Similar to start_profiling it will
157    * return an instance of EventTracerEntry that contains the details of this
158    * event.
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    */
171   virtual EventTracerEntry start_profiling_delegate(
172       const char* name,
173       DebugHandle delegate_debug_index) = 0;
174 
175   /**
176    * Signal the end of the delegate profiling event contained in
177    * event_tracer_entry. Users also have the option to log some some free-from
178    * string based metadata along with this.
179    *
180    * @param[in] event_tracer_entry The EventTracerEntry returned by a call to
181    * start_profiling_delegate().
182    * @param[in] metadata Optional data relevant to the execution that the user
183    * wants to log along with this event. Pointer to metadata doesn't need to be
184    * valid after the call to this function. The contents and format of the data
185    * are transparent to the event tracer. It will just pipe along the data and
186    * make it available for the user again in the post-processing stage.
187    * @param[in] metadata_len Length of the metadata buffer.
188    */
189   virtual void end_profiling_delegate(
190       EventTracerEntry event_tracer_entry,
191       const void* metadata = nullptr,
192       size_t metadata_len = 0) = 0;
193 
194   /**
195    * Some delegates get access to the profiling details only after the complete
196    * graph has been executed. This interface is to support such use cases. It
197    * can be called in a loop etc. to log any number of profiling events that are
198    * part of this delegate.
199    *
200    * @param[in] name Human readable name for the delegate event. This name has
201    * to be the same name that was passed in during the Debug delegate mapping
202    * generation in the export/ahead-of-time process. If indices and not names
203    * are used by this delegate to identify ops executed in the backend then
204    * nullptr can be passed in. Users calling this interface do not need to keep
205    * the memory pointed to by this pointer around. The string must be copied
206    * over into internal memory during this call.
207    * @param[in] delegate_debug_index The id of the delegate event. If string
208    * based names are used by this delegate to identify ops executed in the
209    * backend then kUnsetDebugHandle should be passed in here.
210    * @param[in] start_time The timestamp when the delegate event started.
211    * @param[in] end_time The timestamp when the delegate event finished.
212    * @param[in] metadata Optional data relevant to the execution that the user
213    * wants to log along with this event. Pointer to metadata doesn't need to be
214    * valid after the call to this function. The contents and format of the data
215    * are transparent to the event tracer. It will just pipe along the data and
216    * make it available for the user again in the post-processing stage.
217    * @param[in] metadata_len Length of the metadata buffer.
218    */
219   virtual void log_profiling_delegate(
220       const char* name,
221       DebugHandle delegate_debug_index,
222       et_timestamp_t start_time,
223       et_timestamp_t end_time,
224       const void* metadata = nullptr,
225       size_t metadata_len = 0) = 0;
226 
227   /**
228    * End the profiling of the event identified by prof_entry
229    *
230    * @param[in] prof_entry Value returned by a call to start_profiling
231    */
232   virtual void end_profiling(EventTracerEntry prof_entry) = 0;
233 
234   /**
235    * Track this allocation done via a MemoryAllocator which had profiling
236    * enabled on it.
237    *
238    * @param[in] id Allocator id generated by a call to track_allocator.
239    * @param[in] size The size of the allocation done, in bytes.
240    */
241   virtual void track_allocation(AllocatorID id, size_t size) = 0;
242 
243   /**
244    * Generate an allocator id for this memory allocator that will be used in the
245    * future to identify all the allocations done by this allocator.
246    *
247    * @param[in] name Human readable name for the allocator. Users calling
248    * this interface do not need to keep the memory pointed to by this pointer
249    * around. The string should be copied over into internal memory during this
250    * call.
251    *
252    * @return Identifier to uniquely identify this allocator.
253    */
254   virtual AllocatorID track_allocator(const char* name) = 0;
255 
256   /**
257    * Log an evalue during the execution of the model. This is useful for
258    * debugging purposes. Model outputs are a special case of this and will
259    * be logged with the output bool enabled.
260    *
261    * Users of this should refer to the chain_id and debug_handle to get the
262    * context for these evalues and their corresponding op.
263    *
264    * @param[in] evalue The value to be logged.
265    * @param[in] evalue_type Indicates what type of output this is logging e.g.
266    * an intermediate output, program output etc.
267    */
268   virtual void log_evalue(
269       const EValue& evalue,
270       LoggedEValueType evalue_type) = 0;
271 
272   /**
273    * Log an intermediate tensor output from a delegate.
274    *
275    * @param[in] name Human readable name for the delegate event. This name has
276    * to be the same name that was passed in during the Debug delegate mapping
277    * generation in the export/ahead-of-time process. If indices and not names
278    * are used by this delegate to identify ops executed in the backend then
279    * nullptr can be passed in. Users calling this interface do not need to keep
280    * the memory pointed to by this pointer around. The string must be copied
281    * over into internal memory during this call.
282    * @param[in] delegate_debug_index The id of the delegate event. If string
283    * based names are used by this delegate to identify ops executed in the
284    * backend then kUnsetDebugHandle should be passed in here.
285    * @param[in] output The tensor type output to be logged.
286    */
287   virtual void log_intermediate_output_delegate(
288       const char* name,
289       DebugHandle delegate_debug_index,
290       const executorch::aten::Tensor& output) = 0;
291 
292   /**
293    * Log an intermediate tensor array output from a delegate.
294    *
295    * @param[in] name Human readable name for the delegate event. This name has
296    * to be the same name that was passed in during the Debug delegate mapping
297    * generation in the export/ahead-of-time process. If indices and not names
298    * are used by this delegate to identify ops executed in the backend then
299    * nullptr can be passed in. Users calling this interface do not need to keep
300    * the memory pointed to by this pointer around. The string must be copied
301    * over into internal memory during this call.
302    * @param[in] delegate_debug_index The id of the delegate event. If string
303    * based names are used by this delegate to identify ops executed in the
304    * backend then kUnsetDebugHandle should be passed in here.
305    * @param[in] output The tensor array type output to be logged.
306    */
307   virtual void log_intermediate_output_delegate(
308       const char* name,
309       DebugHandle delegate_debug_index,
310       const ArrayRef<executorch::aten::Tensor> output) = 0;
311 
312   /**
313    * Log an intermediate int output from a delegate.
314    *
315    * @param[in] name Human readable name for the delegate event. This name has
316    * to be the same name that was passed in during the Debug delegate mapping
317    * generation in the export/ahead-of-time process. If indices and not names
318    * are used by this delegate to identify ops executed in the backend then
319    * nullptr can be passed in. Users calling this interface do not need to keep
320    * the memory pointed to by this pointer around. The string must be copied
321    * over into internal memory during this call.
322    * @param[in] delegate_debug_index The id of the delegate event. If string
323    * based names are used by this delegate to identify ops executed in the
324    * backend then kUnsetDebugHandle should be passed in here.
325    * @param[in] output The int type output to be logged.
326    */
327   virtual void log_intermediate_output_delegate(
328       const char* name,
329       DebugHandle delegate_debug_index,
330       const int& output) = 0;
331 
332   /**
333    * Log an intermediate bool output from a delegate.
334    *
335    * @param[in] name Human readable name for the delegate event. This name has
336    * to be the same name that was passed in during the Debug delegate mapping
337    * generation in the export/ahead-of-time process. If indices and not names
338    * are used by this delegate to identify ops executed in the backend then
339    * nullptr can be passed in. Users calling this interface do not need to keep
340    * the memory pointed to by this pointer around. The string must be copied
341    * over into internal memory during this call.
342    * @param[in] delegate_debug_index The id of the delegate event. If string
343    * based names are used by this delegate to identify ops executed in the
344    * backend then kUnsetDebugHandle should be passed in here.
345    * @param[in] output The bool type output to be logged.
346    */
347   virtual void log_intermediate_output_delegate(
348       const char* name,
349       DebugHandle delegate_debug_index,
350       const bool& output) = 0;
351 
352   /**
353    * Log an intermediate double output from a delegate.
354    *
355    * @param[in] name Human readable name for the delegate event. This name has
356    * to be the same name that was passed in during the Debug delegate mapping
357    * generation in the export/ahead-of-time process. If indices and not names
358    * are used by this delegate to identify ops executed in the backend then
359    * nullptr can be passed in. Users calling this interface do not need to keep
360    * the memory pointed to by this pointer around. The string must be copied
361    * over into internal memory during this call.
362    * @param[in] delegate_debug_index The id of the delegate event. If string
363    * based names are used by this delegate to identify ops executed in the
364    * backend then kUnsetDebugHandle should be passed in here.
365    * @param[in] output The double type output to be logged.
366    */
367   virtual void log_intermediate_output_delegate(
368       const char* name,
369       DebugHandle delegate_debug_index,
370       const double& output) = 0;
371 
372   /**
373    * Helper function to set the chain id ands debug handle. Users have two
374    * options, the first is that they can directly pass in the chain id and debug
375    * handle to start_profiling or they can explicitly set them through this
376    * helper before calling start_profiling.
377    *
378    * The reason this helper exists is to
379    * solve a specific problem. We want to do profiling logging inside the
380    * codegen layer which calls the kernels. The problem though is that the
381    * codegen layer doesn't have access to these ids when calling
382    * start_profiling.
383    *
384    * Users should ideally use these within a RAII scope interface to make sure
385    * that these values are unset after the end_profiling call. If non-default
386    * values are passed into the start_profiling call they will always be given
387    * precedence over the values set by this interface.
388    *
389    * So what we do is call this helper in method.cpp before
390    * we hit the codegen layer and in the codegen layer we do a start_profiling
391    * call without passing in a chain_id or debug_handle. This ensures that the
392    * values set via this helper are the ones associated with that call.
393    *
394    * @param[in] chain_id Chain id of the current instruction being exectuted.
395    * @param[in] debug_handle Debug handle of the current instruction being
396    * executed. In this context debug handle and instruction id are the same
397    * thing.
398    */
set_chain_debug_handle(ChainID chain_id,DebugHandle debug_handle)399   void set_chain_debug_handle(ChainID chain_id, DebugHandle debug_handle) {
400     chain_id_ = chain_id;
401     debug_handle_ = debug_handle;
402   }
403 
404   /**
405    * When running a program wrapped in a bundled program, log the bundled input
406    * index of the current bundled input being tested out on this method.
407    * If users want to unset the index back to the default value, they can call
408    * this method with kUnsetBundledInputIndex.
409    *
410    * @param[in] bundled_input_index Index of the current input being tested
411    */
set_bundled_input_index(int bundled_input_index)412   void set_bundled_input_index(int bundled_input_index) {
413     bundled_input_index_ = bundled_input_index;
414   }
415 
416   /**
417    * Return the current bundled input index.
418    */
bundled_input_index()419   int bundled_input_index() {
420     return bundled_input_index_;
421   }
422 
423   /**
424    * Set the level of event tracer debug logging that is desired.
425    *
426    */
set_event_tracer_debug_level(EventTracerDebugLogLevel log_level)427   void set_event_tracer_debug_level(EventTracerDebugLogLevel log_level) {
428     event_tracer_debug_level_ = log_level;
429   }
430 
431   /**
432    * Return the current level of event tracer debug logging.
433    */
event_tracer_debug_level()434   EventTracerDebugLogLevel event_tracer_debug_level() {
435     return event_tracer_debug_level_;
436   }
437 
438   /**
439    * Set the level of event tracer profiling that is desired.
440    */
set_event_tracer_profiling_level(EventTracerProfilingLevel profiling_level)441   void set_event_tracer_profiling_level(
442       EventTracerProfilingLevel profiling_level) {
443     event_tracer_profiling_level_ = profiling_level;
444   }
445 
446   /**
447    * Return the current level of event tracer profiling.
448    */
event_tracer_profiling_level()449   EventTracerProfilingLevel event_tracer_profiling_level() {
450     return event_tracer_profiling_level_;
451   }
452 
453   /**
454    * Return the current status of intermediate outputs logging mode.
455    */
intermediate_outputs_logging_status()456   bool intermediate_outputs_logging_status() {
457     return log_intermediate_tensors_;
458   }
459 
460   /**
461    * Get the current chain id.
462    *
463    * @return Current chain id.
464    */
current_chain_id()465   ChainID current_chain_id() {
466     return chain_id_;
467   }
468 
469   /**
470    * Get the current debug handle.
471    *
472    * @return Current debug handle.
473    */
current_debug_handle()474   DebugHandle current_debug_handle() {
475     return debug_handle_;
476   }
477 
~EventTracer()478   virtual ~EventTracer() {}
479 
480  protected:
481   ChainID chain_id_ = kUnsetChainId;
482   DebugHandle debug_handle_ = kUnsetDebugHandle;
483   bool event_tracer_enable_debugging_ = false;
484   bool log_intermediate_tensors_ = false;
485   int bundled_input_index_ = kUnsetBundledInputIndex;
486   EventTracerDebugLogLevel event_tracer_debug_level_ =
487       EventTracerDebugLogLevel::kNoLogging;
488   EventTracerProfilingLevel event_tracer_profiling_level_ =
489       EventTracerProfilingLevel::kProfileAllEvents;
490 };
491 
492 } // namespace runtime
493 } // namespace executorch
494 
495 namespace torch {
496 namespace executor {
497 // TODO(T197294990): Remove these deprecated aliases once all users have moved
498 // to the new `::executorch` namespaces.
499 using ::executorch::runtime::AllocatorID;
500 using ::executorch::runtime::ChainID;
501 using ::executorch::runtime::DebugHandle;
502 using ::executorch::runtime::DelegateDebugIdType;
503 using ::executorch::runtime::EventTracer;
504 using ::executorch::runtime::EventTracerDebugLogLevel;
505 using ::executorch::runtime::EventTracerEntry;
506 using ::executorch::runtime::kUnsetBundledInputIndex;
507 using ::executorch::runtime::kUnsetChainId;
508 using ::executorch::runtime::kUnsetDebugHandle;
509 using ::executorch::runtime::LoggedEValueType;
510 } // namespace executor
511 } // namespace torch
512