/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "perfetto/ext/base/utils.h" #include #include "perfetto/base/build_config.h" #include "perfetto/base/logging.h" #include "perfetto/ext/base/file_utils.h" #include "perfetto/ext/base/pipe.h" #include "perfetto/ext/base/string_utils.h" #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) #include #include // For _exit() #include // For getpagesize() and geteuid() & fork() #endif #if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) #include #include #endif #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) #include #include #include // For _aligned_malloc(). #endif #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) #include #include #ifdef M_PURGE #define PERFETTO_M_PURGE M_PURGE #else // Only available in in-tree builds and on newer SDKs. #define PERFETTO_M_PURGE -101 #endif // M_PURGE #ifdef M_PURGE_ALL #define PERFETTO_M_PURGE_ALL M_PURGE_ALL #else // Only available in in-tree builds and on newer SDKs. #define PERFETTO_M_PURGE_ALL -104 #endif // M_PURGE namespace { extern "C" { using MalloptType = int (*)(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_COMPONENT __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); // Get level 7 features (eax = 7 and ecx= 0), to check for AVX2 support. // (See Intel 64 and IA-32 Architectures Software Developer's Manual // Volume 2A: Instruction Set Reference, A-M CPUID). PERFETTO_GETCPUID(eax, ebx, ecx, edx, 7, 0); const bool have_avx2 = have_avx && ((ebx >> 5) & 0x1); const bool have_bmi = (ebx >> 3) & 0x1; const bool have_bmi2 = (ebx >> 8) & 0x1; if (!have_sse4_2 || !have_popcnt || !have_avx2 || !have_bmi || !have_bmi2) { fprintf( stderr, "This executable requires a x86_64 cpu that supports SSE4.2, BMI2 and " "AVX2.\n" #if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) "On MacOS, this might be caused by running x86_64 binaries on arm64.\n" "See https://github.com/google/perfetto/issues/294 for more.\n" #endif "Rebuild with enable_perfetto_x64_cpu_opt=false.\n"); _exit(126); } } #endif } // namespace namespace perfetto { namespace base { namespace internal { std::atomic g_cached_page_size{0}; uint32_t GetSysPageSizeSlowpath() { uint32_t page_size = 0; #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) const int page_size_int = getpagesize(); // If sysconf() fails for obscure reasons (e.g. SELinux denial) assume the // page size is 4KB. This is to avoid regressing subtle SDK usages, as old // versions of this code had a static constant baked in. page_size = static_cast(page_size_int > 0 ? page_size_int : 4096); #elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) page_size = static_cast(vm_page_size); #else page_size = 4096; #endif PERFETTO_CHECK(page_size > 0 && page_size % 4096 == 0); // Races here are fine because any thread will write the same value. g_cached_page_size.store(page_size, std::memory_order_relaxed); return page_size; } } // namespace internal void MaybeReleaseAllocatorMemToOS() { #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) // mallopt() on Android requires SDK level 26. Many targets and embedders // still depend on a lower SDK level. Given mallopt() is a quite simple API, // use reflection to do this rather than bumping the SDK level for all // embedders. This keeps the behavior of standalone builds aligned with // in-tree builds. static MalloptType mallopt_fn = reinterpret_cast(dlsym(RTLD_DEFAULT, "mallopt")); if (!mallopt_fn) return; if (mallopt_fn(PERFETTO_M_PURGE_ALL, 0) == 0) { mallopt_fn(PERFETTO_M_PURGE, 0); } #endif } uid_t GetCurrentUserId() { #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) return geteuid(); #else // TODO(primiano): On Windows we could hash the current user SID and derive a // numeric user id [1]. It is not clear whether we need that. Right now that // would not bring any benefit. Returning 0 unil we can prove we need it. // [1]:https://android-review.googlesource.com/c/platform/external/perfetto/+/1513879/25/src/base/utils.cc return 0; #endif } void SetEnv(const std::string& key, const std::string& value) { #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) PERFETTO_CHECK(::_putenv_s(key.c_str(), value.c_str()) == 0); #else PERFETTO_CHECK(::setenv(key.c_str(), value.c_str(), /*overwrite=*/true) == 0); #endif } void UnsetEnv(const std::string& key) { #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) PERFETTO_CHECK(::_putenv_s(key.c_str(), "") == 0); #else PERFETTO_CHECK(::unsetenv(key.c_str()) == 0); #endif } void Daemonize(std::function parent_cb) { #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) Pipe pipe = Pipe::Create(Pipe::kBothBlock); 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(); WriteAll(*pipe.wr, "1", 1); break; } default: { // Wait for the child process to have reached the setsid() call. This is // to avoid that 'adb shell perfetto -D' destroys the terminal (hence // sending a SIGHUP to the child) before the child has detached from the // terminal (see b/238644870). // This is to unblock the read() below (with EOF, which will fail the // CHECK) in the unlikely case of the child crashing before WriteAll("1"). pipe.wr.reset(); char one = '\0'; PERFETTO_CHECK(Read(*pipe.rd, &one, sizeof(one)) == 1 && one == '1'); printf("%d\n", pid); int err = parent_cb(); exit(err); } } #else // Avoid -Wunreachable warnings. if (reinterpret_cast(&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)); #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(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(data_void); std::string res; static const size_t kPadding = bytes_per_line * 3 + 12; std::unique_ptr line(new char[bytes_per_line * 4 + 128]); for (size_t i = 0; i < len; i += bytes_per_line) { char* wptr = line.get(); wptr += base::SprintfTrunc(wptr, 19, "%08zX: ", i); for (size_t j = i; j < i + bytes_per_line && j < len; j++) { wptr += base::SprintfTrunc(wptr, 4, "%02X ", static_cast(data[j]) & 0xFF); } for (size_t j = static_cast(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 } // namespace perfetto