1 /* Copyright 2017 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/core/profiler/rpc/client/save_profile.h"
17
18 #include <memory>
19 #include <sstream>
20 #include <string>
21 #include <vector>
22
23 #include "absl/strings/match.h"
24 #include "absl/strings/str_cat.h"
25 #include "absl/strings/str_replace.h"
26 #include "absl/strings/string_view.h"
27 #include "absl/strings/strip.h"
28 #include "absl/time/clock.h"
29 #include "absl/time/time.h"
30 #include "tensorflow/core/lib/io/zlib_compression_options.h"
31 #include "tensorflow/core/lib/io/zlib_outputbuffer.h"
32 #include "tensorflow/core/platform/env.h"
33 #include "tensorflow/core/platform/errors.h"
34 #include "tensorflow/core/platform/file_system.h"
35 #include "tensorflow/core/platform/logging.h"
36 #include "tensorflow/core/platform/status.h"
37 #include "tensorflow/core/profiler/profiler_service.pb.h"
38 #include "tensorflow/core/profiler/utils/file_system_utils.h"
39
40 // Windows.h #defines ERROR, but it is also used in
41 // tensorflow/core/util/event.proto
42 #undef ERROR
43 #include "tensorflow/core/util/events_writer.h"
44
45 namespace tensorflow {
46 namespace profiler {
47 namespace {
48
49
50 constexpr char kProtoTraceFileName[] = "trace";
51 constexpr char kTfStatsHelperSuffix[] = "tf_stats_helper_result";
52
DumpToolData(absl::string_view run_dir,absl::string_view host,const ProfileToolData & tool,std::ostream * os)53 Status DumpToolData(absl::string_view run_dir, absl::string_view host,
54 const ProfileToolData& tool, std::ostream* os) {
55 // Don't save the intermediate results for combining the per host tool data.
56 if (absl::EndsWith(tool.name(), kTfStatsHelperSuffix)) return Status::OK();
57 std::string host_prefix = host.empty() ? "" : absl::StrCat(host, ".");
58 std::string path =
59 ProfilerJoinPath(run_dir, absl::StrCat(host_prefix, tool.name()));
60 TF_RETURN_IF_ERROR(WriteStringToFile(Env::Default(), path, tool.data()));
61 if (os) {
62 *os << "Dumped tool data for " << tool.name() << " to " << path
63 << std::endl;
64 }
65 return Status::OK();
66 }
67
WriteGzippedDataToFile(const std::string & filepath,const std::string & data)68 Status WriteGzippedDataToFile(const std::string& filepath,
69 const std::string& data) {
70 std::unique_ptr<WritableFile> file;
71 TF_RETURN_IF_ERROR(Env::Default()->NewWritableFile(filepath, &file));
72 io::ZlibCompressionOptions options = io::ZlibCompressionOptions::GZIP();
73 io::ZlibOutputBuffer buffer(file.get(), options.input_buffer_size,
74 options.output_buffer_size, options);
75 TF_RETURN_IF_ERROR(buffer.Init());
76 TF_RETURN_IF_ERROR(buffer.Append(data));
77 TF_RETURN_IF_ERROR(buffer.Close());
78 TF_RETURN_IF_ERROR(file->Close());
79 return Status::OK();
80 }
81
GetOrCreateRunDir(const std::string & repository_root,const std::string & run,std::string * run_dir,std::ostream * os)82 Status GetOrCreateRunDir(const std::string& repository_root,
83 const std::string& run, std::string* run_dir,
84 std::ostream* os) {
85 // Creates a directory to <repository_root>/<run>/.
86 *run_dir = ProfilerJoinPath(repository_root, run);
87 *os << "Creating directory: " << *run_dir;
88 TF_RETURN_IF_ERROR(Env::Default()->RecursivelyCreateDir(*run_dir));
89 return Status::OK();
90 }
91 } // namespace
92
GetTensorBoardProfilePluginDir(const std::string & logdir)93 std::string GetTensorBoardProfilePluginDir(const std::string& logdir) {
94 constexpr char kPluginName[] = "plugins";
95 constexpr char kProfileName[] = "profile";
96 return ProfilerJoinPath(logdir, kPluginName, kProfileName);
97 }
98
MaybeCreateEmptyEventFile(const std::string & logdir)99 Status MaybeCreateEmptyEventFile(const std::string& logdir) {
100 // Suffix for an empty event file. it should be kept in sync with
101 // _EVENT_FILE_SUFFIX in tensorflow/python/eager/profiler.py.
102 constexpr char kProfileEmptySuffix[] = ".profile-empty";
103 TF_RETURN_IF_ERROR(Env::Default()->RecursivelyCreateDir(logdir));
104
105 std::vector<std::string> children;
106 TF_RETURN_IF_ERROR(Env::Default()->GetChildren(logdir, &children));
107 for (const std::string& child : children) {
108 if (absl::EndsWith(child, kProfileEmptySuffix)) {
109 return Status::OK();
110 }
111 }
112 EventsWriter event_writer(ProfilerJoinPath(logdir, "events"));
113 return event_writer.InitWithSuffix(kProfileEmptySuffix);
114 }
115
SaveProfile(const std::string & repository_root,const std::string & run,const std::string & host,const ProfileResponse & response,std::ostream * os)116 Status SaveProfile(const std::string& repository_root, const std::string& run,
117 const std::string& host, const ProfileResponse& response,
118 std::ostream* os) {
119 if (response.tool_data().empty()) return Status::OK();
120 std::string run_dir;
121 TF_RETURN_IF_ERROR(GetOrCreateRunDir(repository_root, run, &run_dir, os));
122 // Windows file names do not support colons.
123 std::string hostname = absl::StrReplaceAll(host, {{":", "_"}});
124 for (const auto& tool_data : response.tool_data()) {
125 TF_RETURN_IF_ERROR(DumpToolData(run_dir, hostname, tool_data, os));
126 }
127 return Status::OK();
128 }
129
SaveGzippedToolData(const std::string & repository_root,const std::string & run,const std::string & host,const std::string & tool_name,const std::string & data)130 Status SaveGzippedToolData(const std::string& repository_root,
131 const std::string& run, const std::string& host,
132 const std::string& tool_name,
133 const std::string& data) {
134 std::string run_dir;
135 std::stringstream ss;
136 Status status = GetOrCreateRunDir(repository_root, run, &run_dir, &ss);
137 LOG(INFO) << ss.str();
138 TF_RETURN_IF_ERROR(status);
139 std::string host_prefix = host.empty() ? "" : absl::StrCat(host, ".");
140 std::string path =
141 ProfilerJoinPath(run_dir, absl::StrCat(host_prefix, tool_name));
142 TF_RETURN_IF_ERROR(WriteGzippedDataToFile(path, data));
143 LOG(INFO) << "Dumped gzipped tool data for " << tool_name << " to " << path;
144 return Status::OK();
145 }
146
GetCurrentTimeStampAsString()147 std::string GetCurrentTimeStampAsString() {
148 return absl::FormatTime("%E4Y_%m_%d_%H_%M_%S", absl::Now(),
149 absl::LocalTimeZone());
150 }
151
152 } // namespace profiler
153 } // namespace tensorflow
154