diff options
Diffstat (limited to 'src/base/utils.cc')
-rw-r--r-- | src/base/utils.cc | 214 |
1 files changed, 181 insertions, 33 deletions
diff --git a/src/base/utils.cc b/src/base/utils.cc index d5bd5a0d7..b4da80cb1 100644 --- a/src/base/utils.cc +++ b/src/base/utils.cc @@ -14,22 +14,34 @@ * limitations under the License. */ -#include "perfetto/ext/base/file_utils.h" #include "perfetto/ext/base/utils.h" +#include <string> + #include "perfetto/base/build_config.h" #include "perfetto/base/logging.h" +#include "perfetto/ext/base/file_utils.h" #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ - PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) + PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) || \ + PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) +#include <limits.h> +#include <stdlib.h> // For _exit() #include <unistd.h> // For getpagesize() and geteuid() & fork() #endif #if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) +#include <mach-o/dyld.h> #include <mach/vm_page_size.h> #endif +#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) +#include <Windows.h> +#include <io.h> +#include <malloc.h> // For _aligned_malloc(). +#endif + #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) #include <dlfcn.h> #include <malloc.h> @@ -39,7 +51,7 @@ #else // Only available in in-tree builds and on newer SDKs. #define PERFETTO_M_PURGE -101 -#endif +#endif // M_PURGE namespace { extern "C" { @@ -48,6 +60,57 @@ using MalloptType = void (*)(int, int); } // namespace #endif // OS_ANDROID +namespace { + +#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT) + +// Preserve the %rbx register via %rdi to work around a clang bug +// https://bugs.llvm.org/show_bug.cgi?id=17907 (%rbx in an output constraint +// is not considered a clobbered register). +#define PERFETTO_GETCPUID(a, b, c, d, a_inp, c_inp) \ + asm("mov %%rbx, %%rdi\n" \ + "cpuid\n" \ + "xchg %%rdi, %%rbx\n" \ + : "=a"(a), "=D"(b), "=c"(c), "=d"(d) \ + : "a"(a_inp), "2"(c_inp)) + +uint32_t GetXCR0EAX() { + uint32_t eax = 0, edx = 0; + asm("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0)); + return eax; +} + +// If we are building with -msse4 check that the CPU actually supports it. +// This file must be kept in sync with gn/standalone/BUILD.gn. +void PERFETTO_EXPORT __attribute__((constructor)) CheckCpuOptimizations() { + uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; + PERFETTO_GETCPUID(eax, ebx, ecx, edx, 1, 0); + + static constexpr uint64_t xcr0_xmm_mask = 0x2; + static constexpr uint64_t xcr0_ymm_mask = 0x4; + static constexpr uint64_t xcr0_avx_mask = xcr0_xmm_mask | xcr0_ymm_mask; + + const bool have_popcnt = ecx & (1u << 23); + const bool have_sse4_2 = ecx & (1u << 20); + const bool have_avx = + // Does the OS save/restore XMM and YMM state? + (ecx & (1u << 27)) && // OS support XGETBV. + (ecx & (1u << 28)) && // AVX supported in hardware + ((GetXCR0EAX() & xcr0_avx_mask) == xcr0_avx_mask); + + if (!have_sse4_2 || !have_popcnt || !have_avx) { + fprintf( + stderr, + "This executable requires a cpu that supports SSE4.2 and AVX2.\n" + "Rebuild with enable_perfetto_x64_cpu_opt=false (ebx=%x, ecx=%x).\n", + ebx, ecx); + _exit(126); + } +} +#endif + +} // namespace + namespace perfetto { namespace base { @@ -109,36 +172,121 @@ void SetEnv(const std::string& key, const std::string& value) { #endif } -void Daemonize() { - #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ - PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ - PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) - pid_t pid; - switch (pid = fork()) { - case -1: - PERFETTO_FATAL("fork"); - case 0: { - PERFETTO_CHECK(setsid() != -1); - base::ignore_result(chdir("/")); - base::ScopedFile null = base::OpenFile("/dev/null", O_RDONLY); - PERFETTO_CHECK(null); - PERFETTO_CHECK(dup2(*null, STDIN_FILENO) != -1); - PERFETTO_CHECK(dup2(*null, STDOUT_FILENO) != -1); - PERFETTO_CHECK(dup2(*null, STDERR_FILENO) != -1); - // Do not accidentally close stdin/stdout/stderr. - if (*null <= 2) - null.release(); - break; - } - default: - printf("%d\n", pid); - exit(0); - } - #else - // Avoid -Wunreachable warnings. - if (reinterpret_cast<intptr_t>(&Daemonize) != 16) - PERFETTO_FATAL("--background is only supported on Linux/Android/Mac"); - #endif // OS_WIN +void Daemonize(std::function<int()> parent_cb) { +#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ + PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ + PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) + pid_t pid; + switch (pid = fork()) { + case -1: + PERFETTO_FATAL("fork"); + case 0: { + PERFETTO_CHECK(setsid() != -1); + base::ignore_result(chdir("/")); + base::ScopedFile null = base::OpenFile("/dev/null", O_RDONLY); + PERFETTO_CHECK(null); + PERFETTO_CHECK(dup2(*null, STDIN_FILENO) != -1); + PERFETTO_CHECK(dup2(*null, STDOUT_FILENO) != -1); + PERFETTO_CHECK(dup2(*null, STDERR_FILENO) != -1); + // Do not accidentally close stdin/stdout/stderr. + if (*null <= 2) + null.release(); + break; + } + default: + printf("%d\n", pid); + int err = parent_cb(); + exit(err); + } +#else + // Avoid -Wunreachable warnings. + if (reinterpret_cast<intptr_t>(&Daemonize) != 16) + PERFETTO_FATAL("--background is only supported on Linux/Android/Mac"); + ignore_result(parent_cb); +#endif // OS_WIN +} + +std::string GetCurExecutablePath() { + std::string self_path; +#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ + PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ + PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) + char buf[PATH_MAX]; + ssize_t size = readlink("/proc/self/exe", buf, sizeof(buf)); + PERFETTO_CHECK(size != -1); + // readlink does not null terminate. + self_path = std::string(buf, static_cast<size_t>(size)); +#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) + uint32_t size = 0; + PERFETTO_CHECK(_NSGetExecutablePath(nullptr, &size)); + self_path.resize(size); + PERFETTO_CHECK(_NSGetExecutablePath(&self_path[0], &size) == 0); +#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) + char buf[MAX_PATH]; + auto len = ::GetModuleFileNameA(nullptr /*current*/, buf, sizeof(buf)); + self_path = std::string(buf, len); +#else + PERFETTO_FATAL( + "GetCurExecutableDir() not implemented on the current platform"); +#endif + return self_path; +} + +std::string GetCurExecutableDir() { + auto path = GetCurExecutablePath(); +#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) + // Paths in Windows can have both kinds of slashes (mingw vs msvc). + path = path.substr(0, path.find_last_of('\\')); +#endif + path = path.substr(0, path.find_last_of('/')); + return path; +} + +void* AlignedAlloc(size_t alignment, size_t size) { + void* res = nullptr; + alignment = AlignUp<sizeof(void*)>(alignment); // At least pointer size. +#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) + // Window's _aligned_malloc() has a nearly identically signature to Unix's + // aligned_alloc() but its arguments are obviously swapped. + res = _aligned_malloc(size, alignment); +#else + // aligned_alloc() has been introduced in Android only in API 28. + // Also NaCl and Fuchsia seems to have only posix_memalign(). + ignore_result(posix_memalign(&res, alignment, size)); +#endif + PERFETTO_CHECK(res); + return res; +} + +void AlignedFree(void* ptr) { +#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) + _aligned_free(ptr); // MSDN says it is fine to pass nullptr. +#else + free(ptr); +#endif +} + +std::string HexDump(const void* data_void, size_t len, size_t bytes_per_line) { + const char* data = reinterpret_cast<const char*>(data_void); + std::string res; + static const size_t kPadding = bytes_per_line * 3 + 12; + std::unique_ptr<char[]> line(new char[bytes_per_line * 4 + 128]); + for (size_t i = 0; i < len; i += bytes_per_line) { + char* wptr = line.get(); + wptr += sprintf(wptr, "%08zX: ", i); + for (size_t j = i; j < i + bytes_per_line && j < len; j++) + wptr += sprintf(wptr, "%02X ", static_cast<unsigned>(data[j]) & 0xFF); + for (size_t j = static_cast<size_t>(wptr - line.get()); j < kPadding; ++j) + *(wptr++) = ' '; + for (size_t j = i; j < i + bytes_per_line && j < len; j++) { + char c = data[j]; + *(wptr++) = (c >= 32 && c < 127) ? c : '.'; + } + *(wptr++) = '\n'; + *(wptr++) = '\0'; + res.append(line.get()); + } + return res; } } // namespace base |