1 /**
2 * Copyright 2020-2023 Huawei Technologies Co., Ltd
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef MINDSPORE_CCSRC_BACKEND_SESSION_KERNEL_BUILD_CLIENT_H_
18 #define MINDSPORE_CCSRC_BACKEND_SESSION_KERNEL_BUILD_CLIENT_H_
19
20 #include <vector>
21 #include <string>
22 #include <cstring>
23 #include <memory>
24 #include <mutex>
25
26 #include "include/common/duplex_pipe.h"
27 #include "include/backend/visible.h"
28 #include "utils/log_adapter.h"
29 #include "utils/ms_context.h"
30
31 namespace mindspore {
32 namespace kernel {
33 void ReplaceStr(std::string *dest, const std::string &replace, char new_char);
34
35 constexpr inline static int kBufferSize = 4096;
36 constexpr inline static auto kEnv = "python";
37 // The TAG as prefix of real command from remote.
38 constexpr inline static auto kTag = "[~]";
39 BACKEND_EXPORT std::string GetPyExe();
40 BACKEND_EXPORT std::string GetCmdResult();
41
42 class BACKEND_EXPORT KernelBuildClient {
43 public:
44 // Send Finish request to server
45 constexpr inline static auto kFinish = "FINISH";
46 constexpr inline static auto kCompilerStart = "AKG/START";
47 constexpr inline static auto kCompilerData = "AKG/DATA";
48 constexpr inline static auto kCompilerAttr = "AKG/ATTR";
49 constexpr inline static auto kCompilerWait = "AKG/WAIT";
50 // Receive the response from server
51 constexpr inline static auto kAck = "ACK";
52 constexpr inline static auto kErr = "ERR";
53 constexpr inline static auto kTrue = "True";
54 constexpr inline static auto kSuccess = "Success";
55
56 // Revert \n, \r, [space].
57 constexpr inline static auto kLF = "[LF]";
58 constexpr inline static auto kCR = "[CR]";
59 constexpr inline static auto kSP = "[SP]";
60
61 virtual std::string GetEnv() = 0;
62 virtual std::string GetScript() = 0;
63
Open()64 void Open() {
65 if (!init_) {
66 // Exception's thrown if open failed
67 if (dp_->Open({GetEnv(), GetScript()}, true) != -1) {
68 dp_->SetFinalizeCallback(std::make_shared<std::function<void()>>([this]() { Close(); }));
69 init_ = true;
70 }
71 }
72 }
Close()73 void Close() noexcept {
74 if (init_) {
75 dp_->Close();
76 init_ = false;
77 }
78 }
IsOpen()79 bool IsOpen() const { return init_; }
80
81 // Send a request and fetch its response
SendRequest(const std::string & data)82 std::string SendRequest(const std::string &data) {
83 std::lock_guard<std::mutex> locker(mutex_);
84 Request(data);
85 return Response();
86 }
Request(const std::string & req)87 void Request(const std::string &req) {
88 if (!init_) {
89 MS_LOG(EXCEPTION) << "Try to send request before Open(). For more details, please refer to this FAQ: "
90 << "https://www.mindspore.cn/tutorials/zh-CN/master/advanced/error_analysis/mindrt_debug.html";
91 }
92 *dp_ << req;
93 }
Response()94 std::string Response() {
95 if (!init_) {
96 MS_LOG(EXCEPTION) << "Try to get response before Open(). For more details, please refer to this FAQ: "
97 << "https://www.mindspore.cn/tutorials/zh-CN/master/advanced/error_analysis/mindrt_debug.html";
98 }
99 std::string res;
100 *dp_ >> res;
101 // Filter out the interference
102 if (res.empty()) {
103 MS_LOG(EXCEPTION) << "Response is empty. For more details, please refer to this FAQ: "
104 << "https://www.mindspore.cn/tutorials/zh-CN/master/advanced/error_analysis/mindrt_debug.html";
105 }
106 auto start = res.find(kTag);
107 if (start == std::string::npos) {
108 MS_LOG(EXCEPTION) << "Response seems incorrect, res: " << res;
109 }
110 auto pos = start + std::strlen(kTag);
111 if (pos > res.size()) { // Safe check for codedex
112 MS_LOG(EXCEPTION) << "Response seems incorrect, res(" << res.size() << "): {" << res << "}, start: " << start;
113 }
114 res = res.substr(pos);
115 // Revert the line feed and space
116 if (res != kSuccess && res != kAck && res != kErr && res != kTrue) {
117 ReplaceStr(&res, kLF, '\n');
118 ReplaceStr(&res, kSP, ' ');
119 }
120 return res;
121 }
122
123 // Run Kernel Compiler building.
124 bool CompilerStart(int process_num, int wait_time, const std::string &platform);
125 bool CompilerSendAttr(const std::string &attr);
126 bool CompilerSendData(const std::vector<std::string> &jsons);
127 bool CompilerWait();
128
129 protected:
KernelBuildClient()130 KernelBuildClient() : init_(false), dp_(std::make_shared<DuplexPipe>()) {}
131 virtual ~KernelBuildClient() = default;
132
133 private:
134 // Support multi-thread.
135 std::mutex mutex_;
136 bool init_;
137 std::shared_ptr<DuplexPipe> dp_;
138 };
139
GetCmdResult(const std::string & cmd)140 static std::string GetCmdResult(const std::string &cmd) {
141 #ifdef _MSC_VER
142 FILE *fpipe = _popen(cmd.c_str(), "r");
143 #else
144 FILE *fpipe = popen(cmd.c_str(), "r");
145 #endif
146 if (fpipe == nullptr) {
147 MS_LOG(EXCEPTION) << "popen failed, errno: " << errno;
148 }
149 bool start = false;
150 std::string result;
151 char buf[kBufferSize];
152 while (std::fgets(buf, sizeof(buf), fpipe) != nullptr) {
153 auto len = std::strlen(buf);
154 if (len == 0 || len >= kBufferSize) {
155 // Safe check for codedex
156 // Should never reach here
157 MS_LOG(EXCEPTION) << "fgets() failed, len: " << len << ", errno: " << errno;
158 }
159 if (std::strncmp(buf, kTag, std::strlen(kTag)) == 0) {
160 start = true;
161 }
162 // Filter with 'kTAG' and '\n'
163 if (start) {
164 bool line_end = buf[len - 1] == '\n';
165 (void)result.append(buf, line_end ? len - 1 : len);
166 if (line_end) {
167 break;
168 }
169 }
170 }
171 #ifdef _MSC_VER
172 (void)_pclose(fpipe);
173 #else
174 (void)pclose(fpipe);
175 #endif
176 return result;
177 }
178
GetScriptFilePath(const std::string & cmd_env,const std::string & cmd_script,const std::string & server_script)179 static std::string GetScriptFilePath(const std::string &cmd_env, const std::string &cmd_script,
180 const std::string &server_script) {
181 auto ms_context = MsContext::GetInstance();
182 MS_EXCEPTION_IF_NULL(ms_context);
183 auto server_dir = ms_context->get_param<std::string>(MS_CTX_KERNEL_BUILD_SERVER_DIR);
184 if (!server_dir.empty()) {
185 return server_dir + server_script;
186 }
187
188 std::string cmd = cmd_env;
189 (void)cmd.append(1, ' ').append(cmd_script);
190 auto result = GetCmdResult(cmd);
191 const std::string py_suffix = ".py";
192 if (result.empty() || result.rfind(py_suffix) != (result.length() - py_suffix.length())) {
193 MS_LOG(EXCEPTION) << "py file seems incorrect, result: {" << result << "}";
194 }
195 if (strlen(kTag) > result.size()) { // Safe check for codedex
196 MS_LOG(EXCEPTION) << "result size seems incorrect, result(" << result.size() << "): {" << result << "}";
197 }
198 result = result.substr(strlen(kTag));
199 MS_LOG(DEBUG) << "result: " << result;
200 return result;
201 }
202
203 class BACKEND_EXPORT AkgKernelBuildClient : public KernelBuildClient {
204 public:
205 // Server configure
206 constexpr inline static auto kGetPathScript =
207 "-c "
208 "\""
209 "import pkgutil;"
210 "path = pkgutil"
211 ".get_loader(\\\"mindspore._extends.remote.kernel_build_server_akg\\\")" // Server module name
212 ".get_filename();"
213 "print('[~]' + path)"
214 "\"";
215
216 constexpr inline static auto kServerScript = "kernel_build_server_akg.py";
217
218 static AkgKernelBuildClient &Instance();
219
GetEnv()220 std::string GetEnv() override { return GetPyExe(); }
221
GetScript()222 std::string GetScript() override {
223 auto env = GetPyExe();
224 return GetScriptFilePath(env, kGetPathScript, kServerScript);
225 }
226
227 AkgKernelBuildClient(const AkgKernelBuildClient &) = delete;
228 AkgKernelBuildClient &operator=(const AkgKernelBuildClient &) = delete;
229
230 AkgKernelBuildClient(AkgKernelBuildClient &&) = delete;
231 AkgKernelBuildClient &operator=(AkgKernelBuildClient &&) = delete;
232
233 protected:
~AkgKernelBuildClient()234 ~AkgKernelBuildClient() override { Close(); }
235
236 private:
AkgKernelBuildClient()237 AkgKernelBuildClient() { Open(); }
238 };
239
240 class BACKEND_EXPORT AkgV2KernelBuildClient : public KernelBuildClient {
241 public:
242 // Server configure
243 constexpr inline static auto kGetPathScript =
244 "-c "
245 "\""
246 "import pkgutil;"
247 "path = pkgutil"
248 ".get_loader(\\\"mindspore._extends.remote.kernel_build_server_akg_v2\\\")" // Server module name
249 ".get_filename();"
250 "print('[~]' + path)"
251 "\"";
252
253 constexpr inline static auto kServerScript = "kernel_build_server_akg_v2.py";
254
255 static AkgV2KernelBuildClient &Instance();
256
GetEnv()257 std::string GetEnv() override { return GetPyExe(); }
258
GetScript()259 std::string GetScript() override {
260 auto env = GetPyExe();
261 return GetScriptFilePath(env, kGetPathScript, kServerScript);
262 }
263
264 AkgV2KernelBuildClient(const AkgV2KernelBuildClient &) = delete;
265 AkgV2KernelBuildClient &operator=(const AkgV2KernelBuildClient &) = delete;
266
267 AkgV2KernelBuildClient(AkgV2KernelBuildClient &&) = delete;
268 AkgV2KernelBuildClient &operator=(AkgV2KernelBuildClient &&) = delete;
269
270 protected:
~AkgV2KernelBuildClient()271 ~AkgV2KernelBuildClient() override { Close(); }
272
273 private:
AkgV2KernelBuildClient()274 AkgV2KernelBuildClient() { Open(); }
275 };
276 } // namespace kernel
277 } // namespace mindspore
278
279 #endif // MINDSPORE_CCSRC_BACKEND_SESSION_KERNEL_BUILD_CLIENT_H_
280