Add helper to get errno and error message (#20324)

### Description
<!-- Describe your changes. -->
Add platform aware helper to fetch errno message string.


### Motivation and Context
<!-- - Why is this change required? What problem does it solve?
- If it fixes an open issue, please link to the issue here. -->
For usage in #20077

---------

Co-authored-by: Edward Chen <18449977+edgchen1@users.noreply.github.com>
This commit is contained in:
Scott McKay 2024-04-17 21:17:36 +10:00 committed by GitHub
parent 4d2b98155f
commit 8143b0d798
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 65 additions and 41 deletions

View file

@ -34,4 +34,29 @@ std::ostream& operator<<(std::ostream& os, gsl::span<const LogicalProcessors> af
Env::Env() = default;
std::pair<int, std::string> GetErrnoInfo() {
auto err = errno;
std::string msg;
if (err != 0) {
char buf[512];
#if defined(_WIN32)
auto ret = strerror_s(buf, sizeof(buf), err);
msg = ret == 0 ? buf : "Failed to get error message"; // buf is guaranteed to be null terminated by strerror_s
#else
// strerror_r return type differs by platform.
auto ret = strerror_r(err, buf, sizeof(buf));
if constexpr (std::is_same_v<decltype(ret), int>) { // POSIX returns int
msg = ret == 0 ? buf : "Failed to get error message";
} else {
// GNU returns char*
msg = ret;
}
#endif
}
return {err, msg};
}
} // namespace onnxruntime

View file

@ -96,6 +96,12 @@ struct ThreadOptions {
std::ostream& operator<<(std::ostream& os, const LogicalProcessors&);
std::ostream& operator<<(std::ostream& os, gsl::span<const LogicalProcessors>);
/// <summary>
/// Get errno and the corresponding error message.
/// </summary>
/// <returns>errno and the error message string if errno indicates an error.</returns>
std::pair<int, std::string> GetErrnoInfo();
/// \brief An interface used by the onnxruntime implementation to
/// access operating system functionality like the filesystem etc.
///

View file

@ -62,39 +62,11 @@ class UnmapFileParam {
size_t len;
};
/**
* @brief Get System Error
*
* @return a pair of {errno, error message}
*/
static std::pair<int, std::string> GetSystemError(int e) {
char buf[1024];
const char* msg = "";
if (e > 0) {
#if defined(__GLIBC__) && defined(_GNU_SOURCE) && !defined(__ANDROID__)
msg = strerror_r(e, buf, sizeof(buf));
#else
// for Mac OS X and Android lower than API 23
if (strerror_r(e, buf, sizeof(buf)) != 0) {
buf[0] = '\0';
}
msg = buf;
#endif
}
return std::make_pair(e, msg);
}
static std::pair<int, std::string> GetSystemError() {
auto e = errno;
return GetSystemError(e);
}
static void UnmapFile(void* param) noexcept {
std::unique_ptr<UnmapFileParam> p(reinterpret_cast<UnmapFileParam*>(param));
int ret = munmap(p->addr, p->len);
if (ret != 0) {
auto [err_no, err_msg] = GetSystemError();
auto [err_no, err_msg] = GetErrnoInfo();
LOGS_DEFAULT(ERROR) << "munmap failed. error code: " << err_no << " error msg: " << err_msg;
}
}
@ -104,8 +76,9 @@ struct FileDescriptorTraits {
static Handle GetInvalidHandleValue() { return -1; }
static void CleanUp(Handle h) {
if (close(h) == -1) {
auto [err_no, err_msg] = GetSystemError();
LOGS_DEFAULT(ERROR) << "Failed to close file descriptor " << h << " - error code: " << err_no << " error msg: " << err_msg;
auto [err_no, err_msg] = GetErrnoInfo();
LOGS_DEFAULT(ERROR) << "Failed to close file descriptor " << h << " - error code: " << err_no
<< " error msg: " << err_msg;
}
}
};
@ -131,7 +104,7 @@ int nftw_remove(
int /*typeflag*/, struct FTW* /*ftwbuf*/) {
const auto result = remove(fpath);
if (result != 0) {
auto [err_no, err_msg] = GetSystemError();
auto [err_no, err_msg] = GetErrnoInfo();
LOGS_DEFAULT(WARNING) << "remove() failed. Error code: " << err_no << " error msg: " << err_msg
<< ", path: " << fpath;
}
@ -188,7 +161,7 @@ class PosixThread : public EnvThread {
pthread_attr_t attr;
int s = pthread_attr_init(&attr);
if (s != 0) {
auto [err_no, err_msg] = GetSystemError();
auto [err_no, err_msg] = GetErrnoInfo();
ORT_THROW("pthread_attr_init failed, error code: ", err_no, " error msg: ", err_msg);
}
@ -196,14 +169,14 @@ class PosixThread : public EnvThread {
if (stack_size > 0) {
s = pthread_attr_setstacksize(&attr, stack_size);
if (s != 0) {
auto [err_no, err_msg] = GetSystemError();
auto [err_no, err_msg] = GetErrnoInfo();
ORT_THROW("pthread_attr_setstacksize failed, error code: ", err_no, " error msg: ", err_msg);
}
}
s = pthread_create(&hThread, &attr, ThreadMain, param_ptr.get());
if (s != 0) {
auto [err_no, err_msg] = GetSystemError();
auto [err_no, err_msg] = GetErrnoInfo();
ORT_THROW("pthread_create failed, error code: ", err_no, " error msg: ", err_msg);
}
param_ptr.release();
@ -249,7 +222,8 @@ class PosixThread : public EnvThread {
<< ", index: " << p->index
<< ", mask: " << *p->affinity;
} else {
auto [err_no, err_msg] = GetSystemError(ret);
errno = ret;
auto [err_no, err_msg] = GetErrnoInfo();
#if !defined(USE_MIGRAPHX)
LOGS_DEFAULT(ERROR) << "pthread_setaffinity_np failed for thread: " << syscall(SYS_gettid)
<< ", index: " << p->index
@ -461,7 +435,7 @@ class PosixEnv : public Env {
}
static common::Status ReportSystemError(const char* operation_name, const std::string& path) {
auto [err_no, err_msg] = GetSystemError();
auto [err_no, err_msg] = GetErrnoInfo();
std::ostringstream oss;
oss << operation_name << " file \"" << path << "\" failed: " << err_msg;
return common::Status(common::SYSTEM, err_no, oss.str());

View file

@ -110,11 +110,10 @@ class WindowsThread : public EnvThread {
local_param.get(), 0,
&threadID);
if (th_handle == 0) {
auto err = errno;
auto dos_error = _doserrno;
char message_buf[256];
strerror_s(message_buf, sizeof(message_buf), err);
ORT_THROW("WindowThread:_beginthreadex failed with message: ", message_buf, " doserrno: ", dos_error);
auto [err, msg] = GetErrnoInfo();
ORT_THROW("WindowThread:_beginthreadex failed with errno:", err, " message:", msg,
" doserrno:", dos_error);
}
local_param.release();
hThread.reset(reinterpret_cast<HANDLE>(th_handle));

View file

@ -33,5 +33,25 @@ TEST(PlatformEnvTest, DirectoryCreationAndDeletion) {
ASSERT_FALSE(env.FolderExists(root_dir));
}
TEST(PlatformEnvTest, GetErrnoInfo) {
// command that should generate an errno error
std::ifstream file("non_existent_file");
ASSERT_TRUE(file.fail());
auto [err, msg] = GetErrnoInfo();
ASSERT_EQ(err, ENOENT);
#if defined(_WIN32)
#pragma warning(push)
#pragma warning(disable : 4996)
#endif
// GetErrnoInfo uses strerror_r or strerror_s depending on the platform. use the unsafe std::sterror to get the
// expected value given this is a unit test so doesn't have to be as robust.
ASSERT_EQ(msg, std::strerror(ENOENT));
#if defined(_WIN32)
#pragma warning(pop)
#endif
}
} // namespace test
} // namespace onnxruntime