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