• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2019 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 #include "tensorflow/compiler/xla/service/dump.h"
17 
18 #include "absl/strings/ascii.h"
19 #include "absl/strings/str_cat.h"
20 #include "tensorflow/compiler/xla/service/hlo_graph_dumper.h"
21 #include "tensorflow/compiler/xla/service/hlo_module.h"
22 #include "tensorflow/compiler/xla/service/hlo_proto_util.h"
23 #include "tensorflow/compiler/xla/util.h"
24 #include "tensorflow/core/lib/core/status.h"
25 #include "tensorflow/core/lib/io/path.h"
26 #include "tensorflow/core/lib/io/zlib_compression_options.h"
27 #include "tensorflow/core/lib/io/zlib_outputbuffer.h"
28 #include "tensorflow/core/lib/strings/proto_serialization.h"
29 #include "tensorflow/core/platform/env.h"
30 #include "tensorflow/core/platform/path.h"
31 #include "tensorflow/core/platform/regexp.h"
32 
33 namespace xla {
34 
35 namespace {
36 
37 using absl::StrCat;
38 using absl::StrFormat;
39 using absl::string_view;
40 
41 struct CanonicalDebugOptions {
CanonicalDebugOptionsxla::__anon28f990a90111::CanonicalDebugOptions42   explicit CanonicalDebugOptions(const DebugOptions& opts)
43       : dump_to(opts.xla_dump_to()),
44         dump_as_text(opts.xla_dump_hlo_as_text()),
45         dump_as_proto(opts.xla_dump_hlo_as_proto()),
46         dump_as_dot(opts.xla_dump_hlo_as_dot()),
47         dump_as_html(opts.xla_dump_hlo_as_html()),
48         dump_as_url(opts.xla_dump_hlo_as_url()),
49         dump_fusion_visualization(opts.xla_dump_fusion_visualization()),
50         dump_snapshots(opts.xla_dump_hlo_snapshots()),
51         dump_include_timestamp(opts.xla_dump_include_timestamp()),
52         dump_max_hlo_modules(opts.xla_dump_max_hlo_modules()),
53         dump_module_metadata(opts.xla_dump_module_metadata()),
54         dump_compress_protos(opts.xla_dump_compress_protos()),
55         dump_hlo_metadata(!opts.xla_dump_disable_metadata()) {
56     // This constructor examines the values in `opts` and turns on other flags
57     // based on what we think is the user's intent.  To reduce confusion about
58     // what was a user-specified value versus an extrapolated value, within this
59     // function we treat this struct's members as write-only, and read only from
60     // `opts`.
61 
62     // Did the user specify an explicit format for dumping?
63     bool output_format_other_than_url_specified =
64         opts.xla_dump_hlo_as_text() || opts.xla_dump_hlo_as_proto() ||
65         opts.xla_dump_hlo_as_dot() || opts.xla_dump_hlo_as_html() ||
66         opts.xla_dump_hlo_snapshots();
67     bool output_format_specified =
68         output_format_other_than_url_specified || opts.xla_dump_hlo_as_url();
69 
70     // If we haven't specified an output format, default to dumping as text.
71     if (!output_format_specified) {
72       dump_as_text = true;
73     }
74 
75     // Disable dumping if specified by the user.
76     if (!opts.xla_detailed_logging_and_dumping()) {
77       dump_to = "";
78     }
79 
80     // If dump_to is empty, default to dumping to stdout, so long as some dump
81     // format other than dump-as-url was specified.  If the user only specified
82     // --xla_dump_hlo_as_url, then don't dump to stdout, that is likely noise
83     // they don't want.
84     if (opts.xla_dump_to().empty() && output_format_other_than_url_specified) {
85       dump_to = "-";
86     }
87 
88     // If we specified a regular expression restricting which modules to dump,
89     // respect that.
90     //
91     // If we didn't specify which modules to dump but we passed some other flag
92     // which implies dumping modules, dump all modules.
93     //
94     // Otherwise, don't dump any HLO modules.
95     if (!opts.xla_dump_hlo_module_re().empty()) {
96       // RE2 object is not copyable, and we can't capture "by move", so we
97       // resort to this hack.
98       string pattern = opts.xla_dump_hlo_module_re();
99       should_dump_module = [pattern](string_view module_name) {
100         return RE2::PartialMatch(module_name, pattern);
101       };
102     } else if (!opts.xla_dump_hlo_pass_re().empty() ||
103                !opts.xla_dump_to().empty() || output_format_specified) {
104       should_dump_module = [](string_view) { return true; };
105     } else {
106       should_dump_module = [](string_view) { return false; };
107     }
108 
109     // Initialize should_dump_pass.  This one is easy: We only dump per-pass
110     // data if the user asked for it explicitly.
111     if (!opts.xla_dump_hlo_pass_re().empty()) {
112       string pattern = opts.xla_dump_hlo_pass_re();
113       should_dump_pass = [pattern](string_view pass_name) {
114         return RE2::PartialMatch(pass_name, pattern);
115       };
116     } else {
117       should_dump_pass = [](string_view) { return false; };
118     }
119 
120     // Initialize should_dump_pipeline. If the option was not specified, dump
121     // all pipelines. Otherwise dump only those pipelines that user asked for
122     // explicitly.
123     if (!opts.xla_dump_hlo_pipeline_re().empty()) {
124       string pattern = opts.xla_dump_hlo_pipeline_re();
125       should_dump_pipeline = [pattern](string_view pipeline_name) {
126         return RE2::PartialMatch(pipeline_name, pattern);
127       };
128     } else {
129       should_dump_pipeline = [](string_view) { return true; };
130     }
131 
132     // Output dirs "sponge" and "test_undeclared_outputs_dir" (case-insensitive)
133     // have a special meaning: Dump into the directory specified by the
134     // environment variable TEST_UNDECLARED_OUTPUTS_DIR.
135     string dump_to_lower = absl::AsciiStrToLower(dump_to);
136     if (dump_to_lower == "sponge" ||
137         dump_to_lower == "test_undeclared_outputs_dir") {
138       if (!tensorflow::io::GetTestUndeclaredOutputsDir(&dump_to)) {
139         LOG(ERROR) << "--xla_dump_to=" << opts.xla_dump_to()
140                    << ", but environment variable TEST_UNDECLARED_OUTPUTS_DIR "
141                       "is not set, so cannot dump anywhere.";
142         should_dump_module = [](string_view) { return false; };
143         should_dump_pass = [](string_view) { return false; };
144         should_dump_pipeline = [](string_view) { return false; };
145       }
146     }
147   }
148 
dumping_to_stdoutxla::__anon28f990a90111::CanonicalDebugOptions149   bool dumping_to_stdout() const { return dump_to == "-"; }
150 
151   string dump_to;
152   std::function<bool(string_view module_name)> should_dump_module;
153   std::function<bool(string_view pass_name)> should_dump_pass;
154   std::function<bool(string_view pipeline_name)> should_dump_pipeline;
155 
156   // dump_ir isn't present here because this file is mostly concerned with
157   // dumping HLO.
158   bool dump_as_text;
159   bool dump_as_proto;
160   bool dump_as_dot;
161   bool dump_as_html;
162   bool dump_as_url;
163   bool dump_fusion_visualization;
164   bool dump_snapshots;
165   bool dump_include_timestamp;
166   int64 dump_max_hlo_modules;
167   bool dump_module_metadata;
168   bool dump_compress_protos;
169   bool dump_hlo_metadata;
170 };
171 
WriteStringToFile(tensorflow::Env * env,const string & fname,const tensorflow::StringPiece & data,bool compressed)172 Status WriteStringToFile(tensorflow::Env* env, const string& fname,
173                          const tensorflow::StringPiece& data, bool compressed) {
174   if (!compressed) {
175     return tensorflow::WriteStringToFile(env, fname, data);
176   }
177   std::unique_ptr<tensorflow::WritableFile> file;
178   TF_RETURN_IF_ERROR(env->NewWritableFile(fname, &file));
179   auto gz_opts = tensorflow::io::ZlibCompressionOptions::GZIP();
180   tensorflow::io::ZlibOutputBuffer gz_file(file.get(),
181                                            gz_opts.input_buffer_size,
182                                            gz_opts.output_buffer_size, gz_opts);
183   TF_RETURN_IF_ERROR(gz_file.Init());
184   TF_RETURN_IF_ERROR(gz_file.Append(data));
185   return gz_file.Close();
186 }
187 
DumpToFileInDirImpl(string_view filename,string_view contents,const CanonicalDebugOptions & opts,bool compress=false)188 absl::optional<std::string> DumpToFileInDirImpl(
189     string_view filename, string_view contents,
190     const CanonicalDebugOptions& opts, bool compress = false) {
191   if (opts.dumping_to_stdout()) {
192     LOG(ERROR) << "Refusing to write " << filename
193                << " to stdout.  Pass --xla_dump_to=<path> to write to a file.";
194     return absl::nullopt;
195   }
196 
197   if (opts.dump_to.empty()) {
198     return absl::nullopt;
199   }
200 
201   const string& dir = opts.dump_to;
202   VLOG(1) << "Dumping " << filename << " to " << dir;
203 
204   tensorflow::Env* env = tensorflow::Env::Default();
205   // Two threads can race to observe the absence of the dump directory and
206   // simultaneously try to create it, causing the "losing" thread to get a
207   // "directory already exists" error.  We can work around this by checking
208   // again whether the dir exists.
209   if (!env->IsDirectory(dir).ok()) {
210     auto status = env->RecursivelyCreateDir(dir);
211     if (!status.ok() && !env->IsDirectory(dir).ok()) {
212       LOG(ERROR) << "Could not create directory " << dir
213                  << " for dumping XLA debug data: " << status;
214       return absl::nullopt;
215     }
216   }
217 
218   // Make sure we are not going to dump more modules than the user has asked.
219   if (opts.dump_max_hlo_modules > 0) {
220     std::vector<string> matches;
221     auto pattern = tensorflow::io::JoinPath(dir, "*module_*.*");
222     auto status = env->GetMatchingPaths(pattern, &matches);
223     if (!status.ok()) {
224       LOG(ERROR) << "Could not get matching paths for pattern " << pattern
225                  << ": " << status;
226     }
227     static const LazyRE2 module_id_regex = {R"(.*module_(\d+)\..*)"};
228     absl::flat_hash_set<int64> dumped_module_ids;
229     for (const string& match : matches) {
230       int64_t dumped_module_id;
231       if (RE2::FullMatch(match, *module_id_regex, &dumped_module_id)) {
232         dumped_module_ids.insert(dumped_module_id);
233       }
234     }
235     if (dumped_module_ids.size() >= opts.dump_max_hlo_modules) {
236       int64_t module_id;
237       if (RE2::FullMatch(filename, *module_id_regex, &module_id) &&
238           !dumped_module_ids.contains(module_id)) {
239         LOG(ERROR) << "Have already dumped " << dumped_module_ids.size()
240                    << " modules, more than the limit of "
241                    << opts.dump_max_hlo_modules;
242         return absl::nullopt;
243       }
244     }
245   }
246 
247   string file_path =
248       tensorflow::io::JoinPath(dir, SanitizeFileName(string(filename)));
249   auto status = WriteStringToFile(env, file_path, contents, compress);
250   if (!status.ok()) {
251     LOG(ERROR) << "Could not write XLA debug data to " << file_path << ": "
252                << status;
253   }
254 
255   return file_path;
256 }
257 
DumpToFileInDirOrStdoutImpl(string_view filename,string_view contents,const CanonicalDebugOptions & opts)258 absl::optional<std::string> DumpToFileInDirOrStdoutImpl(
259     string_view filename, string_view contents,
260     const CanonicalDebugOptions& opts) {
261   // Dump to stdout if that's called for.
262   if (opts.dumping_to_stdout()) {
263     std::cout << "*** Begin " << filename << " ***\n"
264               << contents << "\n*** End " << filename << " ***" << std::endl;
265     return absl::nullopt;
266   }
267 
268   // Otherwise, dump to a file.
269   return DumpToFileInDirImpl(filename, contents, opts);
270 }
271 
272 // Returns full file paths of all dumps of the module.
DumpHloModuleImpl(const HloModule & module,const BufferAssignment * buffer_assn,const HloExecutionProfile * profile,string_view prefix,string_view suffix,const CanonicalDebugOptions & opts)273 std::vector<std::string> DumpHloModuleImpl(const HloModule& module,
274                                            const BufferAssignment* buffer_assn,
275                                            const HloExecutionProfile* profile,
276                                            string_view prefix,
277                                            string_view suffix,
278                                            const CanonicalDebugOptions& opts) {
279   string filename = FilenameFor(module, prefix, suffix);
280 
281   std::vector<absl::optional<std::string>> file_paths;
282 
283   if (opts.dump_as_text) {
284     HloPrintOptions print_options;
285     print_options.set_print_backend_config(true);
286     print_options.set_print_metadata(opts.dump_hlo_metadata);
287     file_paths.push_back(DumpToFileInDirOrStdoutImpl(
288         StrCat(filename, ".txt"), module.ToString(print_options), opts));
289     if (buffer_assn) {
290       file_paths.push_back(DumpToFileInDirOrStdoutImpl(
291           StrCat(filename, "-buffer-assignment.txt"),
292           StrCat(buffer_assn->ToString(), "\n\n",
293                  buffer_assn->hlo_live_range().ToString()),
294           opts));
295     }
296   }
297 
298   if (opts.dump_as_proto) {
299     HloProto module_proto =
300         buffer_assn ? MakeHloProto(module, *buffer_assn) : MakeHloProto(module);
301     string pb;
302     if (!tensorflow::SerializeToStringDeterministic(module_proto, &pb)) {
303       pb = "Failed to serialize HLO module proto.";
304     }
305     file_paths.push_back(DumpToFileInDirImpl(
306         StrCat(filename, opts.dump_compress_protos ? ".hlo.pb.gz" : ".hlo.pb"),
307         pb, opts, opts.dump_compress_protos));
308   }
309 
310   auto render_graph = [&](RenderedGraphFormat format) {
311     StatusOr<string> rendered_graph = RenderGraph(
312         *module.entry_computation(),
313         /*label=*/filename, module.config().debug_options(), format, profile);
314     if (rendered_graph.ok()) {
315       return std::move(rendered_graph).ValueOrDie();
316     }
317     return StrFormat("Error rendering graph: %s",
318                      rendered_graph.status().ToString());
319   };
320 
321   if (opts.dump_as_dot) {
322     file_paths.push_back(
323         DumpToFileInDirImpl(StrFormat("%s.dot", filename),
324                             render_graph(RenderedGraphFormat::kDot), opts));
325   }
326 
327   if (opts.dump_as_html) {
328     file_paths.push_back(
329         DumpToFileInDirImpl(StrFormat("%s.html", filename),
330                             render_graph(RenderedGraphFormat::kHtml), opts));
331   }
332 
333   if (opts.dump_fusion_visualization) {
334     for (const HloComputation* computation :
335          module.MakeNonfusionComputations()) {
336       StatusOr<string> rendered_graph = RenderGraph(
337           *computation,
338           /*label=*/absl::StrCat(filename, "_", computation->name()),
339           module.config().debug_options(),
340           RenderedGraphFormat::kFusionVisualization, profile);
341       file_paths.push_back(DumpToFileInDirImpl(
342           StrFormat("%s_%s_fusion_visualization.html", filename,
343                     computation->name()),
344           rendered_graph.ok() ? *rendered_graph
345                               : StrFormat("Error rendering graph: %s",
346                                           rendered_graph.status().ToString()),
347           opts));
348     }
349   }
350 
351   // Special case for rendering graphs as URLs.  We'll dump them to a file
352   // because why not, but we always log them to stdout as well.
353   if (opts.dump_as_url) {
354     string url = render_graph(RenderedGraphFormat::kUrl);
355     std::cout << filename << " --> " << url << std::endl;
356     if (!opts.dumping_to_stdout()) {
357       file_paths.push_back(
358           DumpToFileInDirImpl(StrFormat("%s.url", filename), url, opts));
359     }
360   }
361 
362   std::vector<std::string> dumped_file_paths;
363   for (const absl::optional<std::string>& path : file_paths) {
364     if (path.has_value()) {
365       dumped_file_paths.push_back(*path);
366     }
367   }
368   return dumped_file_paths;
369 }
370 
DumpHloModuleMetadata(const HloModuleMetadataProto & metadata,const CanonicalDebugOptions & opts,absl::flat_hash_set<int64> * dumped_module_ids)371 void DumpHloModuleMetadata(const HloModuleMetadataProto& metadata,
372                            const CanonicalDebugOptions& opts,
373                            absl::flat_hash_set<int64>* dumped_module_ids) {
374   // Return if metadata for this module has already been dumped.
375   if (!dumped_module_ids->insert(metadata.canonical_module_id()).second) {
376     return;
377   }
378   std::string filename = absl::StrFormat("module_%04d.metadata.textproto",
379                                          metadata.canonical_module_id());
380   std::string content;
381   if (tensorflow::protobuf::TextFormat::PrintToString(metadata, &content)) {
382     DumpToFileInDirImpl(filename, content, opts);
383   } else {
384     LOG(ERROR) << "Failed to convert HloModuleMetadataProto to text.";
385   }
386 }
387 
388 static tensorflow::mutex mu(tensorflow::LINKER_INITIALIZED);
389 
390 // Maps a module's unique ID to a counter indicating how many times we've dumped
391 // this module during the compilation pipeline.  This lets us keep the filenames
392 // ordered nicely.
393 //
394 // Entries added here leak forever; we have no way to GC them when a module
395 // dies.  But we only add an entry if dumping is enabled for this module, and
396 // dumping a module leaks buffer space in stdout or bytes on disk *way* faster
397 // than this hashtable leaks memory.
398 static auto& module_id_to_step_number TF_GUARDED_BY(mu) =
399     *new absl::flat_hash_map<int64, int64>();
400 
401 // Maps a module's unique ID to a timestamp indicating when we've first dumped
402 // this module during the compilation pipeline and when we first started
403 // compiling this module.  This lets us keep the filenames ordered nicely.
404 //
405 // Entries added here leak forever; we have no way to GC them when a module
406 // dies.  But we only add an entry if dumping is enabled for this module, and
407 // dumping a module leaks buffer space in stdout or bytes on disk *way* faster
408 // than this hashtable leaks memory.
409 static auto& module_id_to_timestamp TF_GUARDED_BY(mu) =
410     *new absl::flat_hash_map<int64, uint64>();
411 
StepNumberForModule(const HloModule & module)412 int64 StepNumberForModule(const HloModule& module) {
413   tensorflow::mutex_lock lock(mu);
414   return module_id_to_step_number[module.unique_id()]++;
415 }
416 
417 }  // namespace
418 
419 // Get a timestamp which we can use as a filename prefix specific to this
420 // module.
TimestampFor(const HloModule & module)421 string TimestampFor(const HloModule& module) {
422   if (!module.config().debug_options().xla_dump_include_timestamp()) {
423     return "";
424   }
425   tensorflow::mutex_lock lock(mu);
426   auto timestamp_emplace = module_id_to_timestamp.try_emplace(
427       module.unique_id(), tensorflow::Env::Default()->NowMicros());
428   return std::to_string(timestamp_emplace.first->second);
429 }
430 
FilenameFor(int unique_id,string_view module_name,string_view prefix,string_view suffix)431 static string FilenameFor(int unique_id, string_view module_name,
432                           string_view prefix, string_view suffix) {
433   string filename;
434   if (!prefix.empty()) {
435     absl::StrAppend(&filename, prefix, ".");
436   }
437   absl::StrAppendFormat(&filename, "module_%04d", unique_id);
438   if (!module_name.empty()) {
439     absl::StrAppend(&filename, ".", module_name);
440   }
441   absl::StrAppend(&filename, ".", suffix);
442   // Skip the module name if the resulting length is too long.
443   if (!module_name.empty() && filename.size() > 255) {
444     return FilenameFor(unique_id, "", prefix, suffix);
445   }
446   return filename;
447 }
448 
FilenameFor(const HloModule & module,string_view prefix,string_view suffix)449 string FilenameFor(const HloModule& module, string_view prefix,
450                    string_view suffix) {
451   return FilenameFor(module.unique_id(), module.name(), prefix, suffix);
452 }
453 
DumpToFileInDir(const HloModule & module,string_view file_prefix,string_view file_suffix,string_view contents)454 void DumpToFileInDir(const HloModule& module, string_view file_prefix,
455                      string_view file_suffix, string_view contents) {
456   DumpToFileInDirImpl(FilenameFor(module, file_prefix, file_suffix), contents,
457                       CanonicalDebugOptions(module.config().debug_options()));
458 }
459 
DumpToFileInDirOrStdout(const HloModule & module,string_view file_prefix,string_view file_suffix,string_view contents)460 void DumpToFileInDirOrStdout(const HloModule& module, string_view file_prefix,
461                              string_view file_suffix, string_view contents) {
462   DumpToFileInDirOrStdoutImpl(
463       FilenameFor(module, file_prefix, file_suffix), contents,
464       CanonicalDebugOptions(module.config().debug_options()));
465 }
466 
DumpToFileInDirOrStdout(const DebugOptions & debug_options,int unique_id,string_view module_name,string_view file_prefix,string_view file_suffix,string_view contents)467 void DumpToFileInDirOrStdout(const DebugOptions& debug_options, int unique_id,
468                              string_view module_name, string_view file_prefix,
469                              string_view file_suffix, string_view contents) {
470   DumpToFileInDirOrStdoutImpl(
471       FilenameFor(unique_id, module_name, file_prefix, file_suffix), contents,
472       CanonicalDebugOptions(debug_options));
473 }
474 
DumpExecutionOptions(const ExecutionOptions & execution_options,const DebugOptions & debug_options)475 void DumpExecutionOptions(const ExecutionOptions& execution_options,
476                           const DebugOptions& debug_options) {
477   CanonicalDebugOptions opts(debug_options);
478   tensorflow::Env* env = tensorflow::Env::Default();
479   const string& dir = opts.dump_to;
480   if (!env->IsDirectory(dir).ok()) {
481     auto status = env->RecursivelyCreateDir(dir);
482     if (!status.ok()) {
483       LOG(ERROR) << "Could not create directory " << dir
484                  << " for dumping XLA execution options: " << status;
485       return;
486     }
487   }
488   if (env->IsDirectory(dir).ok()) {
489     string filename = tensorflow::io::JoinPath(dir, "execution_options");
490     Status status;
491     if (opts.dump_as_text) {
492       status = tensorflow::WriteTextProto(env, absl::StrCat(filename, ".txt"),
493                                           execution_options);
494     } else {
495       status = tensorflow::WriteBinaryProto(env, absl::StrCat(filename, ".pb"),
496                                             execution_options);
497     }
498     if (!status.ok()) {
499       LOG(ERROR) << "Could not write XLA debug data to " << filename << ": "
500                  << status;
501     }
502   }
503 }
504 
DumpHloModuleIfEnabled(const HloModule & module,string_view name)505 void DumpHloModuleIfEnabled(const HloModule& module, string_view name) {
506   CanonicalDebugOptions opts(module.config().debug_options());
507   if (opts.should_dump_module(module.name())) {
508     DumpHloModuleImpl(module, /*buffer_assn=*/nullptr, /*profile=*/nullptr,
509                       TimestampFor(module), name, opts);
510   }
511 }
512 
DumpHloModuleIfEnabled(const HloModule & module,const BufferAssignment & buffer_assn,string_view name)513 void DumpHloModuleIfEnabled(const HloModule& module,
514                             const BufferAssignment& buffer_assn,
515                             string_view name) {
516   CanonicalDebugOptions opts(module.config().debug_options());
517   if (opts.should_dump_module(module.name())) {
518     DumpHloModuleImpl(module, &buffer_assn, /*profile=*/nullptr,
519                       TimestampFor(module), name, opts);
520   }
521 }
522 
DumpHloModuleIfEnabled(const HloModule & module,const HloExecutionProfile & profile,string_view name)523 void DumpHloModuleIfEnabled(const HloModule& module,
524                             const HloExecutionProfile& profile,
525                             string_view name) {
526   CanonicalDebugOptions opts(module.config().debug_options());
527   if (opts.should_dump_module(module.name())) {
528     DumpHloModuleImpl(module, /*buffer_assn=*/nullptr, &profile,
529                       TimestampFor(module), name, opts);
530   }
531 }
532 
DumpingEnabledForHloModule(string_view hlo_module_name,const DebugOptions & opts)533 bool DumpingEnabledForHloModule(string_view hlo_module_name,
534                                 const DebugOptions& opts) {
535   return CanonicalDebugOptions(opts).should_dump_module(hlo_module_name);
536 }
537 
DumpingToStdout(const DebugOptions & opts)538 bool DumpingToStdout(const DebugOptions& opts) {
539   return CanonicalDebugOptions(opts).dumping_to_stdout();
540 }
541 
DumpHloModuleBetweenPassesIfEnabled(string_view pipeline_name,string_view before_pass_name,string_view after_pass_name,const HloModule & module)542 std::vector<std::string> DumpHloModuleBetweenPassesIfEnabled(
543     string_view pipeline_name, string_view before_pass_name,
544     string_view after_pass_name, const HloModule& module) {
545   CanonicalDebugOptions opts(module.config().debug_options());
546   if (!opts.should_dump_module(module.name())) {
547     return {};
548   }
549 
550   if (!opts.should_dump_pass(before_pass_name) &&
551       !opts.should_dump_pass(after_pass_name)) {
552     return {};
553   }
554 
555   if (!opts.should_dump_pipeline(pipeline_name)) {
556     return {};
557   }
558 
559   int64_t step_number = StepNumberForModule(module);
560   std::string timestamp = TimestampFor(module);
561 
562   string filename_suffix =
563       StrFormat("%04d.%s.after_%s.before_%s", step_number, pipeline_name,
564                 after_pass_name, before_pass_name);
565   return DumpHloModuleImpl(module, /*buffer_assn=*/nullptr, /*profile=*/nullptr,
566                            timestamp, filename_suffix, opts);
567 }
568 
DumpHloModuleDuringPassIfEnabled(string_view pass_name,string_view step_name,const HloModule & module)569 void DumpHloModuleDuringPassIfEnabled(string_view pass_name,
570                                       string_view step_name,
571                                       const HloModule& module) {
572   CanonicalDebugOptions opts(module.config().debug_options());
573   if (!opts.should_dump_module(module.name()) ||
574       !opts.should_dump_pass(pass_name)) {
575     return;
576   }
577 
578   int64_t step_number = StepNumberForModule(module);
579   std::string timestamp = TimestampFor(module);
580 
581   string filename_suffix =
582       StrFormat("%04d.%s.%s", step_number, pass_name, step_name);
583   DumpHloModuleImpl(module, /*buffer_assn=*/nullptr, /*profile=*/nullptr,
584                     timestamp, filename_suffix, opts);
585 }
586 
DumpHloSnapshotIfEnabled(const HloModule & module,const HloSnapshot & snapshot)587 void DumpHloSnapshotIfEnabled(const HloModule& module,
588                               const HloSnapshot& snapshot) {
589   CanonicalDebugOptions opts(module.config().debug_options());
590   if (!opts.should_dump_module(module.name()) || !opts.dump_snapshots) {
591     return;
592   }
593   int64_t execution_count;
594   uint64 timestamp;
595   {
596     static auto& module_id_to_execution_count TF_GUARDED_BY(mu) =
597         *new absl::flat_hash_map<int64, int64>();
598     tensorflow::mutex_lock lock(mu);
599     execution_count = module_id_to_execution_count[module.unique_id()]++;
600     auto timestamp_emplace = module_id_to_timestamp.try_emplace(
601         module.unique_id(), tensorflow::Env::Default()->NowMicros());
602     timestamp = timestamp_emplace.first->second;
603   }
604   string filename =
605       StrCat(FilenameFor(module, std::to_string(timestamp),
606                          StrFormat("execution_%04d", execution_count)),
607              ".hlo_snapshot.pb");
608   if (opts.dumping_to_stdout()) {
609     LOG(ERROR) << "Refusing to write HLO snapshot proto for " << filename
610                << " to stdout.  Pass --xla_dump_to=<path> to write to a file.";
611     return;
612   }
613   string pb;
614   if (!tensorflow::SerializeToStringDeterministic(snapshot, &pb)) {
615     LOG(ERROR) << "Failed to serialize HLO snapshot proto " << filename;
616   }
617   DumpToFileInDirImpl(filename, pb, opts);
618 }
619 
DumpHloSnapshotIfEnabled(const HloSnapshot & snapshot,const DebugOptions & opts)620 void DumpHloSnapshotIfEnabled(const HloSnapshot& snapshot,
621                               const DebugOptions& opts) {
622   CanonicalDebugOptions canonical_opts(opts);
623   string name = snapshot.hlo().hlo_module().name();
624   if (!canonical_opts.should_dump_module(name) ||
625       !canonical_opts.dump_snapshots) {
626     return;
627   }
628 
629   // We don't have a unique id for an HloSnapshot, so in this overload we just
630   // have to use its name.
631   int64_t execution_count;
632   {
633     static auto& module_name_to_execution_count TF_GUARDED_BY(mu) =
634         *new absl::flat_hash_map<string, int64>();
635     tensorflow::mutex_lock lock(mu);
636     execution_count = module_name_to_execution_count[name]++;
637   }
638   string filename = StrFormat("module_%s.execution_%04d.hlo_snapshot.pb", name,
639                               execution_count);
640   if (canonical_opts.dumping_to_stdout()) {
641     LOG(ERROR) << "Refusing to write HLO snapshot proto for " << filename
642                << " to stdout.  Pass --xla_dump_to=<path> to write to a file.";
643     return;
644   }
645   string pb;
646   if (!tensorflow::SerializeToStringDeterministic(snapshot, &pb)) {
647     LOG(ERROR) << "Failed to serialize HLO snapshot proto " << filename;
648   }
649   DumpToFileInDirImpl(filename, pb, canonical_opts);
650 }
651 
DumpHloModuleMetadataIfEnabled(const std::vector<HloModule * > & modules)652 void DumpHloModuleMetadataIfEnabled(const std::vector<HloModule*>& modules) {
653   absl::flat_hash_set<int64> dumped_module_ids;
654   for (const HloModule* module : modules) {
655     CanonicalDebugOptions opts(module->config().debug_options());
656     if (!opts.dump_module_metadata) {
657       continue;
658     }
659     DumpHloModuleMetadata(module->metadata().proto(), opts, &dumped_module_ids);
660     const absl::optional<HloModuleMetadataProto>& prepartitioning_metadata =
661         module->metadata().prepartitioning_metadata();
662     if (prepartitioning_metadata.has_value()) {
663       DumpHloModuleMetadata(*prepartitioning_metadata, opts,
664                             &dumped_module_ids);
665     }
666   }
667 }
668 
669 }  // namespace xla
670