verilator/src/V3Os.cpp

361 lines
12 KiB
C++
Raw Normal View History

2015-02-27 01:40:45 +00:00
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Os-specific function wrapper
//
2019-11-08 03:33:59 +00:00
// Code available from: https://verilator.org
2015-02-27 01:40:45 +00:00
//
//*************************************************************************
//
// Copyright 2003-2020 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
2015-02-27 01:40:45 +00:00
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
2015-02-27 01:40:45 +00:00
//
//*************************************************************************
// clang-format off
#if defined(_WIN32) || defined(__MINGW32__)
# ifndef PSAPI_VERSION
# define PSAPI_VERSION 1 // Needed for compatibility with Windows 7
# endif
#endif
#if defined(__MINGW32__)
# define MINGW_HAS_SECURE_API 1 // Needed to expose a "secure" POSIX-like API
#endif
// clang-format on
2015-02-27 01:40:45 +00:00
#include "config_build.h"
#include "verilatedos.h"
// Limited V3 headers here - this is a base class for Vlc etc
#include "V3String.h"
#include "V3Os.h"
#include <cerrno>
#include <climits> // PATH_MAX (especially on FreeBSD)
#include <cstdarg>
#include <dirent.h>
#include <fstream>
2015-02-27 01:40:45 +00:00
#include <memory>
#include <sys/stat.h>
#include <sys/types.h>
2015-02-27 01:40:45 +00:00
// clang-format off
2019-03-05 01:29:01 +00:00
#if defined(_WIN32) || defined(__MINGW32__)
# include <windows.h> // LONG for bcrypt.h on MINGW
# include <bcrypt.h> // BCryptGenRandom
# include <chrono>
2015-02-27 01:40:45 +00:00
# include <direct.h> // mkdir
# include <psapi.h> // GetProcessMemoryInfo
# include <thread>
// These macros taken from gdbsupport/gdb_wait.h in binutils-gdb
# ifndef WIFEXITED
# ifdef __MINGW32__
# define WIFEXITED(w) (((w) & 0xC0000000) == 0)
# define WEXITSTATUS(w) ((w) & ~0xC0000000)
# else
# define WIFEXITED(w) (((w) & 0377) == 0)
# define WEXITSTATUS(w) (((w) >> 8) & 0377)
# endif
# endif
#else
# include <sys/time.h>
2020-05-31 19:21:07 +00:00
# include <sys/wait.h> // Needed on FreeBSD for WIFEXITED
# include <unistd.h> // usleep
2015-02-27 01:40:45 +00:00
#endif
// clang-format on
2015-02-27 01:40:45 +00:00
//######################################################################
// Environment
string V3Os::getenvStr(const string& envvar, const string& defaultValue) {
#if defined(_MSC_VER)
// Note: MinGW does not offer _dupenv_s
char* envvalue = nullptr;
_dupenv_s(&envvalue, nullptr, envvar.c_str());
if (envvalue != nullptr) {
const std::string result{envvalue};
free(envvalue);
return result;
} else {
return defaultValue;
}
#else
2015-02-27 01:40:45 +00:00
if (const char* envvalue = getenv(envvar.c_str())) {
return envvalue;
2015-02-27 01:40:45 +00:00
} else {
return defaultValue;
2015-02-27 01:40:45 +00:00
}
#endif
2015-02-27 01:40:45 +00:00
}
void V3Os::setenvStr(const string& envvar, const string& value, const string& why) {
if (why != "") {
UINFO(1, "export " << envvar << "=" << value << " # " << why << endl);
2015-02-27 01:40:45 +00:00
} else {
UINFO(1, "export " << envvar << "=" << value << endl);
2015-02-27 01:40:45 +00:00
}
#if defined(_WIN32) || defined(__MINGW32__)
_putenv_s(envvar.c_str(), value.c_str());
#elif defined(_BSD_SOURCE) || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)
setenv(envvar.c_str(), value.c_str(), true);
2015-02-27 01:40:45 +00:00
#else
// setenv() replaced by putenv() in Solaris environment. Prototype is different
// putenv() requires NAME=VALUE format
2015-02-27 01:40:45 +00:00
string vareq = envvar + "=" + value;
putenv(const_cast<char*>(vareq.c_str()));
#endif
}
//######################################################################
2017-09-11 23:18:58 +00:00
// Generic filename utilities
2015-02-27 01:40:45 +00:00
string V3Os::filenameFromDirBase(const string& dir, const string& basename) {
2015-02-27 01:40:45 +00:00
// Don't return ./{filename} because if filename was absolute, that makes it relative
if (dir == ".") {
return basename;
} else {
return dir + "/" + basename;
}
2015-02-27 01:40:45 +00:00
}
string V3Os::filenameDir(const string& filename) {
2015-02-27 01:40:45 +00:00
string::size_type pos;
if ((pos = filename.rfind('/')) != string::npos) {
return filename.substr(0, pos);
2015-02-27 01:40:45 +00:00
} else {
return ".";
2015-02-27 01:40:45 +00:00
}
}
string V3Os::filenameNonDir(const string& filename) {
2015-02-27 01:40:45 +00:00
string::size_type pos;
if ((pos = filename.rfind('/')) != string::npos) {
return filename.substr(pos + 1);
2015-02-27 01:40:45 +00:00
} else {
return filename;
2015-02-27 01:40:45 +00:00
}
}
string V3Os::filenameNonExt(const string& filename) {
2015-02-27 01:40:45 +00:00
string base = filenameNonDir(filename);
string::size_type pos;
if ((pos = base.find('.')) != string::npos) base.erase(pos);
2015-02-27 01:40:45 +00:00
return base;
}
string V3Os::filenameSubstitute(const string& filename) {
2015-02-27 01:40:45 +00:00
string out;
enum : uint8_t { NONE, PAREN, CURLY } brackets = NONE;
2015-02-27 01:40:45 +00:00
for (string::size_type pos = 0; pos < filename.length(); ++pos) {
if ((filename[pos] == '$') && (pos + 1 < filename.length())) {
switch (filename[pos + 1]) {
case '{': brackets = CURLY; break;
case '(': brackets = PAREN; break;
default: brackets = NONE; break;
}
if (brackets != NONE) pos = pos + 1;
string::size_type endpos = pos + 1;
while (((endpos + 1) < filename.length())
&& (((brackets == NONE)
&& (isalnum(filename[endpos + 1]) || filename[endpos + 1] == '_'))
|| ((brackets == CURLY) && (filename[endpos + 1] != '}'))
|| ((brackets == PAREN) && (filename[endpos + 1] != ')'))))
++endpos;
// Catch bracket errors
if (((brackets == CURLY) && (filename[endpos + 1] != '}'))
|| ((brackets == PAREN) && (filename[endpos + 1] != ')'))) {
v3fatal("Unmatched brackets in variable substitution in file: " + filename);
}
string envvar = filename.substr(pos + 1, endpos - pos);
string envvalue;
2020-01-10 12:07:21 +00:00
if (!envvar.empty()) envvalue = getenvStr(envvar, "");
if (!envvalue.empty()) {
out += envvalue;
if (brackets == NONE) {
pos = endpos;
} else {
pos = endpos + 1;
}
} else {
out += filename[pos]; // *pos == '$'
}
} else {
out += filename[pos];
}
2015-02-27 01:40:45 +00:00
}
return out;
}
2017-02-09 23:33:18 +00:00
string V3Os::filenameRealPath(const string& filename) {
// Get rid of all the ../ behavior in the middle of the paths.
// If there is a ../ that goes down from the 'root' of this path it is preserved.
char retpath[PATH_MAX];
if (
#if defined(_WIN32) || defined(__MINGW32__)
_fullpath(retpath, filename.c_str(), PATH_MAX)
#else
realpath(filename.c_str(), retpath)
#endif
) {
return string(retpath);
2017-02-13 23:11:22 +00:00
} else {
return filename;
2017-02-13 23:11:22 +00:00
}
2017-02-09 23:33:18 +00:00
}
2015-02-27 01:40:45 +00:00
bool V3Os::filenameIsRel(const string& filename) {
return (filename.length() > 0 && filename[0] != '/');
2015-02-27 01:40:45 +00:00
}
//######################################################################
// File utilities
string V3Os::getline(std::istream& is, char delim) {
string line;
#if defined(__CYGWIN__) // Work around buggy implementation of getline
char buf[65536];
is.getline(buf, 65535, delim);
line = buf;
#else
std::getline(is, line, delim);
#endif
return line;
}
//######################################################################
2015-02-27 01:40:45 +00:00
// Directory utilities
void V3Os::createDir(const string& dirname) {
#if defined(_WIN32) || defined(__MINGW32__)
_mkdir(dirname.c_str());
2015-02-27 01:40:45 +00:00
#else
mkdir(dirname.c_str(), 0777);
#endif
}
void V3Os::unlinkRegexp(const string& dir, const string& regexp) {
if (DIR* dirp = opendir(dir.c_str())) {
while (struct dirent* direntp = readdir(dirp)) {
if (VString::wildmatch(direntp->d_name, regexp.c_str())) {
string fullname = dir + "/" + string(direntp->d_name);
#if defined(_WIN32) || defined(__MINGW32__)
_unlink(fullname.c_str());
#else
unlink(fullname.c_str());
#endif
}
}
closedir(dirp);
2015-02-27 01:40:45 +00:00
}
}
2018-09-20 22:09:19 +00:00
//######################################################################
// METHODS (random)
vluint64_t V3Os::rand64(std::array<vluint64_t, 2>& stater) {
2018-09-20 22:09:19 +00:00
// Xoroshiro128+ algorithm
vluint64_t result = stater[0] + stater[1];
stater[1] ^= stater[0];
stater[0] = (((stater[0] << 55) | (stater[0] >> 9)) ^ stater[1] ^ (stater[1] << 14));
stater[1] = (stater[1] << 36) | (stater[1] >> 28);
2018-09-20 22:09:19 +00:00
return result;
}
string V3Os::trueRandom(size_t size) {
string result(size, '\xFF');
char* const data = const_cast<char*>(result.data());
// Note: std::string.data() returns a non-const Char* from C++17 onwards.
// For pre-C++17, this cast is OK in practice, even though it's UB.
#if defined(_WIN32) || defined(__MINGW32__)
NTSTATUS hr = BCryptGenRandom(nullptr, reinterpret_cast<BYTE*>(data), size,
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
if (!BCRYPT_SUCCESS(hr)) { v3fatal("Could not acquire random data."); }
#else
std::ifstream is("/dev/urandom", std::ios::in | std::ios::binary);
// This read uses the size of the buffer.
// Flawfinder: ignore
2020-05-16 22:02:54 +00:00
if (VL_UNCOVERABLE(!is.read(data, size))) {
v3fatal("Could not open /dev/urandom, no source of randomness. " // LCOV_EXCL_LINE
"Try specifying a key instead.");
}
#endif
return result;
}
//######################################################################
// METHODS (performance)
uint64_t V3Os::timeUsecs() {
#if defined(_WIN32) || defined(__MINGW32__)
// Microseconds between 1601-01-01 00:00:00 UTC and 1970-01-01 00:00:00 UTC
static const uint64_t EPOCH_DIFFERENCE_USECS = 11644473600000000ULL;
FILETIME ft; // contains number of 0.1us intervals since the beginning of 1601 UTC.
GetSystemTimeAsFileTime(&ft);
uint64_t us
= ((static_cast<uint64_t>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime + 5ULL) / 10ULL;
return us - EPOCH_DIFFERENCE_USECS;
#else
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
timeval tv;
if (gettimeofday(&tv, nullptr) < 0) return 0;
return static_cast<uint64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
#endif
}
uint64_t V3Os::memUsageBytes() {
#if defined(_WIN32) || defined(__MINGW32__)
HANDLE process = GetCurrentProcess();
PROCESS_MEMORY_COUNTERS pmc;
if (GetProcessMemoryInfo(process, &pmc, sizeof(pmc))) {
// The best we can do using simple Windows APIs is to get the size of the working set.
return pmc.WorkingSetSize;
}
return 0;
#else
// Highly unportable. Sorry
const char* const statmFilename = "/proc/self/statm";
FILE* fp = fopen(statmFilename, "r");
if (!fp) return 0;
vluint64_t size, resident, share, text, lib, data, dt; // All in pages
2020-05-16 22:02:54 +00:00
int items = fscanf(fp,
"%" VL_PRI64 "u %" VL_PRI64 "u %" VL_PRI64 "u %" VL_PRI64 "u %" VL_PRI64
"u %" VL_PRI64 "u %" VL_PRI64 "u",
&size, &resident, &share, &text, &lib, &data, &dt);
2017-10-14 18:51:57 +00:00
fclose(fp);
2020-05-16 22:02:54 +00:00
if (VL_UNCOVERABLE(7 != items)) return 0;
return (text + data) * getpagesize();
#endif
}
void V3Os::u_sleep(int64_t usec) {
#if defined(_WIN32) || defined(__MINGW32__)
std::this_thread::sleep_for(std::chrono::microseconds(usec));
#else
// cppcheck-suppress obsoleteFunctionsusleep
// Flawfinder: ignore
::usleep(usec);
#endif
}
//######################################################################
// METHODS (sub command)
int V3Os::system(const string& command) {
UINFO(1, "Running system: " << command << endl);
const int ret = ::system(command.c_str());
2020-05-29 23:35:54 +00:00
if (VL_UNCOVERABLE(ret == -1)) {
v3fatal("Failed to execute command:" // LCOV_EXCL_LINE
<< command << " " << strerror(errno));
return -1; // LCOV_EXCL_LINE
} else {
UASSERT(WIFEXITED(ret), "system(" << command << ") returned unexpected value of " << ret);
const int exit_code = WEXITSTATUS(ret);
UINFO(1, command << " returned exit code of " << exit_code << std::endl);
UASSERT(exit_code >= 0, "exit code must not be negative");
return exit_code;
}
}