From 0b3129fc6b8c3daea8c37c7c82bd422237f05246 Mon Sep 17 00:00:00 2001 From: Artur Wojcik Date: Tue, 3 Oct 2023 23:18:13 +0200 Subject: [PATCH 1/3] port 'process' class to Windows --- src/CMakeLists.txt | 6 +- src/process_win32.cpp | 213 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 src/process_win32.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a00242f4de8..04c6ca79615 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -75,7 +75,6 @@ add_library(migraphx pass_manager.cpp permutation.cpp preallocate_param.cpp - process.cpp program.cpp propagate_constant.cpp promote_literals.cpp @@ -104,6 +103,11 @@ add_library(migraphx value.cpp verify_args.cpp ) +if(WIN32) + target_sources(migraphx PRIVATE process_win32.cpp) +else() + target_sources(migraphx PRIVATE process.cpp) +endif() configure_file(version.h.in include/migraphx/version.h) rocm_set_soversion(migraphx ${MIGRAPHX_SO_VERSION}) function(register_migraphx_ops) diff --git a/src/process_win32.cpp b/src/process_win32.cpp new file mode 100644 index 00000000000..7945d92b4c6 --- /dev/null +++ b/src/process_win32.cpp @@ -0,0 +1,213 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015-2023 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include + +// cppcheck-suppress definePrefix +#define WIN32_LEAN_AND_MEAN +#include + +namespace migraphx { +inline namespace MIGRAPHX_INLINE_NS { + +MIGRAPHX_DECLARE_ENV_VAR(MIGRAPHX_TRACE_CMD_EXECUTE) + +#define MIGRAPHX_PROCESS_BUFSIZE 4096 + +class pipe +{ + public: + explicit pipe(bool inherit_handle = true) + { + SECURITY_ATTRIBUTES attrs; + attrs.nLength = sizeof(SECURITY_ATTRIBUTES); + attrs.bInheritHandle = inherit_handle ? TRUE : FALSE; + attrs.lpSecurityDescriptor = nullptr; + + if(CreatePipe(&hRead_, &hWrite_, &attrs, 0) == FALSE) + throw GetLastError(); + + if(SetHandleInformation(&hRead_, HANDLE_FLAG_INHERIT, 0) == FALSE) + throw GetLastError(); + } + + ~pipe() + { + close_write_handle(); + close_read_handle(); + } + + HANDLE get_read_handle() const { return hRead_; } + + void close_read_handle() + { + if(hRead_ != nullptr) + { + CloseHandle(hRead_); + hRead_ = nullptr; + } + } + + HANDLE get_write_handle() const { return hWrite_; } + + void close_write_handle() + { + if(hWrite_ != nullptr) + { + CloseHandle(hWrite_); + hWrite_ = nullptr; + } + } + + private: + HANDLE hWrite_{nullptr}, hRead_{nullptr}; +}; + +int exec(const std::string& cmd) +{ + try + { + if(enabled(MIGRAPHX_TRACE_CMD_EXECUTE{})) + std::cout << cmd << std::endl; + + pipe stdin_{}, stdout_{}; + + STARTUPINFO info; + PROCESS_INFORMATION processInfo; + + ZeroMemory(&info, sizeof(STARTUPINFO)); + info.cb = sizeof(STARTUPINFO); + info.hStdError = stdout_.get_write_handle(); + info.hStdOutput = stdout_.get_write_handle(); + info.hStdInput = stdin_.get_read_handle(); + info.dwFlags |= STARTF_USESTDHANDLES; + + ZeroMemory(&processInfo, sizeof(processInfo)); + + LPSTR lpCmdLn{const_cast(cmd.c_str())}; + + BOOL bSuccess = CreateProcess( + nullptr, lpCmdLn, nullptr, nullptr, TRUE, 0, nullptr, nullptr, &info, &processInfo); + + if(bSuccess == FALSE) + return GetLastError(); + + DWORD dwRead, dwWritten; + TCHAR chBuf[MIGRAPHX_PROCESS_BUFSIZE]; + HANDLE hStdOut{GetStdHandle(STD_OUTPUT_HANDLE)}; + + for(;;) + { + BOOL bRead = ReadFile( + stdout_.get_read_handle(), chBuf, MIGRAPHX_PROCESS_BUFSIZE, &dwRead, nullptr); + + if(bRead == FALSE) + { + if(GetLastError() != ERROR_MORE_DATA) + break; + } + + if(dwRead == 0) + break; + + BOOL bWrite = WriteFile(hStdOut, chBuf, dwRead, &dwWritten, nullptr); + + if(bWrite == FALSE) + break; + } + + WaitForSingleObject(processInfo.hProcess, INFINITE); + + DWORD status{}; + GetExitCodeProcess(processInfo.hProcess, &status); + + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + + return static_cast(status); + } + // cppcheck-suppress catchExceptionByValue + catch(DWORD lastError) + { + return lastError; + } +} + +struct process_impl +{ + std::string command{}; + fs::path cwd{}; + + std::string get_command() const + { + std::string result; + if(not cwd.empty()) + result += "cd " + cwd.string() + "; "; + result += command; + return result; + } + + template + void check_exec(Ts&&... xs) const + { + int ec = migraphx::exec(std::forward(xs)...); + if(ec != 0) + MIGRAPHX_THROW("Command " + get_command() + " exited with status " + + std::to_string(ec)); + } +}; + +process::process(const std::string& cmd) : impl(std::make_unique()) +{ + impl->command = cmd; +} + +process::process(process&&) noexcept = default; + +process& process::operator=(process rhs) +{ + std::swap(impl, rhs.impl); + return *this; +} + +process::~process() noexcept = default; + +process& process::cwd(const fs::path& p) +{ + impl->cwd = p; + return *this; +} + +void process::exec() { impl->check_exec(impl->get_command()); } + +void process::write(std::function pipe_in) +{ + impl->check_exec(impl->get_command()); +} + +} // namespace MIGRAPHX_INLINE_NS +} // namespace migraphx From e834074f3e2463ef48f751e4c4b2b9ef1810ce45 Mon Sep 17 00:00:00 2001 From: Artur Wojcik Date: Sun, 8 Oct 2023 19:44:08 +0200 Subject: [PATCH 2/3] incorporate review feedback --- src/CMakeLists.txt | 6 +- src/process.cpp | 168 ++++++++++++++++++++++++++++++++- src/process_win32.cpp | 213 ------------------------------------------ 3 files changed, 168 insertions(+), 219 deletions(-) delete mode 100644 src/process_win32.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 04c6ca79615..a00242f4de8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -75,6 +75,7 @@ add_library(migraphx pass_manager.cpp permutation.cpp preallocate_param.cpp + process.cpp program.cpp propagate_constant.cpp promote_literals.cpp @@ -103,11 +104,6 @@ add_library(migraphx value.cpp verify_args.cpp ) -if(WIN32) - target_sources(migraphx PRIVATE process_win32.cpp) -else() - target_sources(migraphx PRIVATE process.cpp) -endif() configure_file(version.h.in include/migraphx/version.h) rocm_set_soversion(migraphx ${MIGRAPHX_SO_VERSION}) function(register_migraphx_ops) diff --git a/src/process.cpp b/src/process.cpp index 06d7fe2c324..e5b3bceda49 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -26,13 +26,23 @@ #include #include #include +#include + +#ifdef _WIN32 +// cppcheck-suppress definePrefix +#define WIN32_LEAN_AND_MEAN +#include +#else #include +#endif namespace migraphx { inline namespace MIGRAPHX_INLINE_NS { MIGRAPHX_DECLARE_ENV_VAR(MIGRAPHX_TRACE_CMD_EXECUTE) +#ifndef _WIN32 + std::function redirect_to(std::ostream& os) { return [&](const char* x) { os << x; }; @@ -74,6 +84,155 @@ int exec(const std::string& cmd, std::function std_in) }); } +#else + +constexpr std::size_t MIGRAPHX_PROCESS_BUFSIZE = 4096; + +class pipe +{ + public: + explicit pipe(bool inherit_handle = true) + { + SECURITY_ATTRIBUTES attrs; + attrs.nLength = sizeof(SECURITY_ATTRIBUTES); + attrs.bInheritHandle = inherit_handle ? TRUE : FALSE; + attrs.lpSecurityDescriptor = nullptr; + + if(CreatePipe(&m_read, &m_write, &attrs, 0) == FALSE) + throw GetLastError(); + + if(SetHandleInformation(&m_read, HANDLE_FLAG_INHERIT, 0) == FALSE) + throw GetLastError(); + } + + pipe(const pipe&) = delete; + pipe& operator=(const pipe&) = delete; + + pipe(pipe&&) = default; + + ~pipe() + { + CloseHandle(m_read); + m_read = nullptr; + CloseHandle(m_write); + m_write = nullptr; + } + + std::optional> read(LPVOID buffer, DWORD length) const + { + DWORD bytes_read; + if(ReadFile(m_read, buffer, length, &bytes_read, nullptr) == FALSE) + { + DWORD error{GetLastError()}; + if(error != ERROR_MORE_DATA) + { + return std::nullopt; + } + return {{true, bytes_read}}; + } + return {{false, bytes_read}}; + } + + HANDLE get_read_handle() const { return m_read; } + + bool write(LPCVOID buffer, DWORD length) const + { + DWORD bytes_written; + return WriteFile(m_write, buffer, length, &bytes_written, nullptr) == TRUE; + } + + HANDLE get_write_handle() const { return m_write; } + + private: + HANDLE m_write = nullptr, m_read = nullptr; +}; + +template +int exec(const std::string& cmd, F f) +{ + try + { + if(enabled(MIGRAPHX_TRACE_CMD_EXECUTE{})) + std::cout << cmd << std::endl; + + STARTUPINFO info; + PROCESS_INFORMATION process_info; + + pipe in{}, out{}; + + ZeroMemory(&info, sizeof(STARTUPINFO)); + info.cb = sizeof(STARTUPINFO); + info.hStdError = out.get_write_handle(); + info.hStdOutput = out.get_write_handle(); + info.hStdInput = in.get_read_handle(); + info.dwFlags |= STARTF_USESTDHANDLES; + + ZeroMemory(&process_info, sizeof(process_info)); + + if(CreateProcess(nullptr, + const_cast(cmd.c_str()), + nullptr, + nullptr, + TRUE, + 0, + nullptr, + nullptr, + &info, + &process_info) == FALSE) + { + return GetLastError(); + } + + f(in, out); + + WaitForSingleObject(process_info.hProcess, INFINITE); + + DWORD status{}; + GetExitCodeProcess(process_info.hProcess, &status); + + CloseHandle(process_info.hProcess); + CloseHandle(process_info.hThread); + + return static_cast(status); + } + // cppcheck-suppress catchExceptionByValue + catch(DWORD last_error) + { + return last_error; + } +} + +int exec(const std::string& cmd) +{ + TCHAR buffer[MIGRAPHX_PROCESS_BUFSIZE]; + HANDLE std_out{GetStdHandle(STD_OUTPUT_HANDLE)}; + return (std_out == nullptr || std_out == INVALID_HANDLE_VALUE) + ? GetLastError() + : exec(cmd, [&](const pipe&, const pipe& out) { + for(;;) + { + if(auto result = out.read(buffer, MIGRAPHX_PROCESS_BUFSIZE)) + { + auto [more_data, bytes_read] = *result; + if(!more_data || bytes_read == 0) + break; + DWORD written; + if(WriteFile(std_out, buffer, bytes_read, &written, nullptr) == FALSE) + break; + } + } + }); +} + +int exec(const std::string& cmd, std::function std_in) +{ + return exec(cmd, [&](const pipe& in, const pipe&) { + std_in([&](const char* buffer, std::size_t n) { in.write(buffer, n); }); + }); +} + +#endif + struct process_impl { std::string command{}; @@ -119,7 +278,14 @@ process& process::cwd(const fs::path& p) return *this; } -void process::exec() { impl->check_exec(impl->get_command(), redirect_to(std::cout)); } +void process::exec() +{ +#ifndef _WIN32 + impl->check_exec(impl->get_command(), redirect_to(std::cout)); +#else + impl->check_exec(impl->get_command()); +#endif +} void process::write(std::function pipe_in) { diff --git a/src/process_win32.cpp b/src/process_win32.cpp deleted file mode 100644 index 7945d92b4c6..00000000000 --- a/src/process_win32.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015-2023 Advanced Micro Devices, Inc. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include -#include -#include -#include -#include - -// cppcheck-suppress definePrefix -#define WIN32_LEAN_AND_MEAN -#include - -namespace migraphx { -inline namespace MIGRAPHX_INLINE_NS { - -MIGRAPHX_DECLARE_ENV_VAR(MIGRAPHX_TRACE_CMD_EXECUTE) - -#define MIGRAPHX_PROCESS_BUFSIZE 4096 - -class pipe -{ - public: - explicit pipe(bool inherit_handle = true) - { - SECURITY_ATTRIBUTES attrs; - attrs.nLength = sizeof(SECURITY_ATTRIBUTES); - attrs.bInheritHandle = inherit_handle ? TRUE : FALSE; - attrs.lpSecurityDescriptor = nullptr; - - if(CreatePipe(&hRead_, &hWrite_, &attrs, 0) == FALSE) - throw GetLastError(); - - if(SetHandleInformation(&hRead_, HANDLE_FLAG_INHERIT, 0) == FALSE) - throw GetLastError(); - } - - ~pipe() - { - close_write_handle(); - close_read_handle(); - } - - HANDLE get_read_handle() const { return hRead_; } - - void close_read_handle() - { - if(hRead_ != nullptr) - { - CloseHandle(hRead_); - hRead_ = nullptr; - } - } - - HANDLE get_write_handle() const { return hWrite_; } - - void close_write_handle() - { - if(hWrite_ != nullptr) - { - CloseHandle(hWrite_); - hWrite_ = nullptr; - } - } - - private: - HANDLE hWrite_{nullptr}, hRead_{nullptr}; -}; - -int exec(const std::string& cmd) -{ - try - { - if(enabled(MIGRAPHX_TRACE_CMD_EXECUTE{})) - std::cout << cmd << std::endl; - - pipe stdin_{}, stdout_{}; - - STARTUPINFO info; - PROCESS_INFORMATION processInfo; - - ZeroMemory(&info, sizeof(STARTUPINFO)); - info.cb = sizeof(STARTUPINFO); - info.hStdError = stdout_.get_write_handle(); - info.hStdOutput = stdout_.get_write_handle(); - info.hStdInput = stdin_.get_read_handle(); - info.dwFlags |= STARTF_USESTDHANDLES; - - ZeroMemory(&processInfo, sizeof(processInfo)); - - LPSTR lpCmdLn{const_cast(cmd.c_str())}; - - BOOL bSuccess = CreateProcess( - nullptr, lpCmdLn, nullptr, nullptr, TRUE, 0, nullptr, nullptr, &info, &processInfo); - - if(bSuccess == FALSE) - return GetLastError(); - - DWORD dwRead, dwWritten; - TCHAR chBuf[MIGRAPHX_PROCESS_BUFSIZE]; - HANDLE hStdOut{GetStdHandle(STD_OUTPUT_HANDLE)}; - - for(;;) - { - BOOL bRead = ReadFile( - stdout_.get_read_handle(), chBuf, MIGRAPHX_PROCESS_BUFSIZE, &dwRead, nullptr); - - if(bRead == FALSE) - { - if(GetLastError() != ERROR_MORE_DATA) - break; - } - - if(dwRead == 0) - break; - - BOOL bWrite = WriteFile(hStdOut, chBuf, dwRead, &dwWritten, nullptr); - - if(bWrite == FALSE) - break; - } - - WaitForSingleObject(processInfo.hProcess, INFINITE); - - DWORD status{}; - GetExitCodeProcess(processInfo.hProcess, &status); - - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - - return static_cast(status); - } - // cppcheck-suppress catchExceptionByValue - catch(DWORD lastError) - { - return lastError; - } -} - -struct process_impl -{ - std::string command{}; - fs::path cwd{}; - - std::string get_command() const - { - std::string result; - if(not cwd.empty()) - result += "cd " + cwd.string() + "; "; - result += command; - return result; - } - - template - void check_exec(Ts&&... xs) const - { - int ec = migraphx::exec(std::forward(xs)...); - if(ec != 0) - MIGRAPHX_THROW("Command " + get_command() + " exited with status " + - std::to_string(ec)); - } -}; - -process::process(const std::string& cmd) : impl(std::make_unique()) -{ - impl->command = cmd; -} - -process::process(process&&) noexcept = default; - -process& process::operator=(process rhs) -{ - std::swap(impl, rhs.impl); - return *this; -} - -process::~process() noexcept = default; - -process& process::cwd(const fs::path& p) -{ - impl->cwd = p; - return *this; -} - -void process::exec() { impl->check_exec(impl->get_command()); } - -void process::write(std::function pipe_in) -{ - impl->check_exec(impl->get_command()); -} - -} // namespace MIGRAPHX_INLINE_NS -} // namespace migraphx From ff1dbd7e4f199abdf178226f5f4c8a1987dd262a Mon Sep 17 00:00:00 2001 From: Artur Wojcik Date: Sun, 8 Oct 2023 20:46:42 +0200 Subject: [PATCH 3/3] fix cppcheck and format --- src/process.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/process.cpp b/src/process.cpp index e5b3bceda49..566190ffd21 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -105,7 +105,7 @@ class pipe throw GetLastError(); } - pipe(const pipe&) = delete; + pipe(const pipe&) = delete; pipe& operator=(const pipe&) = delete; pipe(pipe&&) = default; @@ -206,7 +206,7 @@ int exec(const std::string& cmd) { TCHAR buffer[MIGRAPHX_PROCESS_BUFSIZE]; HANDLE std_out{GetStdHandle(STD_OUTPUT_HANDLE)}; - return (std_out == nullptr || std_out == INVALID_HANDLE_VALUE) + return (std_out == nullptr or std_out == INVALID_HANDLE_VALUE) ? GetLastError() : exec(cmd, [&](const pipe&, const pipe& out) { for(;;) @@ -214,7 +214,7 @@ int exec(const std::string& cmd) if(auto result = out.read(buffer, MIGRAPHX_PROCESS_BUFSIZE)) { auto [more_data, bytes_read] = *result; - if(!more_data || bytes_read == 0) + if(not more_data or bytes_read == 0) break; DWORD written; if(WriteFile(std_out, buffer, bytes_read, &written, nullptr) == FALSE)