You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

420 lines
10.0KB

  1. #include <system.hpp>
  2. #include <string.hpp>
  3. #include <thread>
  4. #include <regex>
  5. #include <dirent.h>
  6. #include <sys/stat.h>
  7. #include <cxxabi.h> // for __cxxabiv1::__cxa_demangle
  8. #if defined ARCH_LIN || defined ARCH_MAC
  9. #include <pthread.h>
  10. #include <sched.h>
  11. #include <execinfo.h> // for backtrace and backtrace_symbols
  12. #include <unistd.h> // for execl
  13. #include <sys/utsname.h>
  14. #endif
  15. #if defined ARCH_MAC
  16. #include <mach/mach_init.h>
  17. #include <mach/thread_act.h>
  18. #endif
  19. #if defined ARCH_WIN
  20. #define _WIN32_WINNT _WIN32_WINNT_VISTA // for QueryThreadCycleTime
  21. #include <windows.h>
  22. #include <shellapi.h>
  23. #include <processthreadsapi.h>
  24. #include <dbghelp.h>
  25. #endif
  26. #define ZIP_STATIC
  27. #include <zip.h>
  28. namespace rack {
  29. namespace system {
  30. std::list<std::string> getEntries(const std::string& path) {
  31. std::list<std::string> filenames;
  32. DIR* dir = opendir(path.c_str());
  33. if (dir) {
  34. struct dirent* d;
  35. while ((d = readdir(dir))) {
  36. std::string filename = d->d_name;
  37. if (filename == "." || filename == "..")
  38. continue;
  39. filenames.push_back(path + "/" + filename);
  40. }
  41. closedir(dir);
  42. }
  43. filenames.sort();
  44. return filenames;
  45. }
  46. std::list<std::string> getEntriesRecursive(const std::string &path, int depth) {
  47. std::list<std::string> entries = getEntries(path);
  48. if (depth > 0) {
  49. // Don't iterate using iterators because the list will be growing.
  50. size_t limit = entries.size();
  51. auto it = entries.begin();
  52. for (size_t i = 0; i < limit; i++) {
  53. const std::string &entry = *it++;
  54. if (isDirectory(entry)) {
  55. std::list<std::string> subEntries = getEntriesRecursive(entry, depth - 1);
  56. // Append subEntries to entries
  57. entries.splice(entries.end(), subEntries);
  58. }
  59. }
  60. }
  61. return entries;
  62. }
  63. bool isFile(const std::string& path) {
  64. struct stat statbuf;
  65. if (stat(path.c_str(), &statbuf))
  66. return false;
  67. return S_ISREG(statbuf.st_mode);
  68. }
  69. bool isDirectory(const std::string& path) {
  70. struct stat statbuf;
  71. if (stat(path.c_str(), &statbuf))
  72. return false;
  73. return S_ISDIR(statbuf.st_mode);
  74. }
  75. void moveFile(const std::string& srcPath, const std::string& destPath) {
  76. std::remove(destPath.c_str());
  77. // Whether this overwrites existing files is implementation-defined.
  78. // i.e. Mingw64 fails to overwrite.
  79. // This is why we remove the file above.
  80. std::rename(srcPath.c_str(), destPath.c_str());
  81. }
  82. void copyFile(const std::string& srcPath, const std::string& destPath) {
  83. // Open source
  84. FILE* source = fopen(srcPath.c_str(), "rb");
  85. if (!source)
  86. return;
  87. DEFER({
  88. fclose(source);
  89. });
  90. // Open destination
  91. FILE* dest = fopen(destPath.c_str(), "wb");
  92. if (!dest)
  93. return;
  94. DEFER({
  95. fclose(dest);
  96. });
  97. // Copy buffer
  98. const int bufferSize = (1 << 15);
  99. char buffer[bufferSize];
  100. while (1) {
  101. size_t size = fread(buffer, 1, bufferSize, source);
  102. if (size == 0)
  103. break;
  104. size = fwrite(buffer, 1, size, dest);
  105. if (size == 0)
  106. break;
  107. }
  108. }
  109. void createDirectory(const std::string& path) {
  110. #if defined ARCH_WIN
  111. std::wstring pathW = string::toWstring(path);
  112. CreateDirectoryW(pathW.c_str(), NULL);
  113. #else
  114. mkdir(path.c_str(), 0755);
  115. #endif
  116. }
  117. int getLogicalCoreCount() {
  118. return std::thread::hardware_concurrency();
  119. }
  120. void setThreadName(const std::string& name) {
  121. #if defined ARCH_LIN
  122. pthread_setname_np(pthread_self(), name.c_str());
  123. #elif defined ARCH_WIN
  124. // Unsupported on Windows
  125. #endif
  126. }
  127. void setThreadRealTime(bool realTime) {
  128. #if defined ARCH_LIN
  129. int err;
  130. int policy;
  131. struct sched_param param;
  132. if (realTime) {
  133. // Round-robin scheduler policy
  134. policy = SCHED_RR;
  135. param.sched_priority = sched_get_priority_max(policy);
  136. }
  137. else {
  138. // Default scheduler policy
  139. policy = 0;
  140. param.sched_priority = 0;
  141. }
  142. err = pthread_setschedparam(pthread_self(), policy, &param);
  143. assert(!err);
  144. // pthread_getschedparam(pthread_self(), &policy, &param);
  145. // DEBUG("policy %d priority %d", policy, param.sched_priority);
  146. #elif defined ARCH_MAC
  147. // Not yet implemented
  148. #elif defined ARCH_WIN
  149. // Set process class first
  150. if (realTime) {
  151. SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
  152. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
  153. }
  154. else {
  155. SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
  156. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
  157. }
  158. #endif
  159. }
  160. double getThreadTime() {
  161. #if defined ARCH_LIN
  162. struct timespec ts;
  163. clockid_t cid;
  164. pthread_getcpuclockid(pthread_self(), &cid);
  165. clock_gettime(cid, &ts);
  166. return ts.tv_sec + ts.tv_nsec * 1e-9;
  167. #elif defined ARCH_MAC
  168. mach_port_t thread = mach_thread_self();
  169. mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
  170. thread_basic_info_data_t info;
  171. kern_return_t kr = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t) &info, &count);
  172. if (kr != KERN_SUCCESS || (info.flags & TH_FLAGS_IDLE) != 0)
  173. return 0.0;
  174. return info.user_time.seconds + info.user_time.microseconds * 1e-6;
  175. #elif defined ARCH_WIN
  176. // FILETIME creationTime;
  177. // FILETIME exitTime;
  178. // FILETIME kernelTime;
  179. // FILETIME userTime;
  180. // GetThreadTimes(GetCurrentThread(), &creationTime, &exitTime, &kernelTime, &userTime);
  181. // return ((uint64_t(userTime.dwHighDateTime) << 32) + userTime.dwLowDateTime) * 1e-7;
  182. uint64_t cycles;
  183. QueryThreadCycleTime(GetCurrentThread(), &cycles);
  184. // HACK Assume that the RDTSC Time-Step Counter instruction is fixed at 2.5GHz. This should only be within a factor of 2 on all PCs.
  185. const double freq = 2.5e9;
  186. return (double) cycles / freq;
  187. #endif
  188. }
  189. std::string getStackTrace() {
  190. int stackLen = 128;
  191. void* stack[stackLen];
  192. std::string s;
  193. #if defined ARCH_LIN || defined ARCH_MAC
  194. stackLen = backtrace(stack, stackLen);
  195. char** strings = backtrace_symbols(stack, stackLen);
  196. // Skip the first line because it's this function.
  197. for (int i = 1; i < stackLen; i++) {
  198. s += string::f("%d: ", stackLen - i - 1);
  199. std::string line = strings[i];
  200. #if 0
  201. // Parse line
  202. std::regex r(R"((.*)\((.*)\+(.*)\) (.*))");
  203. std::smatch match;
  204. if (std::regex_search(line, match, r)) {
  205. s += match[1].str();
  206. s += "(";
  207. std::string symbol = match[2].str();
  208. // Demangle symbol
  209. char* symbolD = __cxxabiv1::__cxa_demangle(symbol.c_str(), NULL, NULL, NULL);
  210. if (symbolD) {
  211. symbol = symbolD;
  212. free(symbolD);
  213. }
  214. s += symbol;
  215. s += "+";
  216. s += match[3].str();
  217. s += ")";
  218. }
  219. #else
  220. s += line;
  221. #endif
  222. s += "\n";
  223. }
  224. free(strings);
  225. #elif defined ARCH_WIN
  226. HANDLE process = GetCurrentProcess();
  227. SymInitialize(process, NULL, true);
  228. stackLen = CaptureStackBackTrace(0, stackLen, stack, NULL);
  229. SYMBOL_INFO* symbol = (SYMBOL_INFO*) calloc(sizeof(SYMBOL_INFO) + 256, 1);
  230. symbol->MaxNameLen = 255;
  231. symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
  232. for (int i = 1; i < stackLen; i++) {
  233. SymFromAddr(process, (DWORD64) stack[i], 0, symbol);
  234. s += string::f("%d: %s 0x%0x\n", stackLen - i - 1, symbol->Name, symbol->Address);
  235. }
  236. free(symbol);
  237. #endif
  238. return s;
  239. }
  240. void openBrowser(const std::string& url) {
  241. #if defined ARCH_LIN
  242. std::string command = "xdg-open \"" + url + "\"";
  243. (void) std::system(command.c_str());
  244. #endif
  245. #if defined ARCH_MAC
  246. std::string command = "open \"" + url + "\"";
  247. std::system(command.c_str());
  248. #endif
  249. #if defined ARCH_WIN
  250. std::wstring urlW = string::toWstring(url);
  251. ShellExecuteW(NULL, L"open", urlW.c_str(), NULL, NULL, SW_SHOWDEFAULT);
  252. #endif
  253. }
  254. void openFolder(const std::string& path) {
  255. #if defined ARCH_LIN
  256. std::string command = "xdg-open \"" + path + "\"";
  257. (void) std::system(command.c_str());
  258. #endif
  259. #if defined ARCH_MAC
  260. std::string command = "open \"" + path + "\"";
  261. std::system(command.c_str());
  262. #endif
  263. #if defined ARCH_WIN
  264. std::wstring pathW = string::toWstring(path);
  265. ShellExecuteW(NULL, L"explore", pathW.c_str(), NULL, NULL, SW_SHOWDEFAULT);
  266. #endif
  267. }
  268. void runProcessDetached(const std::string& path) {
  269. #if defined ARCH_WIN
  270. SHELLEXECUTEINFOW shExInfo;
  271. ZeroMemory(&shExInfo, sizeof(shExInfo));
  272. shExInfo.cbSize = sizeof(shExInfo);
  273. shExInfo.lpVerb = L"runas";
  274. std::wstring pathW = string::toWstring(path);
  275. shExInfo.lpFile = pathW.c_str();
  276. shExInfo.nShow = SW_SHOW;
  277. if (ShellExecuteExW(&shExInfo)) {
  278. // Do nothing
  279. }
  280. #else
  281. // Not implemented on Linux or Mac
  282. assert(0);
  283. #endif
  284. }
  285. std::string getOperatingSystemInfo() {
  286. #if defined ARCH_LIN || defined ARCH_MAC
  287. struct utsname u;
  288. uname(&u);
  289. return string::f("%s %s %s %s", u.sysname, u.release, u.version, u.machine);
  290. #elif defined ARCH_WIN
  291. OSVERSIONINFOW info;
  292. ZeroMemory(&info, sizeof(info));
  293. info.dwOSVersionInfoSize = sizeof(info);
  294. GetVersionExW(&info);
  295. // See https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_osversioninfoa for a list of Windows version numbers.
  296. return string::f("Windows %u.%u", info.dwMajorVersion, info.dwMinorVersion);
  297. #endif
  298. }
  299. int unzipToFolder(const std::string& zipPath, const std::string& dir) {
  300. int err;
  301. // Open ZIP file
  302. zip_t* za = zip_open(zipPath.c_str(), 0, &err);
  303. if (!za) {
  304. WARN("Could not open ZIP file %s: error %d", zipPath.c_str(), err);
  305. return err;
  306. }
  307. DEFER({
  308. zip_close(za);
  309. });
  310. // Iterate ZIP entries
  311. for (int i = 0; i < zip_get_num_entries(za, 0); i++) {
  312. zip_stat_t zs;
  313. err = zip_stat_index(za, i, 0, &zs);
  314. if (err) {
  315. WARN("zip_stat_index() failed: error %d", err);
  316. return err;
  317. }
  318. std::string path = dir + "/" + zs.name;
  319. if (path[path.size() - 1] == '/') {
  320. // Create directory
  321. system::createDirectory(path);
  322. // HACK
  323. // Create and delete file to update the directory's mtime.
  324. std::string tmpPath = path + "/.tmp";
  325. FILE* tmpFile = fopen(tmpPath.c_str(), "w");
  326. fclose(tmpFile);
  327. std::remove(tmpPath.c_str());
  328. }
  329. else {
  330. // Open ZIP entry
  331. zip_file_t* zf = zip_fopen_index(za, i, 0);
  332. if (!zf) {
  333. WARN("zip_fopen_index() failed");
  334. return -1;
  335. }
  336. DEFER({
  337. zip_fclose(zf);
  338. });
  339. // Create file
  340. FILE* outFile = fopen(path.c_str(), "wb");
  341. if (!outFile) {
  342. WARN("Could not create file %s", path.c_str());
  343. return -1;
  344. }
  345. DEFER({
  346. fclose(outFile);
  347. });
  348. // Read buffer and copy to file
  349. while (true) {
  350. char buffer[1 << 15];
  351. int len = zip_fread(zf, buffer, sizeof(buffer));
  352. if (len <= 0)
  353. break;
  354. fwrite(buffer, 1, len, outFile);
  355. }
  356. }
  357. }
  358. return 0;
  359. }
  360. } // namespace system
  361. } // namespace rack