Anklang-0.3.0.dev502+ga23511a1 anklang-0.3.0.dev502+ga23511a1
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
platform.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "platform.hh"
3#include "path.hh"
4#include "dbus.hh"
5#include "utils.hh"
6#include "strings.hh"
7#include "internal.hh"
8#include <unistd.h>
9#include <cstring>
10#include <fstream>
11#include <atomic>
12#include <setjmp.h>
13#include <libintl.h>
14#include <sys/stat.h>
15#include <sys/wait.h>
16#include <sys/time.h>
17#include <sys/resource.h>
18#include <sys/syscall.h> // SYS_gettid
19#include <semaphore.h>
20#include <stdarg.h>
21#include <sys/types.h>
22#include <pwd.h> // getpwuid_r
23#include <algorithm>
24
25#if defined __APPLE__
26#include <mach-o/dyld.h> // _NSGetExecutablePath
27#endif // __APPLE__
28
29#ifdef _WIN32 // includes _WIN64
30#include <windows.h>
31#define LIBTOOL_OBJDIR "_libs"
32#else // !_WIN32
33#define LIBTOOL_OBJDIR ".libs"
34#endif // !_WIN32
35
36#define PDEBUG(...) Ase::debug ("Platform", __VA_ARGS__)
37
38namespace Ase {
39
40// == Runtime Path Handling ==
41static std::string determine_anklangsynthengine_installdir (bool *using_objdir);
42static std::string construct_ladspa_path ();
43
46anklang_home_dir (const String &subdir)
47{
48 const char *anklanghomeenv = getenv ("ANKLANG_HOME_DIR");
49 String anklangdir = anklanghomeenv && anklanghomeenv[0] ? anklanghomeenv :
50 Path::join (Path::xdg_dir ("DOCUMENTS"), "Anklang");
51 if (!subdir.empty())
52 anklangdir = Path::join (anklangdir, subdir);
53 return anklangdir;
54}
55
58anklang_runpath (RPath rpath, const String &segment)
59{
60 static bool using_objdir = false;
61 static const std::string libexec_installdir = determine_anklangsynthengine_installdir (&using_objdir);
62 static const std::string libexec_prefixdir = Path::dirname (libexec_installdir);
63 const char *objdir = using_objdir ? "/" LIBTOOL_OBJDIR : "";
64 switch (rpath)
65 {
66 case RPath::PREFIXDIR: return Path::join (libexec_prefixdir, segment);
67 case RPath::INSTALLDIR: return Path::join (libexec_installdir, segment);
68 case RPath::LOCALEDIR: return Path::join (libexec_installdir + "/locale", segment);
69 case RPath::LIBDIR: return Path::join (libexec_installdir + "/lib" + objdir, segment);
70 case RPath::ELECTRONDIR: return Path::join (libexec_installdir + "/electron", segment);
71 case RPath::LADSPADIRS: return Path::join (construct_ladspa_path(), segment);
72 case RPath::SAMPLEDIR: return Path::join (libexec_installdir + "/media/Samples", segment);
73 case RPath::DEMODIR: return Path::join (libexec_installdir + "/media/Demos", segment);
74 }
75 return "";
76}
77
78static std::string
79construct_ladspa_path()
80{
81 StringS sp;
82 // gather search path candidates from $LADSPA_PATH
83 const char *ladspa_path = getenv ("LADSPA_PATH");
84 if (ladspa_path)
85 sp = Path::searchpath_split (ladspa_path);
86 // add $prefix/ladspa
87 sp.push_back (anklang_runpath (RPath::INSTALLDIR) + "/ladspa");
88 sp.push_back (anklang_runpath (RPath::PREFIXDIR) + "/ladspa");
89 // add standard locations
90 sp.push_back ("/usr/lib/ladspa");
91 sp.push_back ("/usr/local/lib/ladspa");
92 sp.push_back ("/opt/lib/ladspa");
93 // convert candidates to realpath and deduplicate
94 StringS sv;
95 for (const std::string &candidate : sp)
96 {
97 const std::string dir = Path::realpath (candidate);
98 if (std::find (sv.begin(), sv.end(), dir) == sv.end() &&
99 Path::check (dir, "d"))
100 sv.push_back (dir);
101 }
102 return Path::searchpath_join (sv);
103}
104
106static const char*
107initialized_ase_gettext_domain()
108{
109 static const char *const gettexttextdomain = [] () {
110 const char *const gtdomain = ase_gettext_domain;
111 bindtextdomain (gtdomain, anklang_runpath (RPath::LOCALEDIR).c_str());
112 bind_textdomain_codeset (gtdomain, "UTF-8");
113 return gtdomain;
114 } ();
115 return gettexttextdomain;
116}
117
119const char*
120(_) (const char *string)
121{
122 return dgettext (initialized_ase_gettext_domain(), string);
123}
124
127(_) (const std::string &string)
128{
129 return _ (string.c_str());
130}
131
133const char*
134(_) (const char *string, const char *plural, int64_t n)
135{
136 const uint64_t u = n < 0 ? -n : n;
137 const ulong l = u >= 2147483647 ? 2147483647 : u;
138 return dcngettext (initialized_ase_gettext_domain(), string, plural, l, LC_MESSAGES);
139}
140
143(_) (const std::string &string, const std::string &plural, int64_t n)
144{
145 return _ (string.c_str(), plural.c_str(), n);
146}
147
148// == AnsiColors ==
149namespace AnsiColors {
150
151static std::atomic<Colorize> colorize_cache { Colorize::AUTO };
152
154void
155configure (Colorize colorize)
156{
157 atomic_store (&colorize_cache, colorize);
158}
159
161bool
163{
164 if (fd >= 0)
165 return isatty (fd);
166 const Colorize colcache = atomic_load (&colorize_cache);
167 return_unless (colcache == Colorize::AUTO, bool (colcache));
168 const char *ev = getenv ("ASE_COLOR");
169 if (ev)
170 {
171 while (ev[0] == ' ')
172 ev++;
173 if (strncasecmp (ev, "always", 6) == 0)
174 {
175 atomic_store (&colorize_cache, Colorize::ALWAYS);
176 return true;
177 }
178 else if (strncasecmp (ev, "never", 5) == 0)
179 {
180 atomic_store (&colorize_cache, Colorize::NEVER);
181 return false;
182 }
183 }
184 const bool tty_output = isatty (1) && isatty (2);
185 atomic_store (&colorize_cache, tty_output ? Colorize::ALWAYS : Colorize::NEVER);
186 return tty_output;
187}
188
191color (Colors acolor, Colors c1, Colors c2, Colors c3, Colors c4, Colors c5, Colors c6)
192{
194 std::string out = color_code (acolor);
195 out += color_code (c1);
196 out += color_code (c2);
197 out += color_code (c3);
198 out += color_code (c4);
199 out += color_code (c5);
200 out += color_code (c6);
201 return out;
202}
203
205const char*
207{
208 switch (acolor)
209 {
210 default: ;
211 case NONE: return "";
212 case RESET: return "\033[0m";
213 case BOLD: return "\033[1m";
214 case BOLD_OFF: return "\033[22m";
215 case ITALICS: return "\033[3m";
216 case ITALICS_OFF: return "\033[23m";
217 case UNDERLINE: return "\033[4m";
218 case UNDERLINE_OFF: return "\033[24m";
219 case INVERSE: return "\033[7m";
220 case INVERSE_OFF: return "\033[27m";
221 case STRIKETHROUGH: return "\033[9m";
222 case STRIKETHROUGH_OFF: return "\033[29m";
223 case FG_BLACK: return "\033[30m";
224 case FG_RED: return "\033[31m";
225 case FG_GREEN: return "\033[32m";
226 case FG_YELLOW: return "\033[33m";
227 case FG_BLUE: return "\033[34m";
228 case FG_MAGENTA: return "\033[35m";
229 case FG_CYAN: return "\033[36m";
230 case FG_WHITE: return "\033[37m";
231 case FG_DEFAULT: return "\033[39m";
232 case BG_BLACK: return "\033[40m";
233 case BG_RED: return "\033[41m";
234 case BG_GREEN: return "\033[42m";
235 case BG_YELLOW: return "\033[43m";
236 case BG_BLUE: return "\033[44m";
237 case BG_MAGENTA: return "\033[45m";
238 case BG_CYAN: return "\033[46m";
239 case BG_WHITE: return "\033[47m";
240 case BG_DEFAULT: return "\033[49m";
241 }
242}
243
244} // AnsiColors
245
246
247// == cpu_info ==
248// figure architecture name from compiler
249static const char*
250get_arch_name (void)
251{
252#if defined __alpha__
253 return "Alpha";
254#elif defined __frv__
255 return "frv";
256#elif defined __s390__
257 return "s390";
258#elif defined __m32c__
259 return "m32c";
260#elif defined sparc
261 return "Sparc";
262#elif defined __m32r__
263 return "m32r";
264#elif defined __x86_64__ || defined __amd64__
265 return "AMD64";
266#elif defined __ia64__
267 return "Intel Itanium";
268#elif defined __m68k__
269 return "mc68000";
270#elif defined __powerpc__ || defined PPC || defined powerpc || defined __PPC__
271 return "PPC";
272#elif defined __arc__
273 return "arc";
274#elif defined __arm__
275 return "Arm";
276#elif defined __mips__ || defined mips
277 return "Mips";
278#elif defined __tune_i686__ || defined __i686__
279 return "i686";
280#elif defined __tune_i586__ || defined __i586__
281 return "i586";
282#elif defined __tune_i486__ || defined __i486__
283 return "i486";
284#elif defined i386 || defined __i386__
285 return "i386";
286#else
287 return "unknown-arch";
288#warning platform.cc needs updating for this processor type
289#endif
290}
291
293struct CPUInfo {
294 // architecture name
295 const char *machine;
296 // CPU Vendor ID
297 char cpu_vendor[13];
298 // CPU features on X86
299 uint x86_fpu : 1, x86_ssesys : 1, x86_tsc : 1, x86_htt : 1;
300 uint x86_mmx : 1, x86_mmxext : 1, x86_3dnow : 1, x86_3dnowext : 1;
301 uint x86_sse : 1, x86_sse2 : 1, x86_sse3 : 1, x86_ssse3 : 1;
302 uint x86_cx16 : 1, x86_sse4_1 : 1, x86_sse4_2 : 1, x86_rdrand : 1;
303};
304
305static jmp_buf cpu_info_jmp_buf;
306
307static void ASE_NORETURN
308cpu_info_sigill_handler (int dummy)
309{
310 longjmp (cpu_info_jmp_buf, 1);
311}
312
313#if defined __i386__
314# define x86_has_cpuid() ({ \
315 unsigned int __eax = 0, __ecx = 0; \
316 __asm__ __volatile__ \
317 ( \
318 /* copy EFLAGS into eax and ecx */ \
319 "pushf ; pop %0 ; mov %0, %1 \n\t" \
320 /* toggle the ID bit and store back to EFLAGS */ \
321 "xor $0x200000, %0 ; push %0 ; popf \n\t" \
322 /* read back EFLAGS with possibly modified ID bit */ \
323 "pushf ; pop %0 \n\t" \
324 : "=a" (__eax), "=c" (__ecx) \
325 : /* no inputs */ \
326 : "cc" \
327 ); \
328 bool __result = (__eax ^ __ecx) & 0x00200000; \
329 __result; \
330})
331/* save EBX around CPUID, because gcc doesn't like it to be clobbered with -fPIC */
332# define x86_cpuid(input, count, eax, ebx, ecx, edx) \
333 __asm__ __volatile__ ( \
334 /* save ebx in esi */ \
335 "mov %%ebx, %%esi \n\t" \
336 /* get CPUID with eax=input */ \
337 "cpuid \n\t" \
338 /* swap ebx and esi */ \
339 "xchg %%ebx, %%esi" \
340 : "=a" (eax), "=S" (ebx), \
341 "=c" (ecx), "=d" (edx) \
342 : "0" (input), "2" (count) \
343 : "cc")
344#elif defined __x86_64__ || defined __amd64__
345/* CPUID is always present on AMD64, see:
346 * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/24594.pdf
347 * "AMD64 Architecture Programmer's Manual Volume 3",
348 * "Appendix D: Instruction Subsets and CPUID Feature Sets"
349 */
350# define x86_has_cpuid() (1)
351/* save EBX around CPUID, because gcc doesn't like it to be clobbered with -fPIC */
352# define x86_cpuid(input, count, eax, ebx, ecx, edx) \
353 __asm__ __volatile__ ( \
354 /* save ebx in esi */ \
355 "mov %%rbx, %%rsi \n\t" \
356 /* get CPUID with eax=input */ \
357 "cpuid \n\t" \
358 /* swap ebx and esi */ \
359 "xchg %%rbx, %%rsi" \
360 : "=a" (eax), "=S" (ebx), \
361 "=c" (ecx), "=d" (edx) \
362 : "0" (input), "2" (count) \
363 : "cc")
364#else
365# define x86_has_cpuid() (false)
366# define x86_cpuid(input, count, eax, ebx, ecx, edx) do {} while (0)
367#endif
368
369static bool
370get_x86_cpu_features (CPUInfo *ci)
371{
372 memset (ci, 0, sizeof (*ci));
373 /* check if the CPUID instruction is supported */
374 if (!x86_has_cpuid ())
375 return false;
376
377 /* query intel CPUID range */
378 unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0;
379 x86_cpuid (0, 0, eax, ebx, ecx, edx);
380 unsigned int v_ebx = ebx, v_ecx = ecx, v_edx = edx;
381 char *vendor = ci->cpu_vendor;
382 *((unsigned int*) &vendor[0]) = ebx;
383 *((unsigned int*) &vendor[4]) = edx;
384 *((unsigned int*) &vendor[8]) = ecx;
385 vendor[12] = 0;
386 if (eax >= 1) /* may query version and feature information */
387 {
388 x86_cpuid (1, 0, eax, ebx, ecx, edx);
389 if (ecx & (1 << 0))
390 ci->x86_sse3 = true;
391 if (ecx & (1 << 9))
392 ci->x86_ssse3 = true;
393 if (ecx & (1 << 13))
394 ci->x86_cx16 = true;
395 if (ecx & (1 << 19))
396 ci->x86_sse4_1 = true;
397 if (ecx & (1 << 20))
398 ci->x86_sse4_2 = true;
399 if (ecx & (1 << 30))
400 ci->x86_rdrand = true;
401 if (edx & (1 << 0))
402 ci->x86_fpu = true;
403 if (edx & (1 << 4))
404 ci->x86_tsc = true;
405 if (edx & (1 << 23))
406 ci->x86_mmx = true;
407 if (edx & (1 << 25))
408 {
409 ci->x86_sse = true;
410 ci->x86_mmxext = true;
411 }
412 if (edx & (1 << 26))
413 ci->x86_sse2 = true;
414 if (edx & (1 << 28))
415 ci->x86_htt = true;
416 /* http://www.intel.com/content/www/us/en/processors/processor-identification-cpuid-instruction-note.html
417 * "Intel Processor Identification and the CPUID Instruction"
418 */
419 }
420
421 /* query extended CPUID range */
422 x86_cpuid (0x80000000, 0, eax, ebx, ecx, edx);
423 if (eax >= 0x80000001 && /* may query extended feature information */
424 v_ebx == 0x68747541 && /* AuthenticAMD */
425 v_ecx == 0x444d4163 && v_edx == 0x69746e65)
426 {
427 x86_cpuid (0x80000001, 0, eax, ebx, ecx, edx);
428 if (edx & (1 << 31))
429 ci->x86_3dnow = true;
430 if (edx & (1 << 22))
431 ci->x86_mmxext = true;
432 if (edx & (1 << 30))
433 ci->x86_3dnowext = true;
434 /* www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25481.pdf
435 * "AMD CPUID Specification"
436 */
437 }
438
439 /* check system support for SSE */
440 if (ci->x86_sse)
441 {
442 struct sigaction action, old_action;
443 action.sa_handler = cpu_info_sigill_handler;
444 sigemptyset (&action.sa_mask);
445 action.sa_flags = SA_NOMASK;
446 sigaction (SIGILL, &action, &old_action);
447 if (setjmp (cpu_info_jmp_buf) == 0)
448 {
449#if defined __i386__ || defined __x86_64__ || defined __amd64__
450 unsigned int mxcsr;
451 __asm__ __volatile__ ("stmxcsr %0 ; sfence ; emms" : "=m" (mxcsr));
452 /* executed SIMD instructions without exception */
453 ci->x86_ssesys = true;
454#endif // x86
455 }
456 else
457 {
458 /* signal handler jumped here */
459 // g_printerr ("caught SIGILL\n");
460 }
461 sigaction (SIGILL, &old_action, NULL);
462 }
463
464 return true;
465}
466
469{
470 return get_arch_name();
471}
472
480String
482{
483 static String cpu_info_string = []() {
484 CPUInfo cpu_info = { 0, };
485 if (!get_x86_cpu_features (&cpu_info))
486 strcat (cpu_info.cpu_vendor, "Unknown");
487 cpu_info.machine = get_arch_name();
488 String info;
489 // cores
490 info += string_format ("%d", sysconf (_SC_NPROCESSORS_ONLN));
491 // architecture
492 info += String (" ") + cpu_info.machine;
493 // vendor
494 info += String (" ") + cpu_info.cpu_vendor;
495 // processor flags
496 if (cpu_info.x86_fpu)
497 info += " FPU";
498 if (cpu_info.x86_tsc)
499 info += " TSC";
500 if (cpu_info.x86_htt)
501 info += " HTT";
502 if (cpu_info.x86_cx16)
503 info += " CMPXCHG16B";
504 // MMX flags
505 if (cpu_info.x86_mmx)
506 info += " MMX";
507 if (cpu_info.x86_mmxext)
508 info += " MMXEXT";
509 // SSE flags
510 if (cpu_info.x86_ssesys)
511 info += " SSESYS";
512 if (cpu_info.x86_sse)
513 info += " SSE";
514 if (cpu_info.x86_sse2)
515 info += " SSE2";
516 if (cpu_info.x86_sse3)
517 info += " SSE3";
518 if (cpu_info.x86_ssse3)
519 info += " SSSE3";
520 if (cpu_info.x86_sse4_1)
521 info += " SSE4.1";
522 if (cpu_info.x86_sse4_2)
523 info += " SSE4.2";
524 if (cpu_info.x86_rdrand)
525 info += " rdrand";
526 // 3DNOW flags
527 if (cpu_info.x86_3dnow)
528 info += " 3DNOW";
529 if (cpu_info.x86_3dnowext)
530 info += " 3DNOWEXT";
531 info += " ";
532 return String (info.c_str());
533 }();
534 return cpu_info_string;
535}
536
537// == Timestamps ==
538static clockid_t monotonic_clockid = CLOCK_REALTIME;
539static uint64 monotonic_start = 0;
540static uint64 monotonic_resolution = 1000; // assume 1µs resolution for gettimeofday fallback
541static uint64 realtime_start = 0;
542
543static void
544timestamp_init_ ()
545{
546 static const bool initialize = [] () {
547 realtime_start = timestamp_realtime();
548 struct timespec tp = { 0, 0 };
549 if (clock_getres (CLOCK_REALTIME, &tp) >= 0)
550 monotonic_resolution = tp.tv_sec * 1000000000ULL + tp.tv_nsec;
551 uint64 mstart = realtime_start;
552#ifdef CLOCK_MONOTONIC // time.h
553 // CLOCK_MONOTONIC_RAW cannot slew, but doesn't measure SI seconds accurately
554 // CLOCK_MONOTONIC may slew, but attempts to accurately measure SI seconds
555 if (monotonic_clockid == CLOCK_REALTIME && clock_getres (CLOCK_MONOTONIC, &tp) >= 0)
556 {
557 monotonic_clockid = CLOCK_MONOTONIC;
558 monotonic_resolution = tp.tv_sec * 1000000000ULL + tp.tv_nsec;
559 mstart = timestamp_benchmark(); // here, monotonic_start=0 still
560 }
561#endif
562 monotonic_start = mstart;
563 return true;
564 } ();
565 (void) initialize;
566}
567namespace { static struct Timestamper { Timestamper() { timestamp_init_(); } } realtime_startup; } // Anon
568
570uint64
572{
573 timestamp_init_();
574 return realtime_start;
575}
576
578uint64
580{
581 struct timespec tp = { 0, 0 };
582 if (ISLIKELY (clock_gettime (CLOCK_REALTIME, &tp) >= 0))
583 return tp.tv_sec * 1000000ULL + tp.tv_nsec / 1000;
584 else
585 {
586 struct timeval now = { 0, 0 };
587 gettimeofday (&now, NULL);
588 return now.tv_sec * 1000000ULL + now.tv_usec;
589 }
590}
591
593uint64
595{
596 timestamp_init_();
597 return monotonic_resolution;
598}
599
601uint64
603{
604 struct timespec tp = { 0, 0 };
605 uint64 stamp;
606 if (ISLIKELY (clock_gettime (monotonic_clockid, &tp) >= 0))
607 {
608 stamp = tp.tv_sec * 1000000000ULL + tp.tv_nsec;
609 stamp -= monotonic_start; // reduce number of significant bits
610 }
611 else
612 {
613 stamp = timestamp_realtime() * 1000;
614 stamp -= std::min (stamp, monotonic_start); // reduce number of significant bits
615 }
616 return stamp;
617}
618
620String
621timestamp_format (uint64 stamp, uint maxlength)
622{
623 const size_t fieldwidth = std::max (maxlength, 1U);
624 const String fsecs = string_format ("%u", size_t (stamp) / 1000000);
625 const String usecs = string_format ("%06u", size_t (stamp) % 1000000);
626 String r = fsecs;
627 if (r.size() < fieldwidth)
628 r += '.';
629 if (r.size() < fieldwidth)
630 r += usecs.substr (0, fieldwidth - r.size());
631 return r;
632}
633
635uint64
637{
638 static std::atomic<uint64> global_monotonic_counter { 4294967297 };
639 return global_monotonic_counter++;
640}
641
642// == Stopwatch ==
645{
646 start (msg);
647}
648
650void
652{
653 if (!msg.empty())
654 msg_ = msg;
655 start_ = timestamp_realtime();
656}
657
659void
661{
662 end_ = timestamp_realtime();
663 String pmsg = msg.empty() ? msg_ : msg;
664 if (!pmsg.empty())
665 printerr ("%s: %.6fms\n", pmsg, milliseconds());
666}
667
669double
671{
672 return (end_ - start_) * 0.000001;
673}
674
676double
678{
679 return (end_ - start_) * 0.001;
680}
681
684{
685 if (end_ == 0)
686 stop();
687}
688
689// == program and executable names ==
690static std::string
691get_executable_path()
692{
693 const ssize_t max_size = 8100;
694 char system_result[max_size + 1 + 1] = { 0, };
695 ssize_t system_result_size = -1;
696
697#if defined __linux__ || defined __CYGWIN__ || defined __MSYS__
698 system_result_size = readlink ("/proc/self/exe", system_result, max_size);
699 if (system_result_size < 0)
700 {
701 strcpy (system_result, "/proc/self/exe");
702 system_result_size = 0;
703 }
704#elif defined __APPLE__
705 { // int _NSGetExecutablePath(char* buf, uint32_t* bufsize);
706 uint32_t bufsize = max_size;
707 if (_NSGetExecutablePath (system_result, &bufsize) == 0 &&
708 bufsize <= max_size)
709 system_result_size = bufsize;
710 }
711#elif defined _WIN32
712 // DWORD GetModuleFileNameA (HMODULE hModule, LPSTR lpFileName, DWORD size);
713 system_result_size = GetModuleFileNameA (0, system_result, max_size);
714 if (system_result_size <= 0 || system_result_size >= max_size)
715 system_result_size = -1; // error, possibly not enough space
716 else
717 {
718 system_result[system_result_size] = 0;
719 // early conversion to unix slashes
720 char *winslash;
721 while ((winslash = strchr (system_result, '\\')) != NULL)
722 *winslash = '/';
723 }
724#else
725#error "Platform lacks executable_path() implementation"
726#endif
727
728 if (system_result_size < 0)
729 system_result[0] = 0;
730 return std::string (system_result);
731}
732
736{
737 static std::string cached_executable_path = get_executable_path();
738 return cached_executable_path;
739}
740
744{
745 static std::string cached_executable_name = [] () {
747 const char *slash = strrchr (path.c_str(), '/');
748 return slash ? slash + 1 : path;
749 } ();
750 return cached_executable_name;
751}
752
753void fatal_system_error (const char *file, uint line, const char *format, ...) __attribute__ ((__format__ (printf, 3, 4), __noreturn__));
754
755void
756fatal_system_error (const char *file, uint line, const char *format, ...)
757{
758 const int maxbuffer = 8 * 1024;
759 char buffer[maxbuffer + 2] = { 0, }, *b = buffer, *e = b + maxbuffer;
760 if (file && line)
761 snprintf (b, e - b, "%s:%u: fatal: ", file, line);
762 else if (file)
763 snprintf (b, e - b, "%s: fatal: ", file);
764 else
765 snprintf (b, e - b, "fatal: ");
766 b += strlen (b);
767 va_list argv;
768 va_start (argv, format);
769 const int plen = vsnprintf (b, e - b, format, argv);
770 (void) plen;
771 va_end (argv);
772 b += strlen (b);
773 if (b > buffer && b[-1] != '\n')
774 *b++ = '\n';
775 fflush (stdout);
776 fputs (buffer, stderr);
777 fflush (stderr);
778 kill (getpid(), SIGTERM); // try to avoid apport, which is triggered by SIGABRT
779 abort();
780}
781
783static std::string
784find_text_file (ptrdiff_t symbol_address)
785{
786 const char *const self_maps = "/proc/self/maps";
787 std::ifstream maps;
788 maps.open (self_maps);
789 if (!maps.fail())
790 {
791 std::string cxxline;
792 while (std::getline (maps, cxxline))
793 {
794 size_t start = 0, end = 0, offset = 0, inode = 0, pos = 0;
795 char perms[22 + 1], device[22 + 1] = "";
796 int count = sscanf (cxxline.c_str(), "%zx-%zx %22s %zx %22s %zu %zn", &start, &end, perms, &offset, device, &inode, &pos);
797 if (count != 6 || pos == 0)
798 break;
799 if (cxxline[pos] == '/' && symbol_address >= start && symbol_address < end &&
800 strncmp (perms, "r-xp", 4) == 0)
801 return cxxline.substr (pos);
802 }
803 }
804 return ""; // not found
805}
806
807// finding the *right* path is a fundamental requirement for AnklangSynthEngine
808static std::string
809determine_anklangsynthengine_installdir (bool *using_objdir)
810{
811 // determine executable for symbol
812 const ptrdiff_t address = ptrdiff_t (&determine_anklangsynthengine_installdir);
813 String pathname = find_text_file (address);
814 const char *path = pathname.c_str(), *e = strrchr (path, '/');
815 // strip filename component to get dirname
816 if (e)
817 {
818 const ssize_t ods = strlen (LIBTOOL_OBJDIR);
819 if (strncmp (e, "/lt-", 4) == 0 && // libtool executable?
820 e - path >= 1 + ods &&
821 e[-(1 + ods)] == '/' && // in subdir
822 strncmp (e - ods, LIBTOOL_OBJDIR, ods) == 0) // named LIBTOOL_OBJDIR
823 pathname = pathname.substr (0, e - ods - 1 - path);
824 else
825 pathname = pathname.substr (0, e - path);
826 }
827 // strip suffix from $prefix/{lib|bin}
828 if (string_endswith (pathname, "/lib") || string_endswith (pathname, "/bin"))
829 pathname = pathname.substr (0, pathname.size() - 4);
830 if (pathname.empty())
831 fatal_system_error ("AnklangSynthEngine", 0, "failed to find executable for symbol: %p", (void*) address);
832 return pathname;
833}
834
835const char*
837{
838 return ase_version_short;
839}
840
841const char*
843{
844 return ase_version_long;
845}
846
847static String cached_program_alias;
848
849String
851{
852 return cached_program_alias.empty() ? executable_name() : cached_program_alias;
853}
854
855void
857{
858 assert_return (cached_program_alias.empty() == true);
859 cached_program_alias = customname;
860}
861
862static String cached_application_name;
863
864String
866{
867 return cached_application_name.empty() ? program_alias() : cached_application_name;
868}
869
870void
872{
873 assert_return (cached_application_name.empty() == true);
874 cached_application_name = desktopname;
875}
876
877String
879{
880 static String cached_program_cwd = Path::cwd();
881 return cached_program_cwd;
882}
883
884// == ScopedSemaphore ==
886{
887 static_assert (sizeof (mem_) >= sizeof (sem_t), "");
888 sem_t &sem = *(sem_t*) mem_;
889 const int ret = sem_init (&sem, 0 /*pshared*/, 0 /*value*/);
890 assert_return (ret == 0);
891}
892
893int
895{
896 sem_t &sem = *(sem_t*) mem_;
897 errno = 0;
898 const int ret = sem_post (&sem);
899 return ret ? errno : 0;
900}
901
902int
904{
905 sem_t &sem = *(sem_t*) mem_;
906 errno = 0;
907 const int ret = sem_wait (&sem);
908 return ret ? errno : 0;
909}
910
911ScopedSemaphore::~ScopedSemaphore () noexcept
912{
913 sem_t &sem = *(sem_t*) mem_;
914 const int ret = sem_destroy (&sem);
915 assert_return (ret == 0);
916}
917
918// == Scheduling ==
920int
922{
923 errno = 0;
924 const int level = getpriority (PRIO_PROCESS, tid);
925 if (level == -1 && errno)
926 PDEBUG ("getpriority(%d) failed: %s", tid, strerror (errno));
927 return level == -1 && errno ? 0 : level;
928}
929
931bool
932sched_set_priority (int tid, int nicelevel)
933{
934 errno = 0;
935 return setpriority (PRIO_PROCESS, tid, nicelevel) >= 0;
936}
937
939bool
941{
942 String emsg;
943 if (!sched_set_priority (tid, -20))
944 emsg = strerror (errno ? errno : EPERM);
945 if (!emsg.empty())
946 {
947 const int level = DBus::rtkit_get_min_nice_level();
948 if (level < 0)
949 emsg = DBus::rtkit_make_high_priority (tid, level);
950 }
951 if (emsg.empty())
952 PDEBUG ("acquired low latency scheduling for pid=%d: %d", tid, sched_get_priority (tid));
953 else
954 PDEBUG ("failed to acquire low latency scheduling for pid=%d: %s", tid, emsg);
955 return emsg.empty();
956}
957
958// == TaskStatus ==
959TaskStatus::TaskStatus (int pid, int tid) :
960 process_id (pid), task_id (tid >= 0 ? tid : pid), state (UNKNOWN), processor (-1), priority (0),
961 utime (0), stime (0), cutime (0), cstime (0),
962 ac_stamp (0), ac_utime (0), ac_stime (0), ac_cutime (0), ac_cstime (0)
963{}
964
965static bool
966update_task_status (TaskStatus &self)
967{
968 static long clk_tck = 0;
969 if (!clk_tck)
970 {
971 clk_tck = sysconf (_SC_CLK_TCK);
972 if (clk_tck <= 0)
973 clk_tck = 100;
974 }
975 int pid = -1, ppid = -1, pgrp = -1, session = -1, tty_nr = -1, tpgid = -1;
976 int exit_signal = 0, processor = 0;
977 long cutime = 0, cstime = 0, priority = 0, nice = 0, dummyld = 0;
978 long itrealvalue = 0, rss = 0;
979 unsigned long flags = 0, minflt = 0, cminflt = 0, majflt = 0, cmajflt = 0;
980 unsigned long utime = 0, stime = 0, vsize = 0, rlim = 0, startcode = 0;
981 unsigned long endcode = 0, startstack = 0, kstkesp = 0, kstkeip = 0;
982 unsigned long signal = 0, blocked = 0, sigignore = 0, sigcatch = 0;
983 unsigned long wchan = 0, nswap = 0, cnswap = 0, rt_priority = 0, policy = 0;
984 unsigned long long starttime = 0;
985 char state = 0, command[8192 + 1] = { 0 };
986 String filename = string_format ("/proc/%u/task/%u/stat", self.process_id, self.task_id);
987 FILE *file = fopen (filename.c_str(), "r");
988 if (!file)
989 return false;
990 int n = fscanf (file,
991 "%d %8192s %c "
992 "%d %d %d %d %d "
993 "%lu %lu %lu %lu %lu %lu %lu "
994 "%ld %ld %ld %ld %ld %ld "
995 "%llu %lu %ld "
996 "%lu %lu %lu %lu %lu "
997 "%lu %lu %lu %lu %lu "
998 "%lu %lu %lu %d %d "
999 "%lu %lu",
1000 &pid, command, &state, // n=3
1001 &ppid, &pgrp, &session, &tty_nr, &tpgid, // n=8
1002 &flags, &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime, // n=15
1003 &cutime, &cstime, &priority, &nice, &dummyld, &itrealvalue, // n=21
1004 &starttime, &vsize, &rss, // n=24
1005 &rlim, &startcode, &endcode, &startstack, &kstkesp, // n=29
1006 &kstkeip, &signal, &blocked, &sigignore, &sigcatch, // n=34
1007 &wchan, &nswap, &cnswap, &exit_signal, &processor, // n=39
1008 &rt_priority, &policy // n=41
1009 );
1010 fclose (file);
1011 const double jiffies_to_usecs = 1000000.0 / clk_tck;
1012 if (n >= 3)
1013 self.state = TaskStatus::State (state);
1014 if (n >= 15)
1015 {
1016 self.ac_utime = utime * jiffies_to_usecs;
1017 self.ac_stime = stime * jiffies_to_usecs;
1018 }
1019 if (n >= 17)
1020 {
1021 self.ac_cutime = cutime * jiffies_to_usecs;
1022 self.ac_cstime = cstime * jiffies_to_usecs;
1023 }
1024 if (n >= 18)
1025 self.priority = priority;
1026 if (n >= 39)
1027 self.processor = 1 + processor;
1028 return true;
1029}
1030
1031#define ACCOUNTING_MSECS 50
1032
1033bool
1035{
1036 const TaskStatus old (*this);
1037 const uint64 now = timestamp_realtime(); // usecs
1038 if (ac_stamp + ACCOUNTING_MSECS * 1000 >= now)
1039 return false; // limit accounting to a few times per second
1040 if (!update_task_status (*this))
1041 return false;
1042 const double delta = 1000000.0 / MAX (1, now - ac_stamp);
1043 utime = uint64 (MAX (ac_utime - old.ac_utime, 0) * delta);
1044 stime = uint64 (MAX (ac_stime - old.ac_stime, 0) * delta);
1045 cutime = uint64 (MAX (ac_cutime - old.ac_cutime, 0) * delta);
1046 cstime = uint64 (MAX (ac_cstime - old.ac_cstime, 0) * delta);
1047 ac_stamp = now;
1048 return true;
1049}
1050
1051String
1053{
1054 return
1055 string_format ("pid=%d task=%d state=%c processor=%d priority=%d perc=%.2f%% utime=%.3fms stime=%.3fms cutime=%.3f cstime=%.3f",
1057 utime * 0.001, stime * 0.001, cutime * 0.001, cstime * 0.001);
1058}
1059
1060// == TaskRegistry ==
1061static std::mutex task_registry_mutex_;
1062static TaskRegistry::List task_registry_tasks_;
1063
1064void
1065TaskRegistry::add (const std::string &name, int pid, int tid)
1066{
1067 Ase::TaskStatus task (pid, tid);
1068 task.name = name;
1069 task.update();
1070 std::lock_guard<std::mutex> locker (task_registry_mutex_);
1071 task_registry_tasks_.push_back (task);
1072}
1073
1074bool
1076{
1077 std::lock_guard<std::mutex> locker (task_registry_mutex_);
1078 for (auto it = task_registry_tasks_.begin(); it != task_registry_tasks_.end(); it++)
1079 if (it->task_id == tid)
1080 {
1081 task_registry_tasks_.erase (it);
1082 return true;
1083 }
1084 return false;
1085}
1086
1087void
1089{
1090 std::lock_guard<std::mutex> locker (task_registry_mutex_);
1091 for (auto &task : task_registry_tasks_)
1092 task.update();
1093}
1094
1097{
1098 std::lock_guard<std::mutex> locker (task_registry_mutex_);
1099 return task_registry_tasks_;
1100}
1101
1102void
1103TaskRegistry::setup_ase (const String &name16chars)
1104{
1105 assert_return (ase_thread_id == std::thread::id{});
1106 ase_thread_id = std::this_thread::get_id();
1107 assert_return (ase_thread_id != std::thread::id{});
1108 this_thread_set_name (name16chars);
1109}
1110
1111std::thread::id TaskRegistry::ase_thread_id = {};
1112
1113// == Thread Info ==
1115this_thread_self ()
1116{
1117 return std::this_thread::get_id();
1118}
1119
1120void
1121this_thread_set_name (const String &name16chars)
1122{
1123 if (name16chars.find (" ") != name16chars.npos)
1124 ::Ase::assertion_failed (string_format ("new thread name contains spaces: \"%s\"", name16chars).c_str());
1125 pthread_setname_np (pthread_self(), name16chars.c_str());
1126}
1127
1128String
1129this_thread_get_name ()
1130{
1131 char buffer[1024] = { 0, };
1132 pthread_getname_np (pthread_self(), buffer, sizeof (buffer) - 1);
1133 buffer[sizeof (buffer) - 1] = 0;
1134 return buffer;
1135}
1136
1137int
1138this_thread_getpid ()
1139{
1140 return getpid();
1141}
1142
1143int
1144this_thread_gettid ()
1145{
1146 int tid = -1;
1147#ifdef __linux__ // SYS_gettid is present on linux >= 2.4.20
1148 tid = syscall (SYS_gettid);
1149#endif
1150 if (tid < 0)
1151 tid = this_thread_getpid();
1152 return tid;
1153}
1154
1155int
1156this_thread_online_cpus ()
1157{
1158 static int cpus = 0;
1159 if (!cpus)
1160 cpus = sysconf (_SC_NPROCESSORS_ONLN);
1161 return cpus;
1162}
1163
1164// == user ==
1165struct PwdUser {
1166 int uid;
1167 String user_name;
1168 String real_name;
1169};
1170
1171static PwdUser
1172get_pwd_user (int uid)
1173{
1174 PwdUser pu { uid };
1175 struct passwd spwd = { nullptr, }, *pp = nullptr;
1176 char buffer[4096];
1177 if (getpwuid_r (pu.uid, &spwd, buffer, sizeof (buffer), &pp) != 0)
1178 pp = nullptr;
1179 pu.user_name = pp && pp->pw_name && pp->pw_name[0] ? pp->pw_name : "_unknown";
1180 if (pp && pp->pw_gecos && pp->pw_gecos[0])
1181 pu.real_name = string_split (pp->pw_gecos, ",")[0];
1182 memset (buffer, 0, sizeof (buffer));
1183 assert_return (spwd.pw_passwd[0] == 0, pu);
1184 return pu;
1185}
1186
1187static const PwdUser&
1188current_user()
1189{
1190 static PwdUser pu = get_pwd_user (getuid());
1191 return pu;
1192}
1193
1194int
1195user_id ()
1196{
1197 return current_user().uid;
1198}
1199
1200String
1201user_name ()
1202{
1203 return current_user().user_name;
1204}
1205
1206String
1207user_real_name ()
1208{
1209 const PwdUser &pu = current_user();
1210 return pu.real_name.empty() ? pu.user_name : pu.real_name;
1211}
1212
1213// == Early Startup ctors ==
1214namespace {
1215struct EarlyStartup {
1216 EarlyStartup()
1217 {
1218 timestamp_init_();
1219 program_cwd(); // initialize early, i.e. before main() changes cwd
1220 }
1221};
1222static EarlyStartup _early_startup __attribute__ ((init_priority (101)));
1223} // Anon
1224
1225} // Ase
1226
1227// == Testing ==
1228#include "testing.hh"
1229namespace { // Anon
1230using namespace Ase;
1231
1232TEST_INTEGRITY (ase_test_timestamps);
1233static void
1234ase_test_timestamps()
1235{
1236 const uint64 b1 = timestamp_benchmark();
1242 for (size_t i = 0; i < 999999; i++)
1243 {
1244 const uint64 last = c;
1245 c = monotonic_counter();
1246 TASSERT (c > last);
1247 }
1248 const uint64 b2 = timestamp_benchmark();
1249 TASSERT (b1 < b2);
1250}
1251
1252} // Anon
#define EPERM
abort
T begin(T... args)
T c_str(T... args)
int wait() noexcept
Unlock ScopedSemaphore.
Definition platform.cc:903
ScopedSemaphore() noexcept
Create a process-local semaphore.
Definition platform.cc:885
int post() noexcept
Unlock ScopedSemaphore.
Definition platform.cc:894
~Stopwatch()
Stop and print a previous msg if still running.
Definition platform.cc:683
void stop(const String &msg="")
Stop stop watch, print msg.
Definition platform.cc:660
Stopwatch(const String &msg="")
Automatically start stop watch.
Definition platform.cc:644
double seconds() const
Provide seconds elapsed between start() and stop().
Definition platform.cc:670
void start(const String &msg="")
Start or restart stop watch, printing msg later on.
Definition platform.cc:651
double milliseconds() const
Provide milliseconds elapsed between start() and stop().
Definition platform.cc:677
static void add(const std::string &name, int pid, int tid=-1)
Add process/thread to registry for runtime profiling.
Definition platform.cc:1065
static void update()
Issue TaskStatus.update on all tasks in registry.
Definition platform.cc:1088
static bool remove(int tid)
Remove process/thread based on thread_id.
Definition platform.cc:1075
static List list()
Retrieve a copy to the list of all tasks in registry.
Definition platform.cc:1096
clock_gettime
T count(T... args)
snprintf
T empty(T... args)
T end(T... args)
T erase(T... args)
errno
T fail(T... args)
fclose
fflush
T find(T... args)
fopen
fputs
sscanf
T get_id(T... args)
getenv
T getline(T... args)
getpid
getpriority
gettimeofday
getuid
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
Definition internal.hh:29
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
Definition internal.hh:71
#define _(...)
Retrieve the translation of a C or C++ string.
Definition internal.hh:18
#define TEST_INTEGRITY(FUNC)
Register func as an integrity test.
Definition internal.hh:77
#define MAX(a, b)
Yield maximum of a and b.
Definition internal.hh:52
#define ISLIKELY(cond)
Hint to the compiler to optimize for cond == TRUE.
Definition internal.hh:61
isatty
kill
longjmp
T max(T... args)
memset
T min(T... args)
std::string color(Colors acolor, Colors c1, Colors c2, Colors c3, Colors c4, Colors c5, Colors c6)
Return ANSI code for the specified color if stdout & stderr should be colorized, see colorize_tty().
Definition platform.cc:191
Colors
ANSI color symbols.
Definition platform.hh:44
@ RESET
Reset combines BOLD_OFF, ITALICS_OFF, UNDERLINE_OFF, INVERSE_OFF, STRIKETHROUGH_OFF.
Definition platform.hh:46
void configure(Colorize colorize)
Override the environment variable $ASE_COLOR (which may contain "always", "never" or "auto").
Definition platform.cc:155
bool colorize_tty(int fd)
Check whether the tty fd should use colorization, checks ASE_COLOR if fd == -1.
Definition platform.cc:162
const char * color_code(Colors acolor)
Return ANSI code for the specified color.
Definition platform.cc:206
String cwd()
Return the current working directoy, including symlinks used in $PWD if available.
Definition path.cc:662
String realpath(const String &path)
Resolve links and directory references in path and provide a canonicalized absolute pathname.
Definition path.cc:93
String dirname(const String &path)
Retrieve the directory part of the filename path.
Definition path.cc:60
bool check(const String &file, const String &mode)
Definition path.cc:625
The Anklang C++ API namespace.
Definition api.hh:9
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
uint64 timestamp_resolution()
Provide resolution of timestamp_benchmark() in nano-seconds.
Definition platform.cc:594
uint64_t uint64
A 64-bit unsigned integer, use PRI*64 in format strings.
Definition cxxaux.hh:25
String cpu_info()
Retrieve string identifying the runtime CPU type.
Definition platform.cc:481
void assertion_failed(const char *msg, const char *file, int line, const char *func) noexcept
Print instructive message, handle "breakpoint", "backtrace" and "fatal-warnings" in $ASE_DEBUG.
Definition cxxaux.cc:122
uint64 monotonic_counter()
A monotonically increasing counter, increments are atomic and visible in all threads.
Definition platform.cc:636
String application_name()
Retrieve the localized program name intended for user display.
Definition platform.cc:865
StringS string_split(const String &string, const String &splitter, size_t maxn)
Definition strings.cc:343
uint64 timestamp_benchmark()
Returns benchmark timestamp in nano-seconds, clock starts around program startup.
Definition platform.cc:602
std::string executable_path()
Retrieve the path to the currently running executable.
Definition platform.cc:735
void program_alias_init(String customname)
Set program_alias to a non-localized alias other than program_argv0 if desired.
Definition platform.cc:856
void application_name_init(String desktopname)
Set the application_name to a name other than program_alias if desired.
Definition platform.cc:871
std::string anklang_runpath(RPath rpath, const String &segment)
Retrieve various resource paths at runtime.
Definition platform.cc:58
bool sched_set_priority(int tid, int nicelevel)
Try to set the nice level of process or thread tid to nicelevel.
Definition platform.cc:932
String program_cwd()
The current working directory during startup.
Definition platform.cc:878
String anklang_home_dir(const String &subdir)
Get Anklang home dir, possibly adding subdir.
Definition platform.cc:46
std::string cpu_arch()
Retrieve string identifying the CPU architecture.
Definition platform.cc:468
bool sched_fast_priority(int tid)
Try to acquire low latency scheduling priority, returns true if nice level is < 0.
Definition platform.cc:940
String program_alias()
Retrieve the program name as used for logging or debug messages.
Definition platform.cc:850
const char * ase_version()
Provide a string containing the package version.
Definition platform.cc:836
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
std::string executable_name()
Retrieve the name part of executable_path().
Definition platform.cc:743
uint32_t uint
Provide 'uint' as convenience type.
Definition cxxaux.hh:18
bool string_endswith(const String &string, const String &fragment)
Returns whether string ends with fragment.
Definition strings.cc:863
int sched_get_priority(int tid)
Retrieve the nice level of process or thread tid.
Definition platform.cc:921
uint64 timestamp_startup()
Provides the timestamp_realtime() value from program startup.
Definition platform.cc:571
uint64 timestamp_realtime()
Return the current time as uint64 in µseconds.
Definition platform.cc:579
String timestamp_format(uint64 stamp, uint maxlength)
Convert stamp into a string, adding µsecond fractions if space permits.
Definition platform.cc:621
const char * ase_build_id()
Provide a string containing the ASE library build id.
Definition platform.cc:842
Acquire information about the runtime architecture and CPU type.
Definition platform.cc:293
nice
T open(T... args)
pthread_self
T push_back(T... args)
readlink
sem_destroy
sem_init
sem_post
sem_wait
sigaction
sigemptyset
sigignore
signal
T size(T... args)
typedef uint64_t
strcpy
strncasecmp
strcat
strerror
strlen
strrchr
Acquire information about a task (process or thread) at runtime.
Definition platform.hh:136
State state
Thread state.
Definition platform.hh:141
int priority
Priority or nice value.
Definition platform.hh:143
String string()
Retrieve string representation of the status information.
Definition platform.cc:1052
uint64 utime
Userspace time.
Definition platform.hh:144
uint64 ac_stamp
Accounting stamp.
Definition platform.hh:148
uint64 cutime
Userspace time of dead children.
Definition platform.hh:146
TaskStatus(int pid, int tid=-1)
Construct from process ID and optionally thread ID.
Definition platform.cc:959
int processor
Rrunning processor number.
Definition platform.hh:142
uint64 cstime
System time of dead children.
Definition platform.hh:147
bool update()
Update status information, might return false if called too frequently.
Definition platform.cc:1034
int process_id
Process ID.
Definition platform.hh:138
int task_id
Process ID or thread ID.
Definition platform.hh:139
uint64 stime
System time.
Definition platform.hh:145
String name
Thread name (set by user).
Definition platform.hh:140
T substr(T... args)
typedef clockid_t
sysconf
#define TASSERT(cond)
Unconditional test assertion, enters breakpoint if not fullfilled.
Definition testing.hh:24
utime
va_start
vsnprintf