blob: 1aa1af5b1410565743594c9e302fa227b9f6183d [file] [log] [blame]
/*
* 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 <string>
#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/file_utils.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 <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>
#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
namespace {
extern "C" {
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_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 {
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<MalloptType>(dlsym(RTLD_DEFAULT, "mallopt"));
if (!mallopt_fn)
return;
mallopt_fn(PERFETTO_M_PURGE, 0);
#endif
}
uint32_t GetSysPageSize() {
ignore_result(kPageSize); // Just to keep the amalgamated build happy.
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
static std::atomic<uint32_t> page_size{0};
// This function might be called in hot paths. Avoid calling getpagesize() all
// the times, in many implementations getpagesize() calls sysconf() which is
// not cheap.
uint32_t cached_value = page_size.load(std::memory_order_relaxed);
if (PERFETTO_UNLIKELY(cached_value == 0)) {
cached_value = static_cast<uint32_t>(getpagesize());
page_size.store(cached_value, std::memory_order_relaxed);
}
return cached_value;
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
return static_cast<uint32_t>(vm_page_size);
#else
return 4096;
#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 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 += 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<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
} // namespace perfetto