1 /* Copyright 2016 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_CORE_DEBUG_DEBUG_GRAPH_UTILS_H_ 17 #define TENSORFLOW_CORE_DEBUG_DEBUG_GRAPH_UTILS_H_ 18 19 #include <unordered_map> 20 #include <vector> 21 22 #include "tensorflow/core/common_runtime/debugger_state_interface.h" 23 #include "tensorflow/core/common_runtime/device.h" 24 #include "tensorflow/core/graph/graph.h" 25 #include "tensorflow/core/lib/core/status.h" 26 #include "tensorflow/core/platform/protobuf.h" 27 #include "tensorflow/core/protobuf/debug.pb.h" 28 29 namespace tensorflow { 30 31 class DebugNodeInserter { 32 public: 33 // EXPERIMENTAL: Insert special debug ops (e.g., DebugIdentity) to graph for 34 // debugging. Currently, such ops need to take exactly one input and has the 35 // string attribute "tensor_name" to indicate what tensor it watches. 36 // For example, before the node insertion, the graph may look like: 37 // 38 // A:0 -----------1----------> B 39 // | 40 // ---------2-----------> C 41 // 42 // wherein the output slot 0 of node A feeds as the input to nodes B through 43 // edge 1 and to node C through edge 2. 44 // After the node insertion, assuming both B and C have non-Ref input, the 45 // graph becomes: 46 // A:0 ---3---> Copy -----------4----------> B 47 // | 48 // ---------5--------> C 49 // | 50 // ---------6--------> X 51 // 52 // If a node (e.g., B) has Ref input, the graph becomes: 53 // 54 // --------------------------------> B 55 // | 56 // A:0 ---3-----> Copy -----------4----------> C 57 // | 58 // -----------5--------> X 59 // 60 // In other words, we do not feed Refs to deep-copies to downstream nodes. 61 // 62 // Copy is the inserted deep-copy node that copies the input tensor on-device 63 // (e.g., CPU-to-CPU or GPU-to-GPU deep copy) that reduces the likelihood of 64 // racy updates during the debug watches. X is the newly created debug node 65 // that transforms the input (copy of the watched tensor) into a debug signal. 66 // 67 // DebugIdentity is the simplest debugging paradigm, in which the debug signal 68 // (i.e., X:0) equals the tensor itself. More sophisticated debug ops can be 69 // used to transform the tensor into other debug signals. An example is the 70 // DebugNanCounter op. 71 // 72 // If the nodes (A, B and C) are located on GPU and the edges from A to B or C 73 // is HOST_MEMORY, then the CopyHost op will be used instead of the Copy op. 74 static Status InsertNodes( 75 const protobuf::RepeatedPtrField<DebugTensorWatch>& watches, Graph* graph, 76 Device* device); 77 78 // Set the parallel_iterations attribute of TensorFlow while loops 79 // (specifically the nodes for which IsEnter() returns true) to 1 to prevent 80 // any node from being executed multiple times concurrently and 81 // generating temporally-overlapping debug Tensor dumps. 82 static void DeparallelizeWhileLoops(Graph* graph, Device* device); 83 84 // Get canonical name of a copy node. 85 static const string GetCopyNodeName(const string& node_name, 86 const int output_slot); 87 88 // Get canonical name of a debug node. 89 static const string GetDebugNodeName(const string& tensor_name, 90 const int debug_op_num, 91 const string& debug_op_name); 92 93 private: 94 static Status CreateCopyNode( 95 Graph* graph, const DeviceType device_type, const bool is_host_memory, 96 const string& src_node_name, const int src_output, const DataType src_dt, 97 const string& tensor_name, const std::vector<string>& debug_ops, 98 const std::vector<string>& debug_urls, Node** copy_node); 99 100 // Parse the debug_op_name string to extract proper op name and attributes. 101 // debug_op_name can be the proper op name only, e.g., "DebugNumericSummary". 102 // It can also contain customizable keys and values. Each key-value pair is 103 // connected with an equal sign ("="). Multiple key-value pairs are separated 104 // with semicolons (";"), which optional whitespace in between, e.g., 105 // "DebugNumericSummary(mute_if_healthy=true, lower_bound=-100.0)". 106 static Status ParseDebugOpName( 107 const string& debug_op_name, string* debug_op_name_proper, 108 std::unordered_map<string, string>* attributes); 109 110 static Status SetDebugNodeAttributes( 111 Node* debug_node, const std::unordered_map<string, string>& attributes); 112 113 static Status CreateDebugNode(Graph* graph, const Device& device, 114 const string& src_copy_node_name, 115 const DataType src_dt, 116 const string& tensor_name, 117 const std::vector<string>& debug_urls, 118 const int debug_op_num, 119 const string& debug_op_name, Node** debug_node); 120 // TODO(cais): Cut down the number of args to this method. 121 122 friend class DebugGraphUtilsTest; 123 }; 124 } // namespace tensorflow 125 126 #endif // TENSORFLOW_CORE_DEBUG_DEBUG_GRAPH_UTILS_H_ 127