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