// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Os-specific function wrapper // // Code available from: https://verilator.org // //************************************************************************* // // 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 // Lesser General Public License Version 3 or the Perl Artistic License // Version 2.0. // // Verilator is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // //************************************************************************* #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 #include "config_build.h" #include "verilatedos.h" // Limited V3 headers here - this is a base class for Vlc etc #include "V3Global.h" #include "V3String.h" #include "V3Os.h" #include #include #include #include #include #include #include #include #include #include #if defined(_WIN32) || defined(__MINGW32__) # include // LONG for bcrypt.h on MINGW # include // BCryptGenRandom # include # include // mkdir # include // GetProcessMemoryInfo # include #else # include # include // usleep #endif //###################################################################### // Environment string V3Os::getenvStr(const string& envvar, const string& defaultValue) { #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 if (const char* envvalue = getenv(envvar.c_str())) { return envvalue; } else { return defaultValue; } #endif } void V3Os::setenvStr(const string& envvar, const string& value, const string& why) { if (why != "") { UINFO(1,"export "<(vareq.c_str())); #endif } //###################################################################### // Generic filename utilities string V3Os::filenameFromDirBase(const string& dir, const string& basename) { // Don't return ./{filename} because if filename was absolute, that makes it relative if (dir == ".") return basename; else return dir+"/"+basename; } string V3Os::filenameDir(const string& filename) { string::size_type pos; if ((pos = filename.rfind('/')) != string::npos) { return filename.substr(0, pos); } else { return "."; } } string V3Os::filenameNonDir(const string& filename) { string::size_type pos; if ((pos = filename.rfind('/')) != string::npos) { return filename.substr(pos+1); } else { return filename; } } string V3Os::filenameNonExt(const string& filename) { string base = filenameNonDir(filename); string::size_type pos; if ((pos = base.find('.')) != string::npos) { base.erase(pos); } return base; } string V3Os::filenameSubstitute(const string& filename) { 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())) { 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; 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]; } } return out; } 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); } else { return filename; } } bool V3Os::filenameIsRel(const string& filename) { return (filename.length()>0 && filename[0] != '/'); } //###################################################################### // 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; } //###################################################################### // Directory utilities void V3Os::createDir(const string& dirname) { #if defined(_WIN32) || defined(__MINGW32__) _mkdir(dirname.c_str()); #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); } } //###################################################################### // 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; } string V3Os::trueRandom(size_t size) { string result(size, '\xFF'); char *const data = const_cast(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(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 if (!is.read(data, size)) { v3fatal("Could not open /dev/urandom, no source of randomness. 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(ft.dwHighDateTime) << 32) + ft.dwLowDateTime + 5ull) / 10ull; return us - EPOCH_DIFFERENCE_USECS; #else // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) timeval tv; if (gettimeofday(&tv, NULL) < 0) return 0; return static_cast(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 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", &size, &resident, &share, &text, &lib, &data, &dt)) { fclose(fp); return 0; } fclose(fp); 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 }