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
|
|
|
//
|
|
|
|
//*************************************************************************
|
|
|
|
//
|
2020-03-21 15:24:24 +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.
|
2020-03-21 15:24:24 +00:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2015-02-27 01:40:45 +00:00
|
|
|
//
|
|
|
|
//*************************************************************************
|
2019-10-05 00:17:11 +00:00
|
|
|
|
2019-12-28 16:44:24 +00:00
|
|
|
#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
|
|
|
|
|
2015-02-27 01:40:45 +00:00
|
|
|
#include "config_build.h"
|
|
|
|
#include "verilatedos.h"
|
2018-10-14 17:43:24 +00:00
|
|
|
|
2019-10-03 01:38:06 +00:00
|
|
|
// Limited V3 headers here - this is a base class for Vlc etc
|
2018-10-14 17:43:24 +00:00
|
|
|
#include "V3Global.h"
|
|
|
|
#include "V3String.h"
|
|
|
|
#include "V3Os.h"
|
|
|
|
|
2018-10-14 11:04:18 +00:00
|
|
|
#include <cerrno>
|
2017-02-09 23:33:18 +00:00
|
|
|
#include <climits>
|
2018-10-14 11:04:18 +00:00
|
|
|
#include <cstdarg>
|
|
|
|
#include <dirent.h>
|
2015-02-27 01:40:45 +00:00
|
|
|
#include <fcntl.h>
|
2019-10-06 17:24:21 +00:00
|
|
|
#include <fstream>
|
2015-02-27 01:40:45 +00:00
|
|
|
#include <iomanip>
|
|
|
|
#include <memory>
|
2018-10-14 11:04:18 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
2015-02-27 01:40:45 +00:00
|
|
|
|
2019-03-05 01:29:01 +00:00
|
|
|
#if defined(_WIN32) || defined(__MINGW32__)
|
2019-12-28 16:44:24 +00:00
|
|
|
# include <winnt.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
|
2019-12-28 16:44:24 +00:00
|
|
|
# include <psapi.h> // GetProcessMemoryInfo
|
|
|
|
# include <thread>
|
|
|
|
#else
|
|
|
|
# include <sys/time.h>
|
|
|
|
# include <unistd.h> // usleep
|
2015-02-27 01:40:45 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
// Environment
|
|
|
|
|
|
|
|
string V3Os::getenvStr(const string& envvar, const string& defaultValue) {
|
2019-12-28 16:44:24 +00:00
|
|
|
#if defined(_MSC_VER)
|
|
|
|
// Note: MinGW does not offer _dupenv_s
|
|
|
|
char* envvalue;
|
|
|
|
if (_dupenv_s(&envvalue, nullptr, envvar.c_str()) == 0) {
|
|
|
|
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())) {
|
2019-05-19 20:13:13 +00:00
|
|
|
return envvalue;
|
2015-02-27 01:40:45 +00:00
|
|
|
} else {
|
2019-05-19 20:13:13 +00:00
|
|
|
return defaultValue;
|
2015-02-27 01:40:45 +00:00
|
|
|
}
|
2019-12-28 16:44:24 +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 != "") {
|
2019-05-19 20:13:13 +00:00
|
|
|
UINFO(1,"export "<<envvar<<"="<<value<<" # "<<why<<endl);
|
2015-02-27 01:40:45 +00:00
|
|
|
} else {
|
2019-05-19 20:13:13 +00:00
|
|
|
UINFO(1,"export "<<envvar<<"="<<value<<endl);
|
2015-02-27 01:40:45 +00:00
|
|
|
}
|
2019-12-28 16:44:24 +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)
|
2019-05-19 20:13:13 +00:00
|
|
|
setenv(envvar.c_str(), value.c_str(), true);
|
2015-02-27 01:40:45 +00:00
|
|
|
#else
|
2019-12-28 16:44:24 +00:00
|
|
|
//setenv() replaced by putenv() in Solaris environment. Prototype is different
|
2015-02-27 01:40:45 +00:00
|
|
|
//putenv() requires NAME=VALUE format
|
|
|
|
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
|
|
|
|
2018-08-25 13:52: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;
|
|
|
|
}
|
|
|
|
|
2018-08-25 13:52:45 +00:00
|
|
|
string V3Os::filenameDir(const string& filename) {
|
2015-02-27 01:40:45 +00:00
|
|
|
string::size_type pos;
|
2018-10-14 02:28:59 +00:00
|
|
|
if ((pos = filename.rfind('/')) != string::npos) {
|
2019-05-19 20:13:13 +00:00
|
|
|
return filename.substr(0, pos);
|
2015-02-27 01:40:45 +00:00
|
|
|
} else {
|
2019-05-19 20:13:13 +00:00
|
|
|
return ".";
|
2015-02-27 01:40:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-25 13:52:45 +00:00
|
|
|
string V3Os::filenameNonDir(const string& filename) {
|
2015-02-27 01:40:45 +00:00
|
|
|
string::size_type pos;
|
2018-10-14 02:28:59 +00:00
|
|
|
if ((pos = filename.rfind('/')) != string::npos) {
|
2019-05-19 20:13:13 +00:00
|
|
|
return filename.substr(pos+1);
|
2015-02-27 01:40:45 +00:00
|
|
|
} else {
|
2019-05-19 20:13:13 +00:00
|
|
|
return filename;
|
2015-02-27 01:40:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-25 13:52: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;
|
2018-10-14 02:28:59 +00:00
|
|
|
if ((pos = base.find('.')) != string::npos) {
|
2019-05-19 20:13:13 +00:00
|
|
|
base.erase(pos);
|
2015-02-27 01:40:45 +00:00
|
|
|
}
|
|
|
|
return base;
|
|
|
|
}
|
|
|
|
|
2018-08-25 13:52:45 +00:00
|
|
|
string V3Os::filenameSubstitute(const string& filename) {
|
2015-02-27 01:40:45 +00:00
|
|
|
string out;
|
|
|
|
enum { NONE, PAREN, CURLY } brackets = NONE;
|
|
|
|
for (string::size_type pos = 0; pos < filename.length(); ++pos) {
|
|
|
|
if ((filename[pos] == '$') && (pos+1 < filename.length())) {
|
2019-05-19 20:13:13 +00:00
|
|
|
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);
|
2019-12-28 16:44:24 +00:00
|
|
|
string envvalue;
|
2020-01-10 12:07:21 +00:00
|
|
|
if (!envvar.empty()) envvalue = getenvStr(envvar, "");
|
2019-12-28 16:44:24 +00:00
|
|
|
if (!envvalue.empty()) {
|
2019-05-19 20:13:13 +00:00
|
|
|
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];
|
2017-03-16 00:08:19 +00:00
|
|
|
if (
|
2019-12-28 16:44:24 +00:00
|
|
|
#if defined(_WIN32) || defined(__MINGW32__)
|
|
|
|
_fullpath(retpath, filename.c_str(), PATH_MAX)
|
2017-03-16 00:08:19 +00:00
|
|
|
#else
|
2019-05-19 20:13:13 +00:00
|
|
|
realpath(filename.c_str(), retpath)
|
2017-03-16 00:08:19 +00:00
|
|
|
#endif
|
2019-05-19 20:13:13 +00:00
|
|
|
) {
|
|
|
|
return string(retpath);
|
2017-02-13 23:11:22 +00:00
|
|
|
} else {
|
2019-05-19 20:13:13 +00:00
|
|
|
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] != '/');
|
|
|
|
}
|
|
|
|
|
|
|
|
//######################################################################
|
2018-11-27 00:09:08 +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__)
|
2019-12-28 16:44:24 +00:00
|
|
|
_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())) {
|
2019-05-19 20:13:13 +00:00
|
|
|
while (struct dirent* direntp = readdir(dirp)) {
|
|
|
|
if (VString::wildmatch(direntp->d_name, regexp.c_str())) {
|
|
|
|
string fullname = dir + "/" + string(direntp->d_name);
|
2019-12-28 16:44:24 +00:00
|
|
|
#if defined(_WIN32) || defined(__MINGW32__)
|
|
|
|
_unlink(fullname.c_str());
|
|
|
|
#else
|
2018-08-25 13:52:45 +00:00
|
|
|
unlink(fullname.c_str());
|
2019-12-28 16:44:24 +00:00
|
|
|
#endif
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
closedir(dirp);
|
2015-02-27 01:40:45 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-18 02:52:57 +00:00
|
|
|
|
2018-09-20 22:09:19 +00:00
|
|
|
//######################################################################
|
|
|
|
// METHODS (random)
|
|
|
|
|
|
|
|
vluint64_t V3Os::rand64(vluint64_t* statep) {
|
|
|
|
// Xoroshiro128+ algorithm
|
|
|
|
vluint64_t result = statep[0] + statep[1];
|
|
|
|
statep[1] ^= statep[0];
|
|
|
|
statep[0] = (((statep[0] << 55) | (statep[0] >> 9))
|
|
|
|
^ statep[1] ^ (statep[1] << 14));
|
|
|
|
statep[1] = (statep[1] << 36) | (statep[1] >> 28);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-10-06 17:24:21 +00:00
|
|
|
string V3Os::trueRandom(size_t size) {
|
2019-12-28 16:44:24 +00:00
|
|
|
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(NULL, reinterpret_cast<BYTE*>(data), size, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
|
|
|
if (!BCRYPT_SUCCESS(hr)) {
|
|
|
|
v3fatal("Could not acquire random data.");
|
|
|
|
}
|
|
|
|
#else
|
2019-10-06 17:24:21 +00:00
|
|
|
std::ifstream is ("/dev/urandom", std::ios::in | std::ios::binary);
|
2019-12-28 16:44:24 +00:00
|
|
|
// This read uses the size of the buffer.
|
|
|
|
// Flawfinder: ignore
|
|
|
|
if (!is.read(data, size)) {
|
2020-01-12 02:08:20 +00:00
|
|
|
v3fatal("Could not open /dev/urandom, no source of randomness. Try specifying a key instead.");
|
2019-10-06 17:24:21 +00:00
|
|
|
}
|
2019-12-28 16:44:24 +00:00
|
|
|
#endif
|
|
|
|
return result;
|
2019-10-06 17:24:21 +00:00
|
|
|
}
|
|
|
|
|
2017-09-18 02:52:57 +00:00
|
|
|
//######################################################################
|
|
|
|
// METHODS (performance)
|
|
|
|
|
|
|
|
uint64_t V3Os::timeUsecs() {
|
|
|
|
#if defined(_WIN32) || defined(__MINGW32__)
|
2019-12-28 16:44:24 +00:00
|
|
|
// 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;
|
2017-09-18 02:52:57 +00:00
|
|
|
#else
|
2018-10-14 22:39:33 +00:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
2017-09-18 02:52:57 +00:00
|
|
|
timeval tv;
|
|
|
|
if (gettimeofday(&tv, NULL) < 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__)
|
2019-12-28 16:44:24 +00:00
|
|
|
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;
|
|
|
|
}
|
2017-09-18 02:52:57 +00:00
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
// Highly unportable. Sorry
|
2017-10-11 00:16:35 +00:00
|
|
|
const char* const statmFilename = "/proc/self/statm";
|
2019-05-19 20:13:13 +00:00
|
|
|
FILE* fp = fopen(statmFilename, "r");
|
2017-09-18 02:52:57 +00:00
|
|
|
if (!fp) {
|
2019-05-19 20:13:13 +00:00
|
|
|
return 0;
|
2017-09-18 02:52:57 +00:00
|
|
|
}
|
2018-06-14 22:59:24 +00:00
|
|
|
vluint64_t size, resident, share, text, lib, data, dt; // All in pages
|
|
|
|
if (7 != fscanf(fp, "%" VL_PRI64 "u %" VL_PRI64 "u %" VL_PRI64 "u %"
|
|
|
|
VL_PRI64 "u %" VL_PRI64 "u %" VL_PRI64 "u %" VL_PRI64 "u",
|
2019-05-19 20:13:13 +00:00
|
|
|
&size, &resident, &share, &text, &lib, &data, &dt)) {
|
|
|
|
fclose(fp);
|
|
|
|
return 0;
|
2017-09-18 02:52:57 +00:00
|
|
|
}
|
2017-10-14 18:51:57 +00:00
|
|
|
fclose(fp);
|
2017-09-18 02:52:57 +00:00
|
|
|
return (text + data) * getpagesize();
|
|
|
|
#endif
|
|
|
|
}
|
2019-12-28 16:44:24 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|