Files
OpenCL-CTS/test_common/harness/os_helpers.cpp
Kevin Petit d8733efc0f Synchronise with Khronos-private Gitlab branch
The maintenance of the conformance tests is moving to Github.

This commit contains all the changes that have been done in
Gitlab since the first public release of the conformance tests.

Signed-off-by: Kevin Petit <kevin.petit@arm.com>
2019-03-05 16:23:49 +00:00

565 lines
16 KiB
C++

//
// Copyright (c) 2017 The Khronos Group Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "os_helpers.h"
#include "errorHelpers.h"
// =================================================================================================
// C++ interface.
// =================================================================================================
#include <cerrno> // errno, error constants
#include <climits> // PATH_MAX
#include <cstdlib> // abort, _splitpath, _makepath
#include <cstring> // strdup, strerror_r
#include <sstream>
#include <vector>
#define CHECK_PTR( ptr ) \
if ( (ptr) == NULL ) { \
abort(); \
}
typedef std::vector< char > buffer_t;
#if ! defined( PATH_MAX )
#define PATH_MAX 1000
#endif
int const _size = PATH_MAX + 1; // Initial buffer size for path.
int const _count = 8; // How many times we will try to double buffer size.
// -------------------------------------------------------------------------------------------------
// MacOS X
// -------------------------------------------------------------------------------------------------
#if defined( __APPLE__ )
#include <mach-o/dyld.h> // _NSGetExecutablePath
#include <libgen.h> // dirname
static
std::string
_err_msg(
int err, // Error number (e. g. errno).
int level // Nesting level, for avoiding infinite recursion.
) {
/*
There are 3 incompatible versions of strerror_r:
char * strerror_r( int, char *, size_t ); // GNU version
int strerror_r( int, char *, size_t ); // BSD version
int strerror_r( int, char *, size_t ); // XSI version
BSD version returns error code, while XSI version returns 0 or -1 and sets errno.
*/
// BSD version of strerror_r.
buffer_t buffer( 100 );
int count = _count;
for ( ; ; ) {
int rc = strerror_r( err, & buffer.front(), buffer.size() );
if ( rc == EINVAL ) {
// Error code is not recognized, but anyway we got the message.
return & buffer.front();
} else if ( rc == ERANGE ) {
// Buffer is not enough.
if ( count > 0 ) {
// Enlarge the buffer.
-- count;
buffer.resize( buffer.size() * 2 );
} else {
std::stringstream ostr;
ostr
<< "Error " << err << " "
<< "(Getting error message failed: "
<< "Buffer of " << buffer.size() << " bytes is still too small"
<< ")";
return ostr.str();
}; // if
} else if ( rc == 0 ) {
// We got the message.
return & buffer.front();
} else {
std::stringstream ostr;
ostr
<< "Error " << err << " "
<< "(Getting error message failed: "
<< ( level < 2 ? _err_msg( rc, level + 1 ) : "Oops" )
<< ")";
return ostr.str();
}; // if
}; // forever
} // _err_msg
std::string
dir_sep(
) {
return "/";
} // dir_sep
std::string
exe_path(
) {
buffer_t path( _size );
int count = _count;
for ( ; ; ) {
uint32_t size = path.size();
int rc = _NSGetExecutablePath( & path.front(), & size );
if ( rc == 0 ) {
break;
}; // if
if ( count > 0 ) {
-- count;
path.resize( size );
} else {
log_error(
"ERROR: Getting executable path failed: "
"_NSGetExecutablePath failed: Buffer of %lu bytes is still too small\n",
(unsigned long) path.size()
);
exit( 2 );
}; // if
}; // forever
return & path.front();
} // exe_path
std::string
exe_dir(
) {
std::string path = exe_path();
// We cannot pass path.c_str() to `dirname' bacause `dirname' modifies its argument.
buffer_t buffer( path.c_str(), path.c_str() + path.size() + 1 ); // Copy with trailing zero.
return dirname( & buffer.front() );
} // exe_dir
#endif // __APPLE__
// -------------------------------------------------------------------------------------------------
// Linux
// -------------------------------------------------------------------------------------------------
#if defined( __linux__ )
#include <cerrno> // errno
#include <libgen.h> // dirname
#include <unistd.h> // readlink
static
std::string
_err_msg(
int err,
int level
) {
/*
There are 3 incompatible versions of strerror_r:
char * strerror_r( int, char *, size_t ); // GNU version
int strerror_r( int, char *, size_t ); // BSD version
int strerror_r( int, char *, size_t ); // XSI version
BSD version returns error code, while XSI version returns 0 or -1 and sets errno.
*/
#if defined(__ANDROID__) || ( ( _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 ) && ! _GNU_SOURCE )
// XSI version of strerror_r.
#warning Not tested!
buffer_t buffer( 200 );
int count = _count;
for ( ; ; ) {
int rc = strerror_r( err, & buffer.front(), buffer.size() );
if ( rc == -1 ) {
int _err = errno;
if ( _err == ERANGE ) {
if ( count > 0 ) {
// Enlarge the buffer.
-- count;
buffer.resize( buffer.size() * 2 );
} else {
std::stringstream ostr;
ostr
<< "Error " << err << " "
<< "(Getting error message failed: "
<< "Buffer of " << buffer.size() << " bytes is still too small"
<< ")";
return ostr.str();
}; // if
} else {
std::stringstream ostr;
ostr
<< "Error " << err << " "
<< "(Getting error message failed: "
<< ( level < 2 ? _err_msg( _err, level + 1 ) : "Oops" )
<< ")";
return ostr.str();
}; // if
} else {
// We got the message.
return & buffer.front();
}; // if
}; // forever
#else
// GNU version of strerror_r.
char buffer[ 2000 ];
return strerror_r( err, buffer, sizeof( buffer ) );
#endif
} // _err_msg
std::string
dir_sep(
) {
return "/";
} // dir_sep
std::string
exe_path(
) {
static std::string const exe = "/proc/self/exe";
buffer_t path( _size );
int count = _count; // Max number of iterations.
for ( ; ; ) {
ssize_t len = readlink( exe.c_str(), & path.front(), path.size() );
if ( len < 0 ) {
// Oops.
int err = errno;
log_error(
"ERROR: Getting executable path failed: "
"Reading symlink `%s' failed: %s\n",
exe.c_str(), err_msg( err ).c_str()
);
exit( 2 );
}; // if
if ( len < path.size() ) {
// We got the path.
path.resize( len );
break;
}; // if
// Oops, buffer is too small.
if ( count > 0 ) {
-- count;
// Enlarge the buffer.
path.resize( path.size() * 2 );
} else {
log_error(
"ERROR: Getting executable path failed: "
"Reading symlink `%s' failed: Buffer of %lu bytes is still too small\n",
exe.c_str(),
(unsigned long) path.size()
);
exit( 2 );
}; // if
}; // forever
return std::string( & path.front(), path.size() );
} // exe_path
std::string
exe_dir(
) {
std::string path = exe_path();
// We cannot pass path.c_str() to `dirname' bacause `dirname' modifies its argument.
buffer_t buffer( path.c_str(), path.c_str() + path.size() + 1 ); // Copy with trailing zero.
return dirname( & buffer.front() );
} // exe_dir
#endif // __linux__
// -------------------------------------------------------------------------------------------------
// MS Windows
// -------------------------------------------------------------------------------------------------
#if defined( _WIN32 )
#include <windows.h>
#if defined( max )
#undef max
#endif
#include <cctype>
#include <algorithm>
static
std::string
_err_msg(
int err,
int level
) {
std::string msg;
LPSTR buffer = NULL;
DWORD flags =
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS;
DWORD len =
FormatMessageA(
flags,
NULL,
err,
LANG_USER_DEFAULT,
reinterpret_cast< LPSTR >( & buffer ),
0,
NULL
);
if ( buffer == NULL || len == 0 ) {
int _err = GetLastError();
char str[1024] = { 0 };
snprintf(str, sizeof(str), "Error 0x%08x (Getting error message failed: %s )", err, ( level < 2 ? _err_msg( _err, level + 1 ).c_str() : "Oops" ));
msg = std::string(str);
} else {
// Trim trailing whitespace (including `\r' and `\n').
while ( len > 0 && isspace( buffer[ len - 1 ] ) ) {
-- len;
}; // while
// Drop trailing full stop.
if ( len > 0 && buffer[ len - 1 ] == '.' ) {
-- len;
}; // if
msg.assign( buffer, len );
}; //if
if ( buffer != NULL ) {
LocalFree( buffer );
}; // if
return msg;
} // _get_err_msg
std::string
dir_sep(
) {
return "\\";
} // dir_sep
std::string
exe_path(
) {
buffer_t path( _size );
int count = _count;
for ( ; ; ) {
DWORD len = GetModuleFileNameA( NULL, & path.front(), path.size() );
if ( len == 0 ) {
int err = GetLastError();
log_error( "ERROR: Getting executable path failed: %s\n", err_msg( err ).c_str() );
exit( 2 );
}; // if
if ( len < path.size() ) {
path.resize( len );
break;
}; // if
// Buffer too small.
if ( count > 0 ) {
-- count;
path.resize( path.size() * 2 );
} else {
log_error(
"ERROR: Getting executable path failed: "
"Buffer of %lu bytes is still too small\n",
(unsigned long) path.size()
);
exit( 2 );
}; // if
}; // forever
return std::string( & path.front(), path.size() );
} // exe_path
std::string
exe_dir(
) {
std::string exe = exe_path();
int count = 0;
// Splitting path into components.
buffer_t drv( _MAX_DRIVE );
buffer_t dir( _MAX_DIR );
count = _count;
#if defined(_MSC_VER)
for ( ; ; ) {
int rc =
_splitpath_s(
exe.c_str(),
& drv.front(), drv.size(),
& dir.front(), dir.size(),
NULL, 0, // We need neither name
NULL, 0 // nor extension
);
if ( rc == 0 ) {
break;
} else if ( rc == ERANGE ) {
if ( count > 0 ) {
-- count;
// Buffer is too small, but it is not clear which one.
// So we have to enlarge all.
drv.resize( drv.size() * 2 );
dir.resize( dir.size() * 2 );
} else {
log_error(
"ERROR: Getting executable path failed: "
"Splitting path `%s' to components failed: "
"Buffers of %lu and %lu bytes are still too small\n",
exe.c_str(),
(unsigned long) drv.size(),
(unsigned long) dir.size()
);
exit( 2 );
}; // if
} else {
log_error(
"ERROR: Getting executable path failed: "
"Splitting path `%s' to components failed: %s\n",
exe.c_str(),
err_msg( rc ).c_str()
);
exit( 2 );
}; // if
}; // forever
#else // __MINGW32__
// MinGW does not have the "secure" _splitpath_s, use the insecure version instead.
_splitpath(
exe.c_str(),
& drv.front(),
& dir.front(),
NULL, // We need neither name
NULL // nor extension
);
#endif // __MINGW32__
// Combining components back to path.
// I failed with "secure" `_makepath_s'. If buffer is too small, instead of returning
// ERANGE, `_makepath_s' pops up dialog box and offers to debug the program. D'oh!
// So let us try to guess the size of result and go with insecure `_makepath'.
buffer_t path( std::max( drv.size() + dir.size(), size_t( _MAX_PATH ) ) + 10 );
_makepath( & path.front(), & drv.front(), & dir.front(), NULL, NULL );
return & path.front();
} // exe_dir
#endif // _WIN32
std::string
err_msg(
int err
) {
return _err_msg( err, 0 );
} // err_msg
// =================================================================================================
// C interface.
// =================================================================================================
char *
get_err_msg(
int err
) {
char * msg = strdup( err_msg( err ).c_str() );
CHECK_PTR( msg );
return msg;
} // get_err_msg
char *
get_dir_sep(
) {
char * sep = strdup( dir_sep().c_str() );
CHECK_PTR( sep );
return sep;
} // get_dir_sep
char *
get_exe_path(
) {
char * path = strdup( exe_path().c_str() );
CHECK_PTR( path );
return path;
} // get_exe_path
char *
get_exe_dir(
) {
char * dir = strdup( exe_dir().c_str() );
CHECK_PTR( dir );
return dir;
} // get_exe_dir
// end of file //