// This file is part of BOINC. // http://boinc.berkeley.edu // Copyright (C) 2023 University of California // // BOINC is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License // as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // BOINC 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 Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with BOINC. If not, see . #if defined(_WIN32) #include "boinc_win.h" #include "str_replace.h" #include "str_util.h" #include "win_util.h" #endif #if defined(_MSC_VER) || defined(__MINGW32__) #define finite _finite #define snprintf _snprintf #endif #ifndef M_LN2 #define M_LN2 0.693147180559945309417 #endif #include "boinc_stdio.h" #ifndef _WIN32 #include "config.h" #if defined(__APPLE__) #include #endif #include #include #include #include #include #include #include #include #include #include #if HAVE_IEEEFP_H #include extern "C" { int finite(double); } #endif #endif #include "base64.h" #include "common_defs.h" #include "error_numbers.h" #include "filesys.h" #include "mfile.h" #include "miofile.h" #include "parse.h" #include "util.h" using std::min; using std::string; using std::vector; #define EPOCHFILETIME_SEC (11644473600.) #define TEN_MILLION 10000000. #ifdef GCL_SIMULATOR double simtime; #endif // return time of day (seconds since 1970) as a double // double dtime() { #ifdef GCL_SIMULATOR return simtime; #else #ifdef _WIN32 LARGE_INTEGER time; FILETIME sysTime; double t; GetSystemTimeAsFileTime(&sysTime); time.LowPart = sysTime.dwLowDateTime; time.HighPart = sysTime.dwHighDateTime; // Time is in 100 ns units t = (double)time.QuadPart; // Convert to 1 s units t /= TEN_MILLION; /* In seconds */ t -= EPOCHFILETIME_SEC; /* Offset to the Epoch time */ return t; #else struct timeval tv; gettimeofday(&tv, 0); return tv.tv_sec + (tv.tv_usec/1.e6); #endif #endif } // return time today 0:00 in seconds since 1970 as a double // double dday() { double now=dtime(); return (now-fmod(now, SECONDS_PER_DAY)); } // sleep for a specified number of seconds // void boinc_sleep(double seconds) { #ifdef _WIN32 ::Sleep((int)(1000*seconds)); #else double end_time = dtime() + seconds - 0.01; // sleep() and usleep() can be interrupted by SIGALRM, // so we may need multiple calls // while (1) { if (seconds >= 1) { sleep((unsigned int) seconds); } else { usleep((int)fmod(seconds*1000000, 1000000)); } seconds = end_time - dtime(); if (seconds <= 0) break; } #endif } void push_unique(string s, vector& v) { for (unsigned int i=0; i0, // using the first-order Taylor expansion of // exp(x)=1+x+O(x^2). // So to the lowest order in diff: // weight = 1 - diff ln(2) / half_life // so one has // avg += (1-weight)*(work/diff_days) // avg += [diff*ln(2)/half_life] * (work*SECONDS_PER_DAY/diff) // notice that diff cancels out, leaving // avg += [ln(2)/half_life] * work*SECONDS_PER_DAY double diff, diff_days, weight; diff = now - avg_time; if (diff<0) diff=0; diff_days = diff/SECONDS_PER_DAY; weight = exp(-diff*M_LN2/half_life); avg *= weight; if ((1.0-weight) > 1.e-6) { avg += (1-weight)*(work/diff_days); } else { avg += M_LN2*work*SECONDS_PER_DAY/half_life; } } else if (work) { // If first time, average is just work/duration // double dd = (now - work_start_time)/SECONDS_PER_DAY; avg = work/dd; } avg_time = now; } void boinc_crash() { #ifdef _WIN32 DebugBreak(); #else abort(); #endif } // chdir into the given directory, and run a program there. // argv is set up Unix-style, i.e. argv[0] is the program name // #ifdef _WIN32 int run_program( const char* dir, const char* file, int argc, char *const argv[], HANDLE& id ) { int retval; PROCESS_INFORMATION process_info; STARTUPINFOA startup_info; char cmdline[1024]; char error_msg[1024]; memset(&process_info, 0, sizeof(process_info)); memset(&startup_info, 0, sizeof(startup_info)); startup_info.cb = sizeof(startup_info); // lpApplicationName needs to be NULL for CreateProcess to search path // but argv[0] may be full path or just filename // 'file' should be something runnable so use that as program name snprintf(cmdline, sizeof(cmdline), "\"%s\"", file); for (int i=1; i=0) { DWORD dt_msec = (DWORD)dt*1000; DWORD ret = WaitForSingleObject(pid_handle, dt_msec); if (ret == WAIT_TIMEOUT) { return ERR_NOT_FOUND; } } else { WaitForSingleObject(pid_handle, INFINITE); } unsigned long stat=1; GetExitCodeProcess(pid_handle, &stat); status = (int) stat; return 0; } #else int get_exit_status(int pid, int &status, double dt) { if (dt>=0) { while (1) { int ret = waitpid(pid, &status, WNOHANG); if (ret > 0) return 0; dt -= 1; if (dt<0) break; boinc_sleep(1); } return ERR_NOT_FOUND; } else { waitpid(pid, &status, 0); } return 0; } #endif bool boinc_is_finite(double x) { #if defined (HPUX_SOURCE) return _Isfinite(x); #elif defined (__APPLE__) // finite() is deprecated in OS 10.9 return std::isfinite(x) != 0; #else return finite(x) != 0; #endif } #define PI2 (2*3.1415926) // generate normal random numbers using Box-Muller. // this generates 2 at a time, so cache the other one // double rand_normal() { static bool cached; static double cached_value; if (cached) { cached = false; return cached_value; } double u1 = drand(); double u2 = drand(); double z = sqrt(-2*log(u1)); cached_value = z*sin(PI2*u2); cached = true; return z*cos(PI2*u2); } // determines the real path and filename of the current process // not the current working directory // int get_real_executable_path(char* path, size_t max_len) { #if defined(__APPLE__) uint32_t size = (uint32_t)max_len; if (_NSGetExecutablePath(path, &size)) { return ERR_BUFFER_OVERFLOW; } return BOINC_SUCCESS; #elif (defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)) && defined(KERN_PROC_PATHNAME) #if defined(__DragonFly__) || defined(__FreeBSD__) int name[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; #else int name[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; #endif if (sysctl(name, 4, path, &max_len, NULL, 0)) { return errno == ENOMEM ? ERR_BUFFER_OVERFLOW : ERR_PROC_PARSE; } return BOINC_SUCCESS; #elif defined(_WIN32) DWORD length = GetModuleFileNameA(NULL, path, (DWORD)max_len); if (!length) { return ERR_PROC_PARSE; } else if (length == (DWORD)max_len) { return ERR_BUFFER_OVERFLOW; } return BOINC_SUCCESS; #else const char* links[] = { "/proc/self/exe", "/proc/curproc/exe", "/proc/self/path/a.out", "/proc/curproc/file" }; for (unsigned int i = 0; i < sizeof(links) / sizeof(links[0]); ++i) { ssize_t ret = readlink(links[i], path, max_len - 1); if (ret < 0) { if (errno != ENOENT) { boinc::perror("readlink"); } continue; } else if ((size_t)ret == max_len - 1) { return ERR_BUFFER_OVERFLOW; } path[ret] = '\0'; // readlink does not null terminate return BOINC_SUCCESS; } return ERR_NOT_IMPLEMENTED; #endif } #ifdef _WIN32 int boinc_thread_cpu_time(HANDLE thread_handle, double& cpu) { FILETIME creationTime, exitTime, kernelTime, userTime; if (GetThreadTimes( thread_handle, &creationTime, &exitTime, &kernelTime, &userTime) ) { ULARGE_INTEGER tKernel, tUser; LONGLONG totTime; tKernel.LowPart = kernelTime.dwLowDateTime; tKernel.HighPart = kernelTime.dwHighDateTime; tUser.LowPart = userTime.dwLowDateTime; tUser.HighPart = userTime.dwHighDateTime; totTime = tKernel.QuadPart + tUser.QuadPart; // Runtimes in 100-nanosecond units cpu = totTime / 1.e7; } else { return -1; } return 0; } static void get_elapsed_time(double& cpu) { static double start_time; double now = dtime(); if (start_time) { cpu = now - start_time; } else { cpu = 0; } start_time = now; } int boinc_calling_thread_cpu_time(double& cpu) { if (boinc_thread_cpu_time(GetCurrentThread(), cpu)) { get_elapsed_time(cpu); } return 0; } int boinc_process_cpu_time(HANDLE process_handle, double& cpu) { FILETIME creationTime, exitTime, kernelTime, userTime; if (GetProcessTimes( process_handle, &creationTime, &exitTime, &kernelTime, &userTime) ) { ULARGE_INTEGER tKernel, tUser; LONGLONG totTime; tKernel.LowPart = kernelTime.dwLowDateTime; tKernel.HighPart = kernelTime.dwHighDateTime; tUser.LowPart = userTime.dwLowDateTime; tUser.HighPart = userTime.dwHighDateTime; totTime = tKernel.QuadPart + tUser.QuadPart; // Runtimes in 100-nanosecond units cpu = totTime / 1.e7; } else { return -1; } return 0; } bool process_exists(HANDLE h) { unsigned long status = 1; if (GetExitCodeProcess(h, &status)) { if (status == STILL_ACTIVE) return true; } return false; } #else // Unix: pthreads doesn't provide an API for getting per-thread CPU time, // so just get the process's CPU time // int boinc_calling_thread_cpu_time(double &cpu_t) { struct rusage ru; int retval = getrusage(RUSAGE_SELF, &ru); if (retval) return ERR_GETRUSAGE; cpu_t = (double)ru.ru_utime.tv_sec + ((double)ru.ru_utime.tv_usec) / 1e6; cpu_t += (double)ru.ru_stime.tv_sec + ((double)ru.ru_stime.tv_usec) / 1e6; return 0; } #ifndef _USING_FCGI_ // (linux) return current CPU time of the given process // double linux_cpu_time(int pid) { FILE* file; char file_name[24]; unsigned long utime = 0, stime = 0; int n; snprintf(file_name, sizeof(file_name), "/proc/%d/stat", pid); if ((file = fopen(file_name, "r")) != NULL) { n = fscanf(file, "%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%lu%lu", &utime, &stime); fclose(file); if (n != 2) return 0; } return (double)(utime + stime) / 100; } #endif bool process_exists(int pid) { int retval = kill(pid, 0); if (retval == -1 && errno == ESRCH) return false; return true; } #endif