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.

400 lines
9.3KB

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