1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved. 2 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 ==============================================================================*/ 15 16 #ifndef TENSORFLOW_PLATFORM_TRACING_H_ 17 #define TENSORFLOW_PLATFORM_TRACING_H_ 18 19 // Tracing interface 20 21 #include <atomic> 22 #include <map> 23 #include <memory> 24 25 #include "tensorflow/core/lib/core/stringpiece.h" 26 #include "tensorflow/core/lib/strings/strcat.h" 27 #include "tensorflow/core/platform/macros.h" 28 #include "tensorflow/core/platform/mutex.h" 29 #include "tensorflow/core/platform/platform.h" 30 #include "tensorflow/core/platform/types.h" 31 32 namespace tensorflow { 33 34 namespace port { 35 36 class Tracing { 37 public: 38 // This enumeration contains the identifiers of all TensorFlow 39 // threadscape events and code regions. Threadscape assigns its 40 // own identifiers at runtime when we register our events and we 41 // cannot know in advance what IDs it will choose. The "RecordEvent" 42 // method and "ScopedActivity" use these event IDs for consistency 43 // and remap them to threadscape IDs at runtime. This enum is limited 44 // to 64 values since we use a bitmask to configure which events are 45 // enabled. It must also be kept in step with the code in 46 // "Tracing::EventCategoryString". 47 enum EventCategory { 48 kScheduleClosure = 0, 49 kRunClosure = 1, 50 kCompute = 2, 51 kEventCategoryMax = 3 // sentinel - keep last 52 }; 53 // Note: We currently only support up to 64 categories. 54 static_assert(kEventCategoryMax <= 64, "only support up to 64 events"); 55 56 // Called by main programs to initialize tracing facilities 57 static void Initialize(); 58 59 // Return the pathname of the directory where we are writing log files. 60 static const char* LogDir(); 61 62 // Returns a non-zero identifier which can be used to correlate 63 // related events. 64 static inline uint64 UniqueId(); 65 66 // Returns true if a trace is in progress. Can be used to reduce tracing 67 // overheads in fast-path code. 68 static inline bool IsActive(); 69 70 // Associate name with the current thread. 71 static void RegisterCurrentThread(const char* name); 72 73 // Posts an event with the supplied category and arg. 74 static void RecordEvent(EventCategory category, uint64 arg); 75 76 // Traces a region of code. Posts a tracing "EnterCodeRegion" event 77 // when created and an "ExitCodeRegion" event when destroyed. 78 class ScopedActivity { 79 public: 80 explicit ScopedActivity(EventCategory category, uint64 arg); 81 ~ScopedActivity(); 82 83 private: 84 #if defined(PLATFORM_GOOGLE) 85 const bool enabled_; 86 const int32 region_id_; 87 #endif 88 89 TF_DISALLOW_COPY_AND_ASSIGN(ScopedActivity); 90 }; 91 92 // Trace collection engine can be registered with this module. 93 // If no engine is registered, ScopedAnnotation and TraceMe are no-ops. 94 class Engine; 95 static void RegisterEngine(Engine*); 96 97 // Forward declaration of the GPU utility classes. 98 class ScopedAnnotation; 99 class TraceMe; 100 101 private: 102 friend class TracingTest; 103 friend class ScopedAnnotation; 104 friend class TraceMe; 105 106 static std::atomic<Tracing::Engine*> tracing_engine_; 107 static Tracing::Engine* engine() { 108 return tracing_engine_.load(std::memory_order_acquire); 109 } 110 111 static void RegisterEvent(EventCategory id, const char* name); 112 static const char* EventCategoryString(EventCategory category); 113 114 // 115 // Parses event mask expressions in 'value' of the form: 116 // expr ::= <term> (,<term>)* 117 // term ::= <event> | "!" <event> 118 // event ::= "ALL" | <wait_event> | <other_event> 119 // wait_event ::= "ENewSession" | "ECloseSession" | ... 120 // other_event ::= "Send" | "Wait" | ... 121 // ALL denotes all events, <event> turns on tracing for this event, and 122 // !<event> turns off tracing for this event. 123 // If the expression can be parsed correctly it returns true and sets 124 // the event_mask_. Otherwise it returns false and the event_mask_ is left 125 // unchanged. 126 static bool ParseEventMask(const char* flagname, const string& value); 127 128 // Bit mask of enabled trace categories. 129 static uint64 event_mask_; 130 131 // Records the mappings between Threadscape IDs and the "EventCategory" enum. 132 static int32 category_id_[kEventCategoryMax]; 133 static std::map<string, int32>* name_map_; 134 }; 135 136 // Trace collection engine that actually implements collection. 137 class Tracing::Engine { 138 public: 139 Engine() {} 140 virtual ~Engine(); 141 142 // Returns true if Tracing is currently enabled. 143 virtual bool IsEnabled() const = 0; 144 145 // Represents an active annotation. 146 class Annotation { 147 public: 148 Annotation() {} 149 virtual ~Annotation(); 150 }; 151 152 // Represents an active trace. 153 class Tracer { 154 public: 155 Tracer() {} 156 virtual ~Tracer(); 157 }; 158 159 private: 160 friend class ScopedAnnotation; 161 friend class TraceMe; 162 163 // Register the specified name as an annotation on the current thread. 164 // Caller should delete the result to remove the annotation. 165 // Annotations from the same thread are destroyed in a LIFO manner. 166 // May return nullptr if annotations are not supported. 167 virtual Annotation* PushAnnotation(StringPiece name) = 0; 168 169 // Start tracing under the specified label. Caller should delete the result 170 // to stop tracing. 171 // May return nullptr if tracing is not supported. 172 virtual Tracer* StartTracing(StringPiece label, bool is_expensive) = 0; 173 // Same as above, but implementations can avoid copying the string. 174 virtual Tracer* StartTracing(string&& label, bool is_expensive) { 175 return StartTracing(StringPiece(label), is_expensive); 176 } 177 178 // Backwards compatibility one arg variants (assume is_expensive=true). 179 Tracer* StartTracing(StringPiece label) { 180 return StartTracing(label, /*is_expensive=*/true); 181 } 182 Tracer* StartTracing(string&& label) { 183 return StartTracing(StringPiece(label), /*is_expensive=*/true); 184 } 185 }; 186 187 // This class permits a user to apply annotation on kernels and memcpys 188 // when launching them. While an annotation is in scope, all activities 189 // within that scope get their names replaced by the annotation. The kernel 190 // name replacement is done when constructing the protobuf for sending out to 191 // a client (e.g., the stubby requestor) for both API and Activity records. 192 // 193 // Ownership: The creator of ScopedAnnotation assumes ownership of the object. 194 // 195 // Usage: { 196 // ScopedAnnotation annotation("first set of kernels"); 197 // Kernel1<<<x,y>>>; 198 // LaunchKernel2(); // Which eventually launches a cuda kernel. 199 // } 200 // In the above scenario, the GPUProf UI would show 2 kernels with the name 201 // "first set of kernels" executing -- they will appear as the same kernel. 202 class Tracing::ScopedAnnotation { 203 public: 204 explicit ScopedAnnotation(StringPiece name); 205 206 // If tracing is enabled, set up an annotation with a label of 207 // "<name_part1>:<name_part2>". Can be cheaper than the 208 // single-argument constructor because the concatenation of the 209 // label string is only done if tracing is enabled. 210 ScopedAnnotation(StringPiece name_part1, StringPiece name_part2); 211 212 // Returns true iff scoped annotations are active. 213 static bool Enabled() { 214 auto e = Tracing::engine(); 215 return e && e->IsEnabled(); 216 } 217 218 private: 219 std::unique_ptr<Engine::Annotation> annotation_; 220 }; 221 222 // TODO(opensource): clean up the scoped classes for GPU tracing. 223 // This class permits user-specified (CPU) tracing activities. A trace 224 // activity is started when an object of this class is created and stopped 225 // when the object is destroyed. 226 class Tracing::TraceMe { 227 public: 228 explicit TraceMe(StringPiece name); 229 TraceMe(StringPiece name, bool is_expensive); 230 231 // If tracing is enabled, set up a traceMe with a label of 232 // "<name_part1>:<name_part2>". This can be cheaper than the 233 // single-argument constructor because the concatenation of the 234 // label string is only done if tracing is enabled. 235 TraceMe(StringPiece name_part1, StringPiece name_part2); 236 TraceMe(StringPiece name_part1, StringPiece name_part2, bool is_expensive); 237 238 private: 239 std::unique_ptr<Engine::Tracer> tracer_; 240 }; 241 242 inline Tracing::ScopedAnnotation::ScopedAnnotation(StringPiece name) { 243 auto e = Tracing::engine(); 244 if (e && e->IsEnabled()) { 245 annotation_.reset(e->PushAnnotation(name)); 246 } 247 } 248 249 inline Tracing::ScopedAnnotation::ScopedAnnotation(StringPiece name_part1, 250 StringPiece name_part2) { 251 auto e = Tracing::engine(); 252 if (e && e->IsEnabled()) { 253 annotation_.reset( 254 e->PushAnnotation(strings::StrCat(name_part1, ":", name_part2))); 255 } 256 } 257 258 inline Tracing::TraceMe::TraceMe(StringPiece name) : TraceMe(name, true) {} 259 260 inline Tracing::TraceMe::TraceMe(StringPiece name, bool is_expensive) { 261 auto e = Tracing::engine(); 262 if (e && e->IsEnabled()) { 263 tracer_.reset(e->StartTracing(name, is_expensive)); 264 } 265 } 266 267 inline Tracing::TraceMe::TraceMe(StringPiece name_part1, StringPiece name_part2) 268 : TraceMe(name_part1, name_part2, true) {} 269 270 inline Tracing::TraceMe::TraceMe(StringPiece name_part1, StringPiece name_part2, 271 bool is_expensive) { 272 auto e = Tracing::engine(); 273 if (e && e->IsEnabled()) { 274 tracer_.reset(e->StartTracing(strings::StrCat(name_part1, ":", name_part2), 275 is_expensive)); 276 } 277 } 278 279 } // namespace port 280 } // namespace tensorflow 281 282 #if defined(PLATFORM_GOOGLE) 283 #include "tensorflow/core/platform/google/tracing_impl.h" 284 #else 285 #include "tensorflow/core/platform/default/tracing_impl.h" 286 #endif 287 288 #endif // TENSORFLOW_PLATFORM_TRACING_H_ 289