xref: /aosp_15_r20/external/tensorflow/tensorflow/core/util/dump_graph.cc (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1 /* Copyright 2018 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 // Helper functions for dumping Graphs, GraphDefs, and FunctionDefs to files for
17 // debugging.
18 
19 #include "tensorflow/core/util/dump_graph.h"
20 
21 #include <memory>
22 #include <unordered_map>
23 
24 #include "absl/strings/match.h"
25 #include "absl/strings/str_cat.h"
26 #include "tensorflow/core/lib/strings/proto_serialization.h"
27 #include "tensorflow/core/platform/env.h"
28 #include "tensorflow/core/platform/file_system.h"
29 #include "tensorflow/core/platform/mutex.h"
30 #include "tensorflow/core/platform/path.h"
31 #include "tensorflow/core/platform/strcat.h"
32 
33 namespace tensorflow {
34 
35 namespace {
36 using strings::StrCat;
37 
38 struct NameCounts {
39   mutex counts_mutex;
40   std::unordered_map<string, int> counts;
41 };
42 
MakeUniqueFilename(string name,const string & suffix=".pbtxt")43 string MakeUniqueFilename(string name, const string& suffix = ".pbtxt") {
44   static NameCounts& instance = *new NameCounts;
45 
46   // Remove illegal characters from `name`.
47   for (int i = 0; i < name.size(); ++i) {
48     char ch = name[i];
49     if (ch == '/' || ch == '[' || ch == ']' || ch == '*' || ch == '?' ||
50         ch == '\\') {
51       name[i] = '_';
52     }
53   }
54 
55   int count;
56   {
57     mutex_lock lock(instance.counts_mutex);
58     count = instance.counts[name]++;
59   }
60 
61   string filename = name;
62   if (count > 0) {
63     absl::StrAppend(&filename, "_", count);
64   }
65   absl::StrAppend(&filename, suffix);
66   return filename;
67 }
68 
69 struct GraphDumperConfig {
70   mutex mu;
71 
72   // The dumper and suffix configured.
73   struct Config {
IsSettensorflow::__anon7bf4746b0111::GraphDumperConfig::Config74     bool IsSet() const { return dumper != nullptr; }
75     std::function<Status(const Graph& graph,
76                          const FunctionLibraryDefinition* flib_def,
77                          WritableFile*)>
78         dumper = nullptr;
79     string suffix = ".pbtxt";
80   } config TF_GUARDED_BY(mu);
81 
82   // Returns whether a custom dumper is set.
IsSettensorflow::__anon7bf4746b0111::GraphDumperConfig83   bool IsSet() TF_LOCKS_EXCLUDED(mu) {
84     mutex_lock lock(mu);
85     return config.IsSet();
86   }
87 };
88 
GetGraphDumperConfig()89 GraphDumperConfig& GetGraphDumperConfig() {
90   static GraphDumperConfig config;
91   return config;
92 }
93 
94 // WritableFile that simply prints to stderr.
95 class StderrWritableFile : public WritableFile {
96  public:
StderrWritableFile()97   StderrWritableFile() {}
98 
Append(StringPiece data)99   Status Append(StringPiece data) override {
100     fprintf(stderr, "%.*s", static_cast<int>(data.size()), data.data());
101     return OkStatus();
102   }
103 
Close()104   Status Close() override { return OkStatus(); }
105 
Flush()106   Status Flush() override {
107     fflush(stderr);
108     return OkStatus();
109   }
110 
Name(StringPiece * result) const111   Status Name(StringPiece* result) const override {
112     *result = "stderr";
113     return OkStatus();
114   }
115 
Sync()116   Status Sync() override { return OkStatus(); }
117 
Tell(int64_t * position)118   Status Tell(int64_t* position) override {
119     return errors::Unimplemented("Stream not seekable");
120   }
121 };
122 
CreateWritableFile(Env * env,const string & dirname,const string & name,const string & suffix,string * filepath,std::unique_ptr<WritableFile> * file)123 Status CreateWritableFile(Env* env, const string& dirname, const string& name,
124                           const string& suffix, string* filepath,
125                           std::unique_ptr<WritableFile>* file) {
126   string dir;
127   if (!dirname.empty()) {
128     dir = dirname;
129   } else {
130     const char* prefix = getenv("TF_DUMP_GRAPH_PREFIX");
131     if (prefix != nullptr) dir = prefix;
132   }
133   if (dir.empty()) {
134     LOG(WARNING)
135         << "Failed to dump " << name << " because dump location is not "
136         << " specified through either TF_DUMP_GRAPH_PREFIX environment "
137         << "variable or function argument.";
138     return errors::InvalidArgument("TF_DUMP_GRAPH_PREFIX not specified");
139   }
140 
141   if (absl::EqualsIgnoreCase(dir, "sponge") ||
142       absl::EqualsIgnoreCase(dir, "test_undeclared_outputs_dir")) {
143     if (!io::GetTestUndeclaredOutputsDir(&dir)) {
144       LOG(WARNING) << "TF_DUMP_GRAPH_PREFIX=sponge, but "
145                       "TEST_UNDECLARED_OUTPUT_DIRS is not set, dumping to log";
146       dir = "-";
147     }
148   }
149 
150   *filepath = "NULL";
151   if (dir == "-") {
152     *file = std::make_unique<StderrWritableFile>();
153     *filepath = "(stderr)";
154     return OkStatus();
155   }
156 
157   TF_RETURN_IF_ERROR(env->RecursivelyCreateDir(dir));
158   *filepath = io::JoinPath(dir, MakeUniqueFilename(name, suffix));
159   return env->NewWritableFile(*filepath, file);
160 }
161 
WriteTextProtoToUniqueFile(const tensorflow::protobuf::Message & proto,WritableFile * file)162 Status WriteTextProtoToUniqueFile(const tensorflow::protobuf::Message& proto,
163                                   WritableFile* file) {
164   string s;
165   if (!::tensorflow::protobuf::TextFormat::PrintToString(proto, &s)) {
166     return errors::FailedPrecondition("Unable to convert proto to text.");
167   }
168   TF_RETURN_IF_ERROR(file->Append(s));
169   StringPiece name;
170   TF_RETURN_IF_ERROR(file->Name(&name));
171   VLOG(5) << name;
172   VLOG(5) << s;
173   return file->Close();
174 }
175 
WriteTextProtoToUniqueFile(const tensorflow::protobuf::MessageLite & proto,WritableFile * file)176 Status WriteTextProtoToUniqueFile(
177     const tensorflow::protobuf::MessageLite& proto, WritableFile* file) {
178   string s;
179   if (!SerializeToStringDeterministic(proto, &s)) {
180     return errors::Internal("Failed to serialize proto to string.");
181   }
182   StringPiece name;
183   TF_RETURN_IF_ERROR(file->Name(&name));
184   VLOG(5) << name;
185   VLOG(5) << s;
186   TF_RETURN_IF_ERROR(file->Append(s));
187   return file->Close();
188 }
189 
DumpToFile(const string & name,const string & dirname,const string & suffix,const string & type_name,std::function<Status (WritableFile *)> dumper)190 string DumpToFile(const string& name, const string& dirname,
191                   const string& suffix, const string& type_name,
192                   std::function<Status(WritableFile*)> dumper) {
193   string filepath;
194   std::unique_ptr<WritableFile> file;
195   Status status = CreateWritableFile(Env::Default(), dirname, name, suffix,
196                                      &filepath, &file);
197   if (!status.ok()) {
198     return StrCat("(failed to create writable file: ", status.ToString(), ")");
199   }
200 
201   status = dumper(file.get());
202   if (!status.ok()) {
203     return StrCat("(failed to dump ", type_name, " to '", filepath,
204                   "': ", status.ToString(), ")");
205   }
206   LOG(INFO) << "Dumped " << type_name << " to " << filepath;
207   return filepath;
208 }
209 
210 }  // anonymous namespace
211 
SetGraphDumper(std::function<Status (const Graph & graph,const FunctionLibraryDefinition * flib_def,WritableFile *)> dumper,string suffix)212 void SetGraphDumper(
213     std::function<Status(const Graph& graph,
214                          const FunctionLibraryDefinition* flib_def,
215                          WritableFile*)>
216         dumper,
217     string suffix) {
218   GraphDumperConfig& dumper_config = GetGraphDumperConfig();
219   mutex_lock lock(dumper_config.mu);
220   dumper_config.config.dumper = dumper;
221   dumper_config.config.suffix = suffix;
222 }
223 
DumpGraphDefToFile(const string & name,GraphDef const & graph_def,const string & dirname)224 string DumpGraphDefToFile(const string& name, GraphDef const& graph_def,
225                           const string& dirname) {
226   return DumpToFile(name, dirname, ".pbtxt", "Graph", [&](WritableFile* file) {
227     return WriteTextProtoToUniqueFile(graph_def, file);
228   });
229 }
230 
DumpCostGraphDefToFile(const string & name,CostGraphDef const & graph_def,const string & dirname)231 string DumpCostGraphDefToFile(const string& name, CostGraphDef const& graph_def,
232                               const string& dirname) {
233   return DumpToFile(name, dirname, ".pbtxt", "Graph", [&](WritableFile* file) {
234     return WriteTextProtoToUniqueFile(graph_def, file);
235   });
236 }
237 
DumpGraphToFile(const string & name,Graph const & graph,const FunctionLibraryDefinition * flib_def,const string & dirname)238 string DumpGraphToFile(const string& name, Graph const& graph,
239                        const FunctionLibraryDefinition* flib_def,
240                        const string& dirname) {
241   auto& dumper_config = GetGraphDumperConfig();
242   if (dumper_config.IsSet()) {
243     GraphDumperConfig::Config config;
244     {
245       mutex_lock lock(dumper_config.mu);
246       config = dumper_config.config;
247     }
248     if (config.IsSet()) {
249       return DumpToFile(name, dirname, config.suffix, "Graph",
250                         [&](WritableFile* file) {
251                           return config.dumper(graph, flib_def, file);
252                         });
253     }
254   }
255 
256   GraphDef graph_def;
257   graph.ToGraphDef(&graph_def);
258   if (flib_def) {
259     *graph_def.mutable_library() = flib_def->ToProto();
260   }
261   return DumpGraphDefToFile(name, graph_def, dirname);
262 }
263 
DumpFunctionDefToFile(const string & name,FunctionDef const & fdef,const string & dirname)264 string DumpFunctionDefToFile(const string& name, FunctionDef const& fdef,
265                              const string& dirname) {
266   return DumpToFile(name, dirname, ".pbtxt", "FunctionDef",
267                     [&](WritableFile* file) {
268                       return WriteTextProtoToUniqueFile(fdef, file);
269                     });
270 }
271 
272 }  // namespace tensorflow
273