From 6c7f25b682c833811fd823b8f9405634a15a1917 Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Sun, 9 Aug 2026 00:31:14 +0000 Subject: [PATCH] deps: update perfetto to 57.2 --- deps/perfetto/LICENSE | 20 + deps/perfetto/VERSION | 2 +- deps/perfetto/sdk/perfetto.cc | 6726 +++++++--- deps/perfetto/sdk/perfetto.h | 23033 ++++++++++++++------------------ 4 files changed, 15265 insertions(+), 14516 deletions(-) diff --git a/deps/perfetto/LICENSE b/deps/perfetto/LICENSE index cbdc2881d57d..681d40008ee3 100644 --- a/deps/perfetto/LICENSE +++ b/deps/perfetto/LICENSE @@ -224,6 +224,26 @@ Files: src/trace_processor/perfetto_sql/stdlib/chromium/*, protos/third_party/ch OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +------------------ + +Files: src/trace_processor/perfetto_sql/syntaqlite/syntaqlite_perfetto.{c, h} + + Copyright 2025 The syntaqlite Authors. All rights reserved. + + Machine-generated amalgamation of syntaqlite runtime + Perfetto dialect + sources (https://github.com/LalitMaganti/syntaqlite). Portions derive + from SQLite's public-domain `parse.y` grammar. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + 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. + + ------------------ Files: src/trace_processor/perfetto_sql/preprocessor/preprocessor_grammar.{c, h} diff --git a/deps/perfetto/VERSION b/deps/perfetto/VERSION index 517ac763657c..06b0aef2d7b5 100644 --- a/deps/perfetto/VERSION +++ b/deps/perfetto/VERSION @@ -1 +1 @@ -54.0 +57.2 diff --git a/deps/perfetto/sdk/perfetto.cc b/deps/perfetto/sdk/perfetto.cc index 2ccd11f7b831..4df59d2c46dd 100644 --- a/deps/perfetto/sdk/perfetto.cc +++ b/deps/perfetto/sdk/perfetto.cc @@ -560,6 +560,7 @@ struct std::hash<::perfetto::base::StringView> { #include #include #include +#include #include #include @@ -723,7 +724,9 @@ std::vector SplitString(const std::string& text, const std::string& delimiter); std::string StripPrefix(const std::string& str, const std::string& prefix); std::string StripSuffix(const std::string& str, const std::string& suffix); +std::string_view TrimWhitespace(std::string_view str); std::string TrimWhitespace(const std::string& str); +std::string_view TrimWhitespace(const char* str); std::string ToLower(const std::string& str); std::string ToUpper(const std::string& str); std::string StripChars(const std::string& str, @@ -1544,10 +1547,10 @@ std::optional Base64Decode(const char* src, size_t src_size) { } // namespace base } // namespace perfetto -// gen_amalgamated begin source: src/base/crash_keys.cc -// gen_amalgamated begin header: include/perfetto/ext/base/crash_keys.h +// gen_amalgamated begin source: src/base/cpu_info.cc +// gen_amalgamated begin header: include/perfetto/ext/base/cpu_info.h /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2026 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. @@ -1562,154 +1565,42 @@ std::optional Base64Decode(const char* src, size_t src_size) { * limitations under the License. */ -#ifndef INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_ -#define INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_ - -#include -#include +#ifndef INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_H_ +#define INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_H_ #include -#include - -// gen_amalgamated expanded: #include "perfetto/base/compiler.h" -// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h" - -// Crash keys are very simple global variables with static-storage that -// are reported on crash time for managed crashes (CHECK/FATAL/Watchdog). -// - Translation units can define a CrashKey and register it at some point -// during initialization. -// - CrashKey instances must be long-lived. They should really be just global -// static variable in the anonymous namespace. -// Example: -// subsystem_1.cc -// CrashKey g_client_id("ipc_client_id"); -// ... -// OnIpcReceived(client_id) { -// g_client_id.Set(client_id); -// ... // Process the IPC -// g_client_id.Clear(); -// } -// Or equivalently: -// OnIpcReceived(client_id) { -// auto scoped_key = g_client_id.SetScoped(client_id); -// ... // Process the IPC -// } -// -// If a crash happens while processing the IPC, the crash report will -// have a line "ipc_client_id: 42". -// -// Thread safety considerations: -// CrashKeys can be registered and set/cleared from any thread. -// There is no compelling use-case to have full acquire/release consistency when -// setting a key. This means that if a thread crashes immediately after a -// crash key has been set on another thread, the value printed on the crash -// report could be incomplete. The code guarantees defined behavior and does -// not rely on null-terminated string (in the worst case 32 bytes of random -// garbage will be printed out). - -// The tests live in logging_unittest.cc. +#include +#include +#include namespace perfetto { namespace base { -constexpr size_t kCrashKeyMaxStrSize = 32; - -// CrashKey instances must be long lived -class CrashKey { - public: - class ScopedClear { - public: - explicit ScopedClear(CrashKey* k) : key_(k) {} - ~ScopedClear() { - if (key_) - key_->Clear(); - } - ScopedClear(const ScopedClear&) = delete; - ScopedClear& operator=(const ScopedClear&) = delete; - ScopedClear& operator=(ScopedClear&&) = delete; - ScopedClear(ScopedClear&& other) noexcept : key_(other.key_) { - other.key_ = nullptr; - } - - private: - CrashKey* key_; - }; - - // constexpr so it can be used in the anon namespace without requiring a - // global constructor. - // |name| must be a long-lived string. - constexpr explicit CrashKey(const char* name) - : registered_{}, type_(Type::kUnset), name_(name), str_value_{} {} - CrashKey(const CrashKey&) = delete; - CrashKey& operator=(const CrashKey&) = delete; - CrashKey(CrashKey&&) = delete; - CrashKey& operator=(CrashKey&&) = delete; - - enum class Type : uint8_t { kUnset = 0, kInt, kStr }; - - void Clear() { - int_value_.store(0, std::memory_order_relaxed); - type_.store(Type::kUnset, std::memory_order_relaxed); - } - - void Set(int64_t value) { - int_value_.store(value, std::memory_order_relaxed); - type_.store(Type::kInt, std::memory_order_relaxed); - if (PERFETTO_UNLIKELY(!registered_.load(std::memory_order_relaxed))) - Register(); - } - - void Set(StringView sv) { - size_t len = std::min(sv.size(), sizeof(str_value_) - 1); - for (size_t i = 0; i < len; ++i) - str_value_[i].store(sv.data()[i], std::memory_order_relaxed); - str_value_[len].store('\0', std::memory_order_relaxed); - type_.store(Type::kStr, std::memory_order_relaxed); - if (PERFETTO_UNLIKELY(!registered_.load(std::memory_order_relaxed))) - Register(); - } - - ScopedClear SetScoped(int64_t value) PERFETTO_WARN_UNUSED_RESULT { - Set(value); - return ScopedClear(this); - } - - ScopedClear SetScoped(StringView sv) PERFETTO_WARN_UNUSED_RESULT { - Set(sv); - return ScopedClear(this); - } - - void Register(); - - int64_t int_value() const { - return int_value_.load(std::memory_order_relaxed); - } - size_t ToString(char* dst, size_t len); - - private: - std::atomic registered_; - std::atomic type_; - const char* const name_; - union { - std::atomic str_value_[kCrashKeyMaxStrSize]; - std::atomic int_value_; - }; +struct CpuInfo { + std::string processor; + uint32_t cpu_index = 0; + std::optional implementer; + std::optional architecture; + std::optional variant; + std::optional part; + std::optional revision; + uint64_t features = 0; + char arm_cpuid[32] = {}; }; -// Fills |dst| with a string containing one line for each crash key -// (excluding the unset ones). -// Returns number of chars written, without counting the NUL terminator. -// This is used in logging.cc when emitting the crash report abort message. -size_t SerializeCrashKeys(char* dst, size_t len); +// Parses the contents of the input string into per-CPU entries. +std::vector ParseCpuInfo(std::string proc_cpu_info); -void UnregisterAllCrashKeysForTesting(); +// Reads /proc/cpuinfo and parses it into per-CPU entries. +std::vector ReadCpuInfo(); } // namespace base } // namespace perfetto -#endif // INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_ +#endif // INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_H_ +// gen_amalgamated begin header: include/perfetto/ext/base/cpu_info_features_allowlist.h /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2025 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. @@ -1724,91 +1615,29 @@ void UnregisterAllCrashKeysForTesting(); * limitations under the License. */ -// gen_amalgamated expanded: #include "perfetto/ext/base/crash_keys.h" - -#include - -#include -#include - -// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h" +#ifndef INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_FEATURES_ALLOWLIST_H_ +#define INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_FEATURES_ALLOWLIST_H_ namespace perfetto { namespace base { -namespace { - -constexpr size_t kMaxKeys = 32; - -std::atomic g_keys[kMaxKeys]{}; -std::atomic g_num_keys{}; -} // namespace - -void CrashKey::Register() { - // If doesn't matter if we fail below. If there are no slots left, don't - // keep trying re-registering on every Set(), the outcome won't change. - - // If two threads raced on the Register(), avoid registering the key twice. - if (registered_.exchange(true)) - return; - - uint32_t slot = g_num_keys.fetch_add(1); - if (slot >= kMaxKeys) { - PERFETTO_LOG("Too many crash keys registered"); - return; - } - g_keys[slot].store(this); -} - -// Returns the number of chars written, without counting the \0. -size_t CrashKey::ToString(char* dst, size_t len) { - if (len > 0) - *dst = '\0'; - switch (type_.load(std::memory_order_relaxed)) { - case Type::kUnset: - break; - case Type::kInt: - return SprintfTrunc(dst, len, "%s: %" PRId64 "\n", name_, - int_value_.load(std::memory_order_relaxed)); - case Type::kStr: - char buf[sizeof(str_value_)]; - for (size_t i = 0; i < sizeof(str_value_); i++) - buf[i] = str_value_[i].load(std::memory_order_relaxed); - - // Don't assume |str_value_| is properly null-terminated. - return SprintfTrunc(dst, len, "%s: %.*s\n", name_, int(sizeof(buf)), buf); - } - return 0; -} - -void UnregisterAllCrashKeysForTesting() { - g_num_keys.store(0); - for (auto& key : g_keys) - key.store(nullptr); -} - -size_t SerializeCrashKeys(char* dst, size_t len) { - size_t written = 0; - uint32_t num_keys = g_num_keys.load(); - if (len > 0) - *dst = '\0'; - for (uint32_t i = 0; i < num_keys && written < len; i++) { - CrashKey* key = g_keys[i].load(); - if (!key) - continue; // Can happen if we hit this between the add and the store. - written += key->ToString(dst + written, len - written); - } - PERFETTO_DCHECK(written <= len); - PERFETTO_DCHECK(len == 0 || dst[written] == '\0'); - return written; -} +// APPEND ONLY. DO NOT EVER REMOVE ENTRIES FROM THIS ARRAY OR REORDER. +// This array is used both by traced_probes and trace_processor to index the +// cpuinfo flags. Changing the order will break trace_processor compatibility +// with old traces. +constexpr const char* kCpuInfoFeatures[] = { + "mte", // DO NOT REMOVE/REODER. + "mte3", // DO NOT REMOVE/REODER. +}; } // namespace base } // namespace perfetto -// gen_amalgamated begin source: src/base/ctrl_c_handler.cc -// gen_amalgamated begin header: include/perfetto/ext/base/ctrl_c_handler.h + +#endif // INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_FEATURES_ALLOWLIST_H_ +// gen_amalgamated begin header: include/perfetto/ext/base/file_utils.h +// gen_amalgamated begin header: include/perfetto/base/status.h /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2019 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. @@ -1823,106 +1652,107 @@ size_t SerializeCrashKeys(char* dst, size_t len) { * limitations under the License. */ -#ifndef INCLUDE_PERFETTO_EXT_BASE_CTRL_C_HANDLER_H_ -#define INCLUDE_PERFETTO_EXT_BASE_CTRL_C_HANDLER_H_ +#ifndef INCLUDE_PERFETTO_BASE_STATUS_H_ +#define INCLUDE_PERFETTO_BASE_STATUS_H_ + +#include +#include +#include +#include + +// gen_amalgamated expanded: #include "perfetto/base/compiler.h" +// gen_amalgamated expanded: #include "perfetto/base/export.h" +// gen_amalgamated expanded: #include "perfetto/base/logging.h" namespace perfetto { namespace base { -// On Linux/Android/Mac: installs SIGINT + SIGTERM signal handlers. -// On Windows: installs a SetConsoleCtrlHandler() handler. -// The passed handler must be async safe. -using CtrlCHandlerFunction = void (*)(); -void InstallCtrlCHandler(CtrlCHandlerFunction); +// Represents either the success or the failure message of a function. +// This can used as the return type of functions which would usually return an +// bool for success or int for errno but also wants to add some string context +// (ususally for logging). +// +// Similar to absl::Status, an optional "payload" can also be included with more +// context about the error. This allows passing additional metadata about the +// error (e.g. location of errors, potential mitigations etc). +class PERFETTO_EXPORT_COMPONENT Status { + public: + Status() : ok_(true) {} + explicit Status(std::string msg) : ok_(false), message_(std::move(msg)) { + PERFETTO_CHECK(!message_.empty()); + } -} // namespace base -} // namespace perfetto + // Copy operations. + Status(const Status&) = default; + Status& operator=(const Status&) = default; -#endif // INCLUDE_PERFETTO_EXT_BASE_CTRL_C_HANDLER_H_ -/* - * Copyright (C) 2021 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. - */ + // Move operations. The moved-from state is valid but unspecified. + Status(Status&&) noexcept = default; + Status& operator=(Status&&) = default; -// gen_amalgamated expanded: #include "perfetto/ext/base/ctrl_c_handler.h" + bool ok() const { return ok_; } -// gen_amalgamated expanded: #include "perfetto/base/build_config.h" -// gen_amalgamated expanded: #include "perfetto/base/compiler.h" -// gen_amalgamated expanded: #include "perfetto/base/logging.h" + // When ok() is false this returns the error message. Returns the empty string + // otherwise. + const std::string& message() const { return message_; } + const char* c_message() const { return message_.c_str(); } -#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) -#include + ////////////////////////////////////////////////////////////////////////////// + // Payload Management APIs + ////////////////////////////////////////////////////////////////////////////// -#include -#else -#include -#include -#endif + // Payloads can be attached to error statuses to provide additional context. + // + // Payloads are (key, value) pairs, where the key is a string acting as a + // unique "type URL" and the value is an opaque string. The "type URL" should + // be unique, follow the format of a URL and, ideally, documentation on how to + // interpret its associated data should be available. + // + // To attach a payload to a status object, call `Status::SetPayload()`. + // Similarly, to extract the payload from a status, call + // `Status::GetPayload()`. + // + // Note: the payload APIs are only meaningful to call when the status is an + // error. Otherwise, all methods are noops. -namespace perfetto { -namespace base { + // Gets the payload for the given |type_url| if one exists. + // + // Will always return std::nullopt if |ok()|. + std::optional GetPayload(std::string_view type_url) const; -namespace { -CtrlCHandlerFunction g_handler = nullptr; + // Sets the payload for the given key. The key should + // + // Will always do nothing if |ok()|. + void SetPayload(std::string_view type_url, std::string value); -#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) -BOOL WINAPI Trampoline(DWORD type) { - if (type == CTRL_C_EVENT) { - g_handler(); - return TRUE; - } - return FALSE; -} -#endif -} // namespace + // Erases the payload for the given string and returns true if the payload + // existed and was erased. + // + // Will always do nothing if |ok()|. + bool ErasePayload(std::string_view type_url); -void InstallCtrlCHandler(CtrlCHandlerFunction handler) { - PERFETTO_CHECK(g_handler == nullptr); - g_handler = handler; + private: + struct Payload { + std::string type_url; + std::string payload; + }; -#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) - ::SetConsoleCtrlHandler(Trampoline, true); -#elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ - PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ - PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) - // Setup signal handler. - struct sigaction sa{}; + bool ok_ = false; + std::string message_; + std::vector payloads_; +}; -// Glibc headers for sa_sigaction trigger this. -#pragma GCC diagnostic push -#if defined(__clang__) -#pragma GCC diagnostic ignored "-Wdisabled-macro-expansion" -#endif - sa.sa_handler = [](int) { g_handler(); }; -#if !PERFETTO_BUILDFLAG(PERFETTO_OS_QNX) - sa.sa_flags = static_cast(SA_RESETHAND | SA_RESTART); -#else // POSIX-compliant - sa.sa_flags = static_cast(SA_RESETHAND); -#endif -#pragma GCC diagnostic pop - sigaction(SIGINT, &sa, nullptr); - sigaction(SIGTERM, &sa, nullptr); -#else - // Do nothing on NaCL and Fuchsia. - ignore_result(handler); -#endif +// Returns a status object which represents the Ok status. +inline Status OkStatus() { + return Status(); } +Status ErrStatus(const char* format, ...) PERFETTO_PRINTF_FORMAT(1, 2); + } // namespace base } // namespace perfetto -// gen_amalgamated begin source: src/base/event_fd.cc -// gen_amalgamated begin header: include/perfetto/ext/base/event_fd.h + +#endif // INCLUDE_PERFETTO_BASE_STATUS_H_ // gen_amalgamated begin header: include/perfetto/ext/base/scoped_file.h /* * Copyright (C) 2017 The Android Open Source Project @@ -2061,58 +1891,198 @@ using ScopedDir = ScopedResource; * limitations under the License. */ -#ifndef INCLUDE_PERFETTO_EXT_BASE_EVENT_FD_H_ -#define INCLUDE_PERFETTO_EXT_BASE_EVENT_FD_H_ +#ifndef INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_ +#define INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_ + +#include // For mode_t & O_RDONLY/RDWR. Exists also on Windows. +#include + +#include +#include +#include +#include +#include // gen_amalgamated expanded: #include "perfetto/base/build_config.h" -// gen_amalgamated expanded: #include "perfetto/base/platform_handle.h" +// gen_amalgamated expanded: #include "perfetto/base/export.h" +// gen_amalgamated expanded: #include "perfetto/base/status.h" // gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h" +// gen_amalgamated expanded: #include "perfetto/ext/base/sys_types.h" namespace perfetto { namespace base { -// A waitable event that can be used with poll/select. -// This is really a wrapper around eventfd_create with a pipe-based fallback -// for other platforms where eventfd is not supported. -class EventFd { - public: - EventFd(); - ~EventFd(); - EventFd(EventFd&&) noexcept = default; - EventFd& operator=(EventFd&&) = default; +class TaskRunner; - // The non-blocking file descriptor that can be polled to wait for the event. - PlatformHandle fd() const { return event_handle_.get(); } +#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) +using FileOpenMode = int; +inline constexpr char kDevNull[] = "NUL"; +inline constexpr char kFopenReadFlag[] = "r"; +#else +using FileOpenMode = mode_t; +inline constexpr char kDevNull[] = "/dev/null"; +inline constexpr char kFopenReadFlag[] = "re"; +#endif - // Can be called from any thread. - void Notify(); +constexpr FileOpenMode kFileModeInvalid = static_cast(-1); - // Can be called from any thread. If more Notify() are queued a Clear() call - // can clear all of them (up to 16 per call). - void Clear(); +// Cross-platform variant of ReadFileDescriptor() that takes a PlatformHandle. +// On Windows normalizes ERROR_BROKEN_PIPE to EOF so behavior matches POSIX. +bool ReadPlatformHandle(PlatformHandle, std::string* out); - private: - // The eventfd, when eventfd is supported, otherwise this is the read end of - // the pipe for fallback mode. - ScopedPlatformHandle event_handle_; +// Reads from |fd|, appending what is currently available into |*out|. +// Returns: +// True: EOF reached (all writers of |fd| have closed their end). +// False: read error. On a non-blocking |fd| this includes EAGAIN (no data +// currently available but writers are still alive); callers can check +// IsAgain(errno) and retry on the next readability notification. +bool ReadFileDescriptor(int fd, std::string* out); -// QNX is specified because it is a non-Linux UNIX platform but it -// still sets the PERFETTO_OS_LINUX flag to be as compatible as possible -// with the Linux build. -#if !PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) && \ - !PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \ - !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) - // On Mac and other non-Linux UNIX platforms a pipe-based fallback is used. - // The write end of the wakeup pipe. - ScopedFile write_fd_; -#endif +// Convenience wrapper around ReadFileDescriptor() that takes a FILE*. +bool ReadFileStream(FILE* f, std::string* out); + +// Opens |path| read-only and reads its contents into |*out|. +// Returns false if the file cannot be opened. +bool ReadFile(const std::string& path, std::string* out); + +// A wrapper around read(2). It deals with Linux vs Windows includes. It also +// deals with handling EINTR. Has the same semantics of UNIX's read(2). +ssize_t Read(int fd, void* dst, size_t dst_size); + +// Call write until all data is written or an error is detected. +// +// man 2 write: +// If a write() is interrupted by a signal handler before any bytes are +// written, then the call fails with the error EINTR; if it is +// interrupted after at least one byte has been written, the call +// succeeds, and returns the number of bytes written. +ssize_t WriteAll(int fd, const void* buf, size_t count); + +// Copies all data from |fd_in| to |fd_out|. Saves the offset of |fd_in|, +// rewinds it to the beginning, copies the content, and restores the offset. +// |fd_in| can't be a pipe, socket of FIFO. +base::Status CopyFileContents(int fd_in, int fd_out); + +ssize_t WriteAllHandle(PlatformHandle, const void* buf, size_t count); + +ScopedFile OpenFile(const std::string& path, + int flags, + FileOpenMode = kFileModeInvalid); +ScopedFstream OpenFstream(const std::string& path, const std::string& mode); + +// This is an alias for close(). It's to avoid leaking windows.h in headers. +// Exported because ScopedFile is used in the /include/ext API by Chromium +// component builds. +int PERFETTO_EXPORT_COMPONENT CloseFile(int fd); + +bool FlushFile(int fd); + +// Returns true if mkdir succeeds, false if it fails (see errno in that case). +// `mode` is the permission bits for the new directory; it is ignored on +// Windows. +bool Mkdir(const std::string& path, uint32_t mode = 0755); + +// Calls rmdir() on UNIX, _rmdir() on Windows. +bool Rmdir(const std::string& path); + +// Removes a file: unlink() on UNIX, _unlink() on Windows. Takes a const char* +// and is async-signal-safe on POSIX, so it's callable from a signal handler. +bool Unlink(const char* path); + +// Wrapper around access(path, F_OK). +bool FileExists(const std::string& path); + +// Gets the extension for a filename. If the file has two extensions, returns +// only the last one (foo.pb.gz => .gz). Returns empty string if there is no +// extension. +std::string GetFileExtension(const std::string& filename); + +// Returns the basename component of a path (the final component after the last +// directory separator). Behaves like man 2 basename, but works with both '/' +// and '\' separators for cross-platform compatibility. +// Examples: +// Basename("/usr/bin/ls") => "ls" +// Basename("/usr/bin/") => "bin" +// Basename("/") => "/" +// Basename("foo") => "foo" +// Basename("") => "." +// Basename("C:\\Windows\\System32") => "System32" +std::string Basename(const std::string& path); + +// Returns the directory component of a path (everything up to but not +// including the final component). Behaves like man 2 dirname, but works with +// both '/' and '\' separators for cross-platform compatibility. +// Examples: +// Dirname("/usr/bin/ls") => "/usr/bin" +// Dirname("/usr/bin") => "/usr" +// Dirname("/") => "/" +// Dirname("foo") => "." +// Dirname("") => "." +// Dirname("C:\\Windows\\System32") => "C:\\Windows" +std::string Dirname(const std::string& path); + +// Puts the path to all files under |dir_path| in |output|, recursively walking +// subdirectories. File paths are relative to |dir_path|. Only files are +// included, not directories. Path separator is always '/', even on windows (not +// '\'). +base::Status ListFilesRecursive(const std::string& dir_path, + std::vector& output); + +// Lists immediate subdirectories in |dir_path| (non-recursive). Directory names +// are relative to |dir_path| and do not include the path separator. Returns +// only directories, not files. Works on both Unix and Windows. +base::Status ListDirectories(const std::string& dir_path, + std::vector& output); + +// Sets |path|'s owner group to |group_name| and permission mode bits to +// |mode_bits|. +base::Status SetFilePermissions(const std::string& path, + const std::string& group_name, + const std::string& mode_bits); + +// Returns the size of the file located at |path|, or nullopt in case of error. +std::optional GetFileSize(const std::string& path); + +// Returns the size of the open file |fd|, or nullopt in case of error. +std::optional GetFileSize(PlatformHandle fd); + +// This class uses inotify (on Linux/Android) to watch for the creation of +// files in the filesystem. When the specified file is created, it triggers a +// callback function. +// Destroying the returned unique_ptr will automatically unregister the watch. +// +// Note: This only works with filesystem paths (not abstract sockets or other +// special file types). +// It's only supported on Linux and Android, it's a no-op (returns nullptr) on +// other platforms. +// +// Usage: +// auto watch = LinuxFileWatch::WatchFileCreation( +// task_runner, "/tmp/my_file", []() { +// // Called when /tmp/my_file is created +// }); +class LinuxFileWatch { + public: + // Creates a watcher for file creation. Returns nullptr if the path is not a + // valid filesystem path or if the platform doesn't support inotify. The + // callback will be invoked on the provided TaskRunner when the file is + // created. + static std::unique_ptr WatchFileCreation( + TaskRunner*, + const char* path, + std::function callback); + + virtual ~LinuxFileWatch(); + + protected: + LinuxFileWatch() = default; }; } // namespace base } // namespace perfetto -#endif // INCLUDE_PERFETTO_EXT_BASE_EVENT_FD_H_ -// gen_amalgamated begin header: include/perfetto/ext/base/pipe.h +#endif // INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_ +// gen_amalgamated begin header: include/perfetto/ext/base/string_splitter.h /* * Copyright (C) 2018 The Android Open Source Project * @@ -2129,42 +2099,93 @@ class EventFd { * limitations under the License. */ -#ifndef INCLUDE_PERFETTO_EXT_BASE_PIPE_H_ -#define INCLUDE_PERFETTO_EXT_BASE_PIPE_H_ +#ifndef INCLUDE_PERFETTO_EXT_BASE_STRING_SPLITTER_H_ +#define INCLUDE_PERFETTO_EXT_BASE_STRING_SPLITTER_H_ -// gen_amalgamated expanded: #include "perfetto/base/platform_handle.h" -// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h" +#include namespace perfetto { namespace base { -class Pipe { +// C++ version of strtok(). Splits a string without making copies or any heap +// allocations. Destructs the original string passed in input. +// Supports the special case of using \0 as a delimiter. +// The token returned in output are valid as long as the input string is valid. +class StringSplitter { public: - enum Flags { - kBothBlock = 0, -#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) - kBothNonBlock, - kRdNonBlock, - kWrNonBlock, -#endif + // Whether an empty string (two delimiters side-to-side) is a valid token. + enum class EmptyTokenMode { + DISALLOW_EMPTY_TOKENS, + ALLOW_EMPTY_TOKENS, + + DEFAULT = DISALLOW_EMPTY_TOKENS, }; - static Pipe Create(Flags = kBothBlock); + // Can take ownership of the string if passed via std::move(), e.g.: + // StringSplitter(std::move(str), '\n'); + StringSplitter(std::string, + char delimiter, + EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT); - Pipe(); - Pipe(Pipe&&) noexcept; - Pipe& operator=(Pipe&&); + // Splits a C-string. The input string will be forcefully null-terminated (so + // str[size - 1] should be == '\0' or the last char will be truncated). + StringSplitter(char* str, + size_t size, + char delimiter, + EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT); - ScopedPlatformHandle rd; - ScopedPlatformHandle wr; + // Splits the current token from an outer StringSplitter instance. This is to + // chain splitters as follows: + // for (base::StringSplitter lines(x, '\n'); ss.Next();) + // for (base::StringSplitter words(&lines, ' '); words.Next();) + StringSplitter(StringSplitter*, + char delimiter, + EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT); + + // Returns true if a token is found (in which case it will be stored in + // cur_token()), false if no more tokens are found. + bool Next(); + + // Returns the next token if found (in which case it will be stored in + // cur_token()), nullptr if no more tokens are found. + char* NextToken() { return Next() ? cur_token() : nullptr; } + + // Returns the current token iff last call to Next() returned true. In this + // case it guarantees that the returned string is always null terminated. + // In all other cases (before the 1st call to Next() and after Next() returns + // false) returns nullptr. + char* cur_token() { return cur_; } + + // Returns the length of the current token (excluding the null terminator). + size_t cur_token_size() const { return cur_size_; } + + // Return the untokenized remainder of the input string that occurs after the + // current token. + char* remainder() { return next_; } + + // Returns the size of the untokenized input + size_t remainder_size() { return static_cast(end_ - next_); } + + private: + StringSplitter(const StringSplitter&) = delete; + StringSplitter& operator=(const StringSplitter&) = delete; + void Initialize(char* str, size_t size); + + std::string str_; + char* cur_; + size_t cur_size_; + char* next_; + char* end_; // STL-style, points one past the last char. + const char delimiter_; + const EmptyTokenMode empty_token_mode_; }; } // namespace base } // namespace perfetto -#endif // INCLUDE_PERFETTO_EXT_BASE_PIPE_H_ +#endif // INCLUDE_PERFETTO_EXT_BASE_STRING_SPLITTER_H_ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2026 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. @@ -2179,110 +2200,166 @@ class Pipe { * limitations under the License. */ -// gen_amalgamated expanded: #include "perfetto/base/build_config.h" - -#include -#include - -#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) -#include - -#include -#elif PERFETTO_BUILDFLAG(PERFETTO_OS_QNX) -#include -#elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ - PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) -#include -#include -#else // Mac, Fuchsia and other non-Linux UNIXes -#include -#endif +#include +#include +#include +#include -// gen_amalgamated expanded: #include "perfetto/base/logging.h" -// gen_amalgamated expanded: #include "perfetto/ext/base/event_fd.h" -// gen_amalgamated expanded: #include "perfetto/ext/base/pipe.h" +// gen_amalgamated expanded: #include "perfetto/ext/base/cpu_info.h" +// gen_amalgamated expanded: #include "perfetto/ext/base/cpu_info_features_allowlist.h" +// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h" +// gen_amalgamated expanded: #include "perfetto/ext/base/string_splitter.h" +// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h" // gen_amalgamated expanded: #include "perfetto/ext/base/utils.h" namespace perfetto { namespace base { +namespace { -EventFd::~EventFd() = default; +// Key for default processor string in /proc/cpuinfo as seen on arm. Note the +// uppercase P. +const char kDefaultProcessor[] = "Processor"; -#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) -EventFd::EventFd() { - event_handle_.reset( - CreateEventA(/*lpEventAttributes=*/nullptr, /*bManualReset=*/true, - /*bInitialState=*/false, /*bInitialState=*/nullptr)); -} +// Key for processor entry in /proc/cpuinfo. Used to determine whether a group +// of lines describes a CPU. +const char kProcessor[] = "processor"; -void EventFd::Notify() { - if (!SetEvent(event_handle_.get())) // 0: fail, !0: success, unlike UNIX. - PERFETTO_DFATAL("EventFd::Notify()"); -} +// Key for CPU implementer in /proc/cpuinfo. Arm only. +const char kImplementer[] = "CPU implementer"; -void EventFd::Clear() { - if (!ResetEvent(event_handle_.get())) // 0: fail, !0: success, unlike UNIX. - PERFETTO_DFATAL("EventFd::Clear()"); -} +// Key for CPU architecture in /proc/cpuinfo. Arm only. +const char kArchitecture[] = "CPU architecture"; -#elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \ - PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) +// Key for CPU variant in /proc/cpuinfo. Arm only. +const char kVariant[] = "CPU variant"; -EventFd::EventFd() { - event_handle_.reset(eventfd(/*initval=*/0, EFD_CLOEXEC | EFD_NONBLOCK)); - PERFETTO_CHECK(event_handle_); -} +// Key for CPU part in /proc/cpuinfo. Arm only. +const char kPart[] = "CPU part"; -void EventFd::Notify() { - const uint64_t value = 1; - ssize_t ret = write(event_handle_.get(), &value, sizeof(value)); - if (ret <= 0 && errno != EAGAIN) - PERFETTO_DFATAL("EventFd::Notify()"); -} +// Key for CPU revision in /proc/cpuinfo. Arm only. +const char kRevision[] = "CPU revision"; -void EventFd::Clear() { - uint64_t value; - ssize_t ret = - PERFETTO_EINTR(read(event_handle_.get(), &value, sizeof(value))); - if (ret <= 0 && errno != EAGAIN) - PERFETTO_DFATAL("EventFd::Clear()"); +// Key for feature flags in /proc/cpuinfo. Arm calls them Features, +// Intel calls them Flags. +const char kFeatures[] = "Features"; +const char kFlags[] = "Flags"; + +std::string ReadFile(const std::string& path) { + std::string contents; + if (!base::ReadFile(path, &contents)) + return ""; + return contents; } -#else +} // namespace -EventFd::EventFd() { - // Make the pipe non-blocking so that we never block the waking thread (either - // the main thread or another one) when scheduling a wake-up. - Pipe pipe = Pipe::Create(Pipe::kBothNonBlock); - event_handle_ = ScopedPlatformHandle(std::move(pipe.rd).release()); - write_fd_ = std::move(pipe.wr); -} +std::vector ParseCpuInfo(std::string proc_cpu_info) { + std::vector cpus; + std::string processor = "unknown"; + + std::optional cpu_index; + std::optional implementer; + std::optional architecture; + std::optional variant; + std::optional part; + std::optional revision; + uint64_t features = 0; + uint32_t next_cpu_index = 0; + + auto flush_cpu = [&] { + if (cpu_index.has_value()) { + CpuInfo cpu{}; + cpu.processor = processor; + cpu.cpu_index = *cpu_index; + cpu.implementer = implementer; + cpu.architecture = architecture; + cpu.variant = variant; + cpu.part = part; + cpu.revision = revision; + cpu.features = features; +#if PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64) + if (cpu.implementer && cpu.part) { + std::string cpuid = + base::Uint64ToHexStringNoPrefix(cpu.implementer.value()) + + base::Uint64ToHexStringNoPrefix(cpu.part.value()); + if (cpu.variant) { + cpuid += base::Uint64ToHexStringNoPrefix(cpu.variant.value()); + if (cpu.revision) { + cpuid += base::Uint64ToHexStringNoPrefix(cpu.revision.value()); + } + } + base::StringCopy(cpu.arm_cpuid, cpuid.c_str(), sizeof(cpu.arm_cpuid)); + } +#endif // PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64) + cpus.emplace_back(std::move(cpu)); + next_cpu_index++; + } + cpu_index = std::nullopt; + implementer = std::nullopt; + architecture = std::nullopt; + variant = std::nullopt; + part = std::nullopt; + revision = std::nullopt; + features = 0; + }; -void EventFd::Notify() { - const uint64_t value = 1; - ssize_t ret = write(write_fd_.get(), &value, sizeof(uint8_t)); - if (ret <= 0 && errno != EAGAIN) - PERFETTO_DFATAL("EventFd::Notify()"); + for (base::StringSplitter lines( + std::move(proc_cpu_info), '\n', + base::StringSplitter::EmptyTokenMode::ALLOW_EMPTY_TOKENS); + lines.Next();) { + std::string line(lines.cur_token(), lines.cur_token_size()); + if (line.empty() && cpu_index.has_value()) { + flush_cpu(); + continue; + } + + auto splits = base::SplitString(line, ":"); + if (splits.size() != 2) + continue; + std::string key = + base::StripSuffix(base::StripChars(splits[0], "\t", ' '), " "); + std::string value = base::StripPrefix(splits[1], " "); + + if (key == kDefaultProcessor) { + processor = value; + } else if (key == kProcessor) { + cpu_index = base::StringToUInt32(value); + } else if (key == kImplementer) { + implementer = base::CStringToUInt32(value.data(), 16); + } else if (key == kArchitecture) { + architecture = base::CStringToUInt32(value.data(), 10); + } else if (key == kVariant) { + variant = base::CStringToUInt32(value.data(), 16); + } else if (key == kPart) { + part = base::CStringToUInt32(value.data(), 16); + } else if (key == kRevision) { + revision = base::CStringToUInt32(value.data(), 10); + } else if (key == kFeatures || key == kFlags) { + for (base::StringSplitter ss(value.data(), ' '); ss.Next();) { + for (size_t i = 0; i < base::ArraySize(kCpuInfoFeatures); ++i) { + if (strcmp(ss.cur_token(), kCpuInfoFeatures[i]) == 0) { + static_assert(base::ArraySize(kCpuInfoFeatures) < 64); + features |= 1ull << i; + } + } + } + } + } + + flush_cpu(); + return cpus; } -void EventFd::Clear() { - // Drain the byte(s) written to the wake-up pipe. We can potentially read - // more than one byte if several wake-ups have been scheduled. - char buffer[16]; - ssize_t ret = - PERFETTO_EINTR(read(event_handle_.get(), &buffer[0], sizeof(buffer))); - if (ret <= 0 && errno != EAGAIN) - PERFETTO_DFATAL("EventFd::Clear()"); +std::vector ReadCpuInfo() { + return ParseCpuInfo(ReadFile("/proc/cpuinfo")); } -#endif } // namespace base } // namespace perfetto -// gen_amalgamated begin source: src/base/file_utils.cc -// gen_amalgamated begin header: include/perfetto/ext/base/file_utils.h -// gen_amalgamated begin header: include/perfetto/base/status.h +// gen_amalgamated begin source: src/base/crash_keys.cc +// gen_amalgamated begin header: include/perfetto/ext/base/crash_keys.h /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2021 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. @@ -2297,107 +2374,695 @@ void EventFd::Clear() { * limitations under the License. */ -#ifndef INCLUDE_PERFETTO_BASE_STATUS_H_ -#define INCLUDE_PERFETTO_BASE_STATUS_H_ +#ifndef INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_ +#define INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_ -#include -#include -#include -#include +#include +#include + +#include +#include // gen_amalgamated expanded: #include "perfetto/base/compiler.h" -// gen_amalgamated expanded: #include "perfetto/base/export.h" -// gen_amalgamated expanded: #include "perfetto/base/logging.h" +// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h" + +// Crash keys are very simple global variables with static-storage that +// are reported on crash time for managed crashes (CHECK/FATAL/Watchdog). +// - Translation units can define a CrashKey and register it at some point +// during initialization. +// - CrashKey instances must be long-lived. They should really be just global +// static variable in the anonymous namespace. +// Example: +// subsystem_1.cc +// CrashKey g_client_id("ipc_client_id"); +// ... +// OnIpcReceived(client_id) { +// g_client_id.Set(client_id); +// ... // Process the IPC +// g_client_id.Clear(); +// } +// Or equivalently: +// OnIpcReceived(client_id) { +// auto scoped_key = g_client_id.SetScoped(client_id); +// ... // Process the IPC +// } +// +// If a crash happens while processing the IPC, the crash report will +// have a line "ipc_client_id: 42". +// +// Thread safety considerations: +// CrashKeys can be registered and set/cleared from any thread. +// There is no compelling use-case to have full acquire/release consistency when +// setting a key. This means that if a thread crashes immediately after a +// crash key has been set on another thread, the value printed on the crash +// report could be incomplete. The code guarantees defined behavior and does +// not rely on null-terminated string (in the worst case 32 bytes of random +// garbage will be printed out). + +// The tests live in logging_unittest.cc. namespace perfetto { namespace base { -// Represents either the success or the failure message of a function. -// This can used as the return type of functions which would usually return an -// bool for success or int for errno but also wants to add some string context -// (ususally for logging). -// -// Similar to absl::Status, an optional "payload" can also be included with more -// context about the error. This allows passing additional metadata about the -// error (e.g. location of errors, potential mitigations etc). -class PERFETTO_EXPORT_COMPONENT Status { +constexpr size_t kCrashKeyMaxStrSize = 32; + +// CrashKey instances must be long lived +class CrashKey { public: - Status() : ok_(true) {} - explicit Status(std::string msg) : ok_(false), message_(std::move(msg)) { - PERFETTO_CHECK(!message_.empty()); - } + class ScopedClear { + public: + explicit ScopedClear(CrashKey* k) : key_(k) {} + ~ScopedClear() { + if (key_) + key_->Clear(); + } + ScopedClear(const ScopedClear&) = delete; + ScopedClear& operator=(const ScopedClear&) = delete; + ScopedClear& operator=(ScopedClear&&) = delete; + ScopedClear(ScopedClear&& other) noexcept : key_(other.key_) { + other.key_ = nullptr; + } - // Copy operations. - Status(const Status&) = default; - Status& operator=(const Status&) = default; + private: + CrashKey* key_; + }; - // Move operations. The moved-from state is valid but unspecified. - Status(Status&&) noexcept = default; - Status& operator=(Status&&) = default; + // constexpr so it can be used in the anon namespace without requiring a + // global constructor. + // |name| must be a long-lived string. + constexpr explicit CrashKey(const char* name) + : registered_{}, type_(Type::kUnset), name_(name), str_value_{} {} + CrashKey(const CrashKey&) = delete; + CrashKey& operator=(const CrashKey&) = delete; + CrashKey(CrashKey&&) = delete; + CrashKey& operator=(CrashKey&&) = delete; - bool ok() const { return ok_; } + enum class Type : uint8_t { kUnset = 0, kInt, kStr }; - // When ok() is false this returns the error message. Returns the empty string - // otherwise. - const std::string& message() const { return message_; } - const char* c_message() const { return message_.c_str(); } + void Clear() { + int_value_.store(0, std::memory_order_relaxed); + type_.store(Type::kUnset, std::memory_order_relaxed); + } - ////////////////////////////////////////////////////////////////////////////// - // Payload Management APIs - ////////////////////////////////////////////////////////////////////////////// + void Set(int64_t value) { + int_value_.store(value, std::memory_order_relaxed); + type_.store(Type::kInt, std::memory_order_relaxed); + if (PERFETTO_UNLIKELY(!registered_.load(std::memory_order_relaxed))) + Register(); + } - // Payloads can be attached to error statuses to provide additional context. - // - // Payloads are (key, value) pairs, where the key is a string acting as a - // unique "type URL" and the value is an opaque string. The "type URL" should - // be unique, follow the format of a URL and, ideally, documentation on how to - // interpret its associated data should be available. - // - // To attach a payload to a status object, call `Status::SetPayload()`. - // Similarly, to extract the payload from a status, call - // `Status::GetPayload()`. - // - // Note: the payload APIs are only meaningful to call when the status is an - // error. Otherwise, all methods are noops. + void Set(StringView sv) { + size_t len = std::min(sv.size(), sizeof(str_value_) - 1); + for (size_t i = 0; i < len; ++i) + str_value_[i].store(sv.data()[i], std::memory_order_relaxed); + str_value_[len].store('\0', std::memory_order_relaxed); + type_.store(Type::kStr, std::memory_order_relaxed); + if (PERFETTO_UNLIKELY(!registered_.load(std::memory_order_relaxed))) + Register(); + } - // Gets the payload for the given |type_url| if one exists. - // - // Will always return std::nullopt if |ok()|. - std::optional GetPayload(std::string_view type_url) const; + ScopedClear SetScoped(int64_t value) PERFETTO_WARN_UNUSED_RESULT { + Set(value); + return ScopedClear(this); + } - // Sets the payload for the given key. The key should - // - // Will always do nothing if |ok()|. - void SetPayload(std::string_view type_url, std::string value); + ScopedClear SetScoped(StringView sv) PERFETTO_WARN_UNUSED_RESULT { + Set(sv); + return ScopedClear(this); + } - // Erases the payload for the given string and returns true if the payload - // existed and was erased. - // - // Will always do nothing if |ok()|. - bool ErasePayload(std::string_view type_url); + void Register(); + + int64_t int_value() const { + return int_value_.load(std::memory_order_relaxed); + } + size_t ToString(char* dst, size_t len); private: - struct Payload { - std::string type_url; - std::string payload; + std::atomic registered_; + std::atomic type_; + const char* const name_; + union { + std::atomic str_value_[kCrashKeyMaxStrSize]; + std::atomic int_value_; }; +}; - bool ok_ = false; - std::string message_; - std::vector payloads_; +// Fills |dst| with a string containing one line for each crash key +// (excluding the unset ones). +// Returns number of chars written, without counting the NUL terminator. +// This is used in logging.cc when emitting the crash report abort message. +size_t SerializeCrashKeys(char* dst, size_t len); + +void UnregisterAllCrashKeysForTesting(); + +} // namespace base +} // namespace perfetto + +#endif // INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_ +/* + * Copyright (C) 2021 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. + */ + +// gen_amalgamated expanded: #include "perfetto/ext/base/crash_keys.h" + +#include + +#include +#include + +// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h" + +namespace perfetto { +namespace base { + +namespace { + +constexpr size_t kMaxKeys = 32; + +std::atomic g_keys[kMaxKeys]{}; +std::atomic g_num_keys{}; +} // namespace + +void CrashKey::Register() { + // If doesn't matter if we fail below. If there are no slots left, don't + // keep trying re-registering on every Set(), the outcome won't change. + + // If two threads raced on the Register(), avoid registering the key twice. + if (registered_.exchange(true)) + return; + + uint32_t slot = g_num_keys.fetch_add(1); + if (slot >= kMaxKeys) { + PERFETTO_LOG("Too many crash keys registered"); + return; + } + g_keys[slot].store(this); +} + +// Returns the number of chars written, without counting the \0. +size_t CrashKey::ToString(char* dst, size_t len) { + if (len > 0) + *dst = '\0'; + switch (type_.load(std::memory_order_relaxed)) { + case Type::kUnset: + break; + case Type::kInt: + return SprintfTrunc(dst, len, "%s: %" PRId64 "\n", name_, + int_value_.load(std::memory_order_relaxed)); + case Type::kStr: + char buf[sizeof(str_value_)]; + for (size_t i = 0; i < sizeof(str_value_); i++) + buf[i] = str_value_[i].load(std::memory_order_relaxed); + + // Don't assume |str_value_| is properly null-terminated. + return SprintfTrunc(dst, len, "%s: %.*s\n", name_, int(sizeof(buf)), buf); + } + return 0; +} + +void UnregisterAllCrashKeysForTesting() { + g_num_keys.store(0); + for (auto& key : g_keys) + key.store(nullptr); +} + +size_t SerializeCrashKeys(char* dst, size_t len) { + size_t written = 0; + uint32_t num_keys = g_num_keys.load(); + if (len > 0) + *dst = '\0'; + for (uint32_t i = 0; i < num_keys && written < len; i++) { + CrashKey* key = g_keys[i].load(); + if (!key) + continue; // Can happen if we hit this between the add and the store. + written += key->ToString(dst + written, len - written); + } + PERFETTO_DCHECK(written <= len); + PERFETTO_DCHECK(len == 0 || dst[written] == '\0'); + return written; +} + +} // namespace base +} // namespace perfetto +// gen_amalgamated begin source: src/base/ctrl_c_handler.cc +// gen_amalgamated begin header: include/perfetto/ext/base/ctrl_c_handler.h +/* + * Copyright (C) 2021 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. + */ + +#ifndef INCLUDE_PERFETTO_EXT_BASE_CTRL_C_HANDLER_H_ +#define INCLUDE_PERFETTO_EXT_BASE_CTRL_C_HANDLER_H_ + +namespace perfetto { +namespace base { + +// On Linux/Android/Mac: installs SIGINT + SIGTERM signal handlers. +// On Windows: installs a SetConsoleCtrlHandler() handler. +// The passed handler must be async safe. +using CtrlCHandlerFunction = void (*)(); +void InstallCtrlCHandler(CtrlCHandlerFunction); + +} // namespace base +} // namespace perfetto + +#endif // INCLUDE_PERFETTO_EXT_BASE_CTRL_C_HANDLER_H_ +/* + * Copyright (C) 2021 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. + */ + +// gen_amalgamated expanded: #include "perfetto/ext/base/ctrl_c_handler.h" + +// gen_amalgamated expanded: #include "perfetto/base/build_config.h" +// gen_amalgamated expanded: #include "perfetto/base/compiler.h" +// gen_amalgamated expanded: #include "perfetto/base/logging.h" + +#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) +#include + +#include +#else +#include +#include +#endif + +namespace perfetto { +namespace base { + +namespace { +CtrlCHandlerFunction g_handler = nullptr; + +#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) +BOOL WINAPI Trampoline(DWORD type) { + if (type == CTRL_C_EVENT) { + g_handler(); + return TRUE; + } + return FALSE; +} +#endif +} // namespace + +void InstallCtrlCHandler(CtrlCHandlerFunction handler) { + PERFETTO_CHECK(g_handler == nullptr); + g_handler = handler; + +#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) + ::SetConsoleCtrlHandler(Trampoline, true); +#elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ + PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ + PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) + // Setup signal handler. + struct sigaction sa{}; + +// Glibc headers for sa_sigaction trigger this. +#pragma GCC diagnostic push +#if defined(__clang__) +#pragma GCC diagnostic ignored "-Wdisabled-macro-expansion" +#endif + sa.sa_handler = [](int) { g_handler(); }; +#if !PERFETTO_BUILDFLAG(PERFETTO_OS_QNX) + sa.sa_flags = static_cast(SA_RESETHAND | SA_RESTART); +#else // POSIX-compliant + sa.sa_flags = static_cast(SA_RESETHAND); +#endif +#pragma GCC diagnostic pop + sigaction(SIGINT, &sa, nullptr); + sigaction(SIGTERM, &sa, nullptr); +#else + // Do nothing on NaCL and Fuchsia. + ignore_result(handler); +#endif +} + +} // namespace base +} // namespace perfetto +// gen_amalgamated begin source: src/base/dynamic_string_writer.cc +// gen_amalgamated begin header: include/perfetto/ext/base/dynamic_string_writer.h +/* + * Copyright (C) 2019 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. + */ + +#ifndef INCLUDE_PERFETTO_EXT_BASE_DYNAMIC_STRING_WRITER_H_ +#define INCLUDE_PERFETTO_EXT_BASE_DYNAMIC_STRING_WRITER_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include + +// gen_amalgamated expanded: #include "perfetto/base/logging.h" +// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h" +// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h" + +namespace perfetto { +namespace base { + +// A helper class which writes formatted data to a string buffer. +// This is used in the trace processor where we write O(GBs) of strings and +// sprintf is too slow. +class DynamicStringWriter { + public: + using ScopedCString = std::unique_ptr; + + // Creates a string buffer from a char buffer and length. + DynamicStringWriter() {} + + // Appends n instances of a char to the buffer. + void AppendChar(char in, size_t n = 1) { buffer_.append(n, in); } + + // Appends a length delimited string to the buffer. + void AppendString(const char* in, size_t n) { buffer_.append(in, n); } + + void AppendStringView(StringView sv) { AppendString(sv.data(), sv.size()); } + + // Appends a null-terminated string literal to the buffer. + template + inline void AppendLiteral(const char (&in)[N]) { + AppendString(in, N - 1); + } + + // Appends a StringView to the buffer. + void AppendString(StringView data) { + buffer_.append(data.data(), data.size()); + } + + // Appends an integer to the buffer. + void AppendInt(int64_t value) { + constexpr size_t STACK_BUFFER_SIZE = 32; + StackString buf("%" PRId64, value); + AppendString(buf.string_view()); + } + + // Appends an integer to the buffer, padding with |padchar| if the number of + // digits of the integer is less than |padding|. + template + void AppendPaddedInt(int64_t sign_value) { + const bool negate = std::signbit(static_cast(sign_value)); + uint64_t absolute_value; + if (sign_value == std::numeric_limits::min()) { + absolute_value = + static_cast(std::numeric_limits::max()) + 1; + } else { + absolute_value = static_cast(std::abs(sign_value)); + } + AppendPaddedIntImpl(absolute_value, negate); + } + + void AppendUnsignedInt(uint64_t value) { + constexpr size_t STACK_BUFFER_SIZE = 32; + StackString buf("%" PRIu64, value); + AppendString(buf.string_view()); + } + + template + void AppendPaddedUnsignedInt(uint64_t value) { + AppendPaddedIntImpl(value, false); + } + + template + void AppendPaddedHexInt(IntType value, char padchar, uint64_t padding) { + using UnsignedType = std::make_unsigned_t; + constexpr size_t kMaxHexDigits = sizeof(IntType) * 2; + constexpr size_t kBufferSize = 32; + auto size_needed = + kMaxHexDigits > padding ? kMaxHexDigits : static_cast(padding); + PERFETTO_DCHECK(size_needed <= kBufferSize); + + std::array data; + constexpr char hex_asc[] = "0123456789abcdef"; + + size_t idx = size_needed - 1; + auto uvalue = static_cast(value); + do { + data[idx--] = hex_asc[uvalue & 0xF]; + uvalue >>= 4; + } while (uvalue != 0); + + if (padding > 0) { + const auto num_digits = static_cast(size_needed - 1 - idx); + // std::max() needed to work around GCC not being able to tell that + // padding > 0. + for (auto i = num_digits; i < std::max(uint64_t{1u}, padding); i++) { + data[idx--] = padchar; + } + } + AppendString(&data[idx + 1], size_needed - idx - 1); + } + + // Appends a hex integer to the buffer. + template + void AppendHexInt(IntType value) { + constexpr size_t STACK_BUFFER_SIZE = 64; + StackString buf("%" PRIx64, value); + AppendString(buf.string_view()); + } + + void AppendHexString(const uint8_t* data, size_t size, char separator); + + void AppendHexString(StringView data, char separator) { + AppendHexString(reinterpret_cast(data.data()), data.size(), + separator); + } + + // Appends a double to the buffer. + void AppendDouble(double value) { + constexpr size_t STACK_BUFFER_SIZE = 32; + StackString buf("%.16g", value); + AppendString(buf.string_view()); + } + + void AppendBool(bool value) { + if (value) { + AppendLiteral("true"); + return; + } + AppendLiteral("false"); + } + + StringView GetStringView() { + return StringView(buffer_.c_str(), buffer_.size()); + } + + ScopedCString CreateStringCopy() const { + size_t n = buffer_.size(); + char* dup = reinterpret_cast(malloc(n + 1)); + if (dup) { + memcpy(dup, buffer_.data(), n); + dup[n] = '\0'; + } + return {dup, free}; + } + + size_t pos() const { return buffer_.size(); } + + void Clear() { buffer_.clear(); } + + private: + template + void AppendPaddedIntImpl(uint64_t absolute_value, bool negate) { + // Need to add 2 to the number of digits to account for minus sign and + // rounding down of digits10. + constexpr auto kMaxDigits = std::numeric_limits::digits10 + 2; + constexpr auto kSizeNeeded = kMaxDigits > padding ? kMaxDigits : padding; + + char data[kSizeNeeded]; + + size_t idx; + for (idx = kSizeNeeded - 1; absolute_value >= 10;) { + char digit = absolute_value % 10; + absolute_value /= 10; + data[idx--] = digit + '0'; + } + data[idx--] = static_cast(absolute_value) + '0'; + + if (padding > 0) { + size_t num_digits = kSizeNeeded - 1 - idx; + // std::max() needed to work around GCC not being able to tell that + // padding > 0. + for (size_t i = num_digits; i < std::max(uint64_t{1u}, padding); i++) { + data[idx--] = padchar; + } + } + + if (negate) + AppendChar('-'); + AppendString(&data[idx + 1], kSizeNeeded - idx - 1); + } + + std::string buffer_; }; -// Returns a status object which represents the Ok status. -inline Status OkStatus() { - return Status(); -} +} // namespace base +} // namespace perfetto + +#endif // INCLUDE_PERFETTO_EXT_BASE_DYNAMIC_STRING_WRITER_H_ +/* + * Copyright (C) 2026 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. + */ + +// gen_amalgamated expanded: #include "perfetto/ext/base/dynamic_string_writer.h" + +#include +#include +#include + +namespace perfetto { +namespace base { + +void DynamicStringWriter::AppendHexString(const uint8_t* data, + size_t size, + char separator) { + // Truncate to 64 bytes, as this is the maximum supported by the Linux + // kernel's vsnprintf implementation. + size_t printed_size = std::min(size, size_t{64}); + + if (printed_size) { + AppendPaddedHexInt(data[0], '0', 2); + } + for (size_t pos = 1; pos < printed_size; pos++) { + AppendChar(separator); + AppendPaddedHexInt(data[pos], '0', 2); + } +} + +} // namespace base +} // namespace perfetto +// gen_amalgamated begin source: src/base/event_fd.cc +// gen_amalgamated begin header: include/perfetto/ext/base/event_fd.h +/* + * Copyright (C) 2018 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. + */ + +#ifndef INCLUDE_PERFETTO_EXT_BASE_EVENT_FD_H_ +#define INCLUDE_PERFETTO_EXT_BASE_EVENT_FD_H_ + +// gen_amalgamated expanded: #include "perfetto/base/build_config.h" +// gen_amalgamated expanded: #include "perfetto/base/platform_handle.h" +// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h" + +namespace perfetto { +namespace base { + +// A waitable event that can be used with poll/select. +// This is really a wrapper around eventfd_create with a pipe-based fallback +// for other platforms where eventfd is not supported. +class EventFd { + public: + EventFd(); + ~EventFd(); + EventFd(EventFd&&) noexcept = default; + EventFd& operator=(EventFd&&) = default; + + // The non-blocking file descriptor that can be polled to wait for the event. + PlatformHandle fd() const { return event_handle_.get(); } + + // Can be called from any thread. + void Notify(); + + // Can be called from any thread. If more Notify() are queued a Clear() call + // can clear all of them (up to 16 per call). + void Clear(); + + private: + // The eventfd, when eventfd is supported, otherwise this is the read end of + // the pipe for fallback mode. + ScopedPlatformHandle event_handle_; -Status ErrStatus(const char* format, ...) PERFETTO_PRINTF_FORMAT(1, 2); +// QNX is specified because it is a non-Linux UNIX platform but it +// still sets the PERFETTO_OS_LINUX flag to be as compatible as possible +// with the Linux build. +#if !PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) && \ + !PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \ + !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) + // On Mac and other non-Linux UNIX platforms a pipe-based fallback is used. + // The write end of the wakeup pipe. + ScopedFile write_fd_; +#endif +}; } // namespace base } // namespace perfetto -#endif // INCLUDE_PERFETTO_BASE_STATUS_H_ +#endif // INCLUDE_PERFETTO_EXT_BASE_EVENT_FD_H_ +// gen_amalgamated begin header: include/perfetto/ext/base/pipe.h /* * Copyright (C) 2018 The Android Open Source Project * @@ -2414,177 +3079,156 @@ Status ErrStatus(const char* format, ...) PERFETTO_PRINTF_FORMAT(1, 2); * limitations under the License. */ -#ifndef INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_ -#define INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_ - -#include // For mode_t & O_RDONLY/RDWR. Exists also on Windows. -#include - -#include -#include -#include -#include -#include +#ifndef INCLUDE_PERFETTO_EXT_BASE_PIPE_H_ +#define INCLUDE_PERFETTO_EXT_BASE_PIPE_H_ -// gen_amalgamated expanded: #include "perfetto/base/build_config.h" -// gen_amalgamated expanded: #include "perfetto/base/export.h" -// gen_amalgamated expanded: #include "perfetto/base/status.h" +// gen_amalgamated expanded: #include "perfetto/base/platform_handle.h" // gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h" -// gen_amalgamated expanded: #include "perfetto/ext/base/sys_types.h" namespace perfetto { namespace base { -class TaskRunner; - -#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) -using FileOpenMode = int; -inline constexpr char kDevNull[] = "NUL"; -inline constexpr char kFopenReadFlag[] = "r"; -#else -using FileOpenMode = mode_t; -inline constexpr char kDevNull[] = "/dev/null"; -inline constexpr char kFopenReadFlag[] = "re"; +class Pipe { + public: + enum Flags { + kBothBlock = 0, +#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) + kBothNonBlock, + kRdNonBlock, + kWrNonBlock, #endif + }; -constexpr FileOpenMode kFileModeInvalid = static_cast(-1); + static Pipe Create(Flags = kBothBlock); -bool ReadPlatformHandle(PlatformHandle, std::string* out); -bool ReadFileDescriptor(int fd, std::string* out); -bool ReadFileStream(FILE* f, std::string* out); -bool ReadFile(const std::string& path, std::string* out); + Pipe(); + Pipe(Pipe&&) noexcept; + Pipe& operator=(Pipe&&); -// A wrapper around read(2). It deals with Linux vs Windows includes. It also -// deals with handling EINTR. Has the same semantics of UNIX's read(2). -ssize_t Read(int fd, void* dst, size_t dst_size); + ScopedPlatformHandle rd; + ScopedPlatformHandle wr; +}; -// Call write until all data is written or an error is detected. -// -// man 2 write: -// If a write() is interrupted by a signal handler before any bytes are -// written, then the call fails with the error EINTR; if it is -// interrupted after at least one byte has been written, the call -// succeeds, and returns the number of bytes written. -ssize_t WriteAll(int fd, const void* buf, size_t count); +} // namespace base +} // namespace perfetto -// Copies all data from |fd_in| to |fd_out|. Saves the offset of |fd_in|, -// rewinds it to the beginning, copies the content, and restores the offset. -// |fd_in| can't be a pipe, socket of FIFO. -base::Status CopyFileContents(int fd_in, int fd_out); +#endif // INCLUDE_PERFETTO_EXT_BASE_PIPE_H_ +/* + * Copyright (C) 2018 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. + */ -ssize_t WriteAllHandle(PlatformHandle, const void* buf, size_t count); +// gen_amalgamated expanded: #include "perfetto/base/build_config.h" -ScopedFile OpenFile(const std::string& path, - int flags, - FileOpenMode = kFileModeInvalid); -ScopedFstream OpenFstream(const std::string& path, const std::string& mode); +#include +#include -// This is an alias for close(). It's to avoid leaking windows.h in headers. -// Exported because ScopedFile is used in the /include/ext API by Chromium -// component builds. -int PERFETTO_EXPORT_COMPONENT CloseFile(int fd); +#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) +#include -bool FlushFile(int fd); +#include +#elif PERFETTO_BUILDFLAG(PERFETTO_OS_QNX) +#include +#elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ + PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) +#include +#include +#else // Mac, Fuchsia and other non-Linux UNIXes +#include +#endif -// Returns true if mkdir succeeds, false if it fails (see errno in that case). -bool Mkdir(const std::string& path); +// gen_amalgamated expanded: #include "perfetto/base/logging.h" +// gen_amalgamated expanded: #include "perfetto/ext/base/event_fd.h" +// gen_amalgamated expanded: #include "perfetto/ext/base/pipe.h" +// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h" -// Calls rmdir() on UNIX, _rmdir() on Windows. -bool Rmdir(const std::string& path); +namespace perfetto { +namespace base { -// Wrapper around access(path, F_OK). -bool FileExists(const std::string& path); +EventFd::~EventFd() = default; -// Gets the extension for a filename. If the file has two extensions, returns -// only the last one (foo.pb.gz => .gz). Returns empty string if there is no -// extension. -std::string GetFileExtension(const std::string& filename); +#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) +EventFd::EventFd() { + event_handle_.reset( + CreateEventA(/*lpEventAttributes=*/nullptr, /*bManualReset=*/true, + /*bInitialState=*/false, /*bInitialState=*/nullptr)); +} -// Returns the basename component of a path (the final component after the last -// directory separator). Behaves like man 2 basename, but works with both '/' -// and '\' separators for cross-platform compatibility. -// Examples: -// Basename("/usr/bin/ls") => "ls" -// Basename("/usr/bin/") => "bin" -// Basename("/") => "/" -// Basename("foo") => "foo" -// Basename("") => "." -// Basename("C:\\Windows\\System32") => "System32" -std::string Basename(const std::string& path); +void EventFd::Notify() { + if (!SetEvent(event_handle_.get())) // 0: fail, !0: success, unlike UNIX. + PERFETTO_DFATAL("EventFd::Notify()"); +} -// Returns the directory component of a path (everything up to but not -// including the final component). Behaves like man 2 dirname, but works with -// both '/' and '\' separators for cross-platform compatibility. -// Examples: -// Dirname("/usr/bin/ls") => "/usr/bin" -// Dirname("/usr/bin") => "/usr" -// Dirname("/") => "/" -// Dirname("foo") => "." -// Dirname("") => "." -// Dirname("C:\\Windows\\System32") => "C:\\Windows" -std::string Dirname(const std::string& path); +void EventFd::Clear() { + if (!ResetEvent(event_handle_.get())) // 0: fail, !0: success, unlike UNIX. + PERFETTO_DFATAL("EventFd::Clear()"); +} -// Puts the path to all files under |dir_path| in |output|, recursively walking -// subdirectories. File paths are relative to |dir_path|. Only files are -// included, not directories. Path separator is always '/', even on windows (not -// '\'). -base::Status ListFilesRecursive(const std::string& dir_path, - std::vector& output); +#elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \ + PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) -// Lists immediate subdirectories in |dir_path| (non-recursive). Directory names -// are relative to |dir_path| and do not include the path separator. Returns -// only directories, not files. Works on both Unix and Windows. -base::Status ListDirectories(const std::string& dir_path, - std::vector& output); +EventFd::EventFd() { + event_handle_.reset(eventfd(/*initval=*/0, EFD_CLOEXEC | EFD_NONBLOCK)); + PERFETTO_CHECK(event_handle_); +} -// Sets |path|'s owner group to |group_name| and permission mode bits to -// |mode_bits|. -base::Status SetFilePermissions(const std::string& path, - const std::string& group_name, - const std::string& mode_bits); +void EventFd::Notify() { + const uint64_t value = 1; + ssize_t ret = write(event_handle_.get(), &value, sizeof(value)); + if (ret <= 0 && errno != EAGAIN) + PERFETTO_DFATAL("EventFd::Notify()"); +} -// Returns the size of the file located at |path|, or nullopt in case of error. -std::optional GetFileSize(const std::string& path); +void EventFd::Clear() { + uint64_t value; + ssize_t ret = + PERFETTO_EINTR(read(event_handle_.get(), &value, sizeof(value))); + if (ret <= 0 && errno != EAGAIN) + PERFETTO_DFATAL("EventFd::Clear()"); +} -// Returns the size of the open file |fd|, or nullopt in case of error. -std::optional GetFileSize(PlatformHandle fd); +#else -// This class uses inotify (on Linux/Android) to watch for the creation of -// files in the filesystem. When the specified file is created, it triggers a -// callback function. -// Destroying the returned unique_ptr will automatically unregister the watch. -// -// Note: This only works with filesystem paths (not abstract sockets or other -// special file types). -// It's only supported on Linux and Android, it's a no-op (returns nullptr) on -// other platforms. -// -// Usage: -// auto watch = LinuxFileWatch::WatchFileCreation( -// task_runner, "/tmp/my_file", []() { -// // Called when /tmp/my_file is created -// }); -class LinuxFileWatch { - public: - // Creates a watcher for file creation. Returns nullptr if the path is not a - // valid filesystem path or if the platform doesn't support inotify. The - // callback will be invoked on the provided TaskRunner when the file is - // created. - static std::unique_ptr WatchFileCreation( - TaskRunner*, - const char* path, - std::function callback); +EventFd::EventFd() { + // Make the pipe non-blocking so that we never block the waking thread (either + // the main thread or another one) when scheduling a wake-up. + Pipe pipe = Pipe::Create(Pipe::kBothNonBlock); + event_handle_ = ScopedPlatformHandle(std::move(pipe.rd).release()); + write_fd_ = std::move(pipe.wr); +} - virtual ~LinuxFileWatch(); +void EventFd::Notify() { + const uint64_t value = 1; + ssize_t ret = write(write_fd_.get(), &value, sizeof(uint8_t)); + if (ret <= 0 && errno != EAGAIN) + PERFETTO_DFATAL("EventFd::Notify()"); +} - protected: - LinuxFileWatch() = default; -}; +void EventFd::Clear() { + // Drain the byte(s) written to the wake-up pipe. We can potentially read + // more than one byte if several wake-ups have been scheduled. + char buffer[16]; + ssize_t ret = + PERFETTO_EINTR(read(event_handle_.get(), &buffer[0], sizeof(buffer))); + if (ret <= 0 && errno != EAGAIN) + PERFETTO_DFATAL("EventFd::Clear()"); +} +#endif } // namespace base } // namespace perfetto - -#endif // INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_ +// gen_amalgamated begin source: src/base/file_utils.cc // gen_amalgamated begin header: include/perfetto/base/task_runner.h /* * Copyright (C) 2017 The Android Open Source Project @@ -3121,11 +3765,12 @@ bool FlushFile(int fd) { #endif } -bool Mkdir(const std::string& path) { +bool Mkdir(const std::string& path, uint32_t mode) { #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) + base::ignore_result(mode); return _mkdir(path.c_str()) == 0; #else - return mkdir(path.c_str(), 0755) == 0; + return mkdir(path.c_str(), mode) == 0; #endif } @@ -3137,6 +3782,14 @@ bool Rmdir(const std::string& path) { #endif } +bool Unlink(const char* path) { +#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) + return _unlink(path) == 0; +#else + return unlink(path) == 0; +#endif +} + int CloseFile(int fd) { return close(fd); } @@ -3265,6 +3918,9 @@ base::Status ListFilesRecursive(const std::string& dir_path, struct stat dirstat; std::string full_path = cur_dir + dirent->d_name; PERFETTO_CHECK(stat(full_path.c_str(), &dirstat) == 0); + // MSan's stat() interceptor on glibc 2.35+ does not mark the output + // buffer as initialized (the syscall goes through statx). + PERFETTO_MSAN_UNPOISON(&dirstat, sizeof(dirstat)); if (S_ISDIR(dirstat.st_mode)) { dir_queue.push_back(full_path + '/'); } else if (S_ISREG(dirstat.st_mode)) { @@ -3596,275 +4252,6 @@ LinuxFileWatch::~LinuxFileWatch() = default; #endif // OS_LINUX || OS_ANDROID -} // namespace base -} // namespace perfetto -// gen_amalgamated begin source: src/base/fixed_string_writer.cc -// gen_amalgamated begin header: include/perfetto/ext/base/fixed_string_writer.h -/* - * Copyright (C) 2019 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. - */ - -#ifndef INCLUDE_PERFETTO_EXT_BASE_FIXED_STRING_WRITER_H_ -#define INCLUDE_PERFETTO_EXT_BASE_FIXED_STRING_WRITER_H_ - -#include - -#include -#include -#include -#include -#include -#include - -// gen_amalgamated expanded: #include "perfetto/base/logging.h" -// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h" -// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h" - -namespace perfetto { -namespace base { - -// A helper class which writes formatted data to a string buffer. -// This is used in the trace processor where we write O(GBs) of strings and -// sprintf is too slow. -class FixedStringWriter { - public: - // Creates a string buffer from a char buffer and length. - FixedStringWriter(char* buffer, size_t size) : buffer_(buffer), size_(size) {} - - // Appends n instances of a char to the buffer. - void AppendChar(char in, size_t n = 1) { - PERFETTO_DCHECK(pos_ + n <= size_); - memset(&buffer_[pos_], in, n); - pos_ += n; - } - - // Appends a length delimited string to the buffer. - void AppendString(const char* in, size_t n) { - PERFETTO_DCHECK(pos_ + n <= size_); - memcpy(&buffer_[pos_], in, n); - pos_ += n; - } - - void AppendStringView(StringView sv) { AppendString(sv.data(), sv.size()); } - - // Appends a null-terminated string literal to the buffer. - template - inline void AppendLiteral(const char (&in)[N]) { - AppendString(in, N - 1); - } - - // Appends a StringView to the buffer. - void AppendString(StringView data) { AppendString(data.data(), data.size()); } - - // Appends an integer to the buffer. - void AppendInt(int64_t value) { AppendPaddedInt<'0', 0>(value); } - - // Appends an integer to the buffer, padding with |padchar| if the number of - // digits of the integer is less than |padding|. - template - void AppendPaddedInt(int64_t sign_value) { - const bool negate = std::signbit(static_cast(sign_value)); - uint64_t absolute_value; - if (sign_value == std::numeric_limits::min()) { - absolute_value = - static_cast(std::numeric_limits::max()) + 1; - } else { - absolute_value = static_cast(std::abs(sign_value)); - } - AppendPaddedInt(absolute_value, negate); - } - - void AppendUnsignedInt(uint64_t value) { - AppendPaddedUnsignedInt<'0', 0>(value); - } - - // Appends an unsigned integer to the buffer, padding with |padchar| if the - // number of digits of the integer is less than |padding|. - template - void AppendPaddedUnsignedInt(uint64_t value) { - AppendPaddedInt(value, false); - } - - template - void AppendPaddedHexInt(IntType value, char padchar, uint64_t padding) { - using UnsignedType = std::make_unsigned_t; - constexpr size_t kMaxHexDigits = sizeof(IntType) * 2; - // 32 bytes is more than enough for any integer type (max 16 hex digits for - // 64-bit) - constexpr size_t kBufferSize = 32; - auto size_needed = - kMaxHexDigits > padding ? kMaxHexDigits : static_cast(padding); - PERFETTO_DCHECK(size_needed <= kBufferSize); - PERFETTO_DCHECK(pos_ + size_needed <= size_); - - std::array data; - constexpr char hex_asc[] = "0123456789abcdef"; - - size_t idx = size_needed - 1; - auto uvalue = static_cast(value); - do { - data[idx--] = hex_asc[uvalue & 0xF]; - uvalue >>= 4; - } while (uvalue != 0); - - if (padding > 0) { - const auto num_digits = static_cast(size_needed - 1 - idx); - // std::max() needed to work around GCC not being able to tell that - // padding > 0. - for (auto i = num_digits; i < std::max(uint64_t{1u}, padding); i++) { - data[idx--] = padchar; - } - } - AppendString(&data[idx + 1], size_needed - idx - 1); - } - - // Appends a hex integer to the buffer. - template - void AppendHexInt(IntType value) { - AppendPaddedHexInt(value, '0', 0); - } - - // Appends a hex string to the buffer. - void AppendHexString(const uint8_t* data, size_t size, char separator); - - void AppendHexString(StringView data, char separator) { - AppendHexString(reinterpret_cast(data.data()), data.size(), - separator); - } - - // Appends a double to the buffer. - void AppendDouble(double value) { - // TODO(lalitm): trying to optimize this is premature given we almost never - // print doubles. Reevaluate this in the future if we do print them more. - size_t res = base::SprintfTrunc(buffer_ + pos_, size_ - pos_, "%lf", value); - PERFETTO_DCHECK(pos_ + res <= size_); - pos_ += res; - } - - void AppendBool(bool value) { - if (value) { - AppendLiteral("true"); - return; - } - AppendLiteral("false"); - } - - StringView GetStringView() { - PERFETTO_DCHECK(pos_ <= size_); - return StringView(buffer_, pos_); - } - - char* CreateStringCopy() { - char* dup = reinterpret_cast(malloc(pos_ + 1)); - if (dup) { - memcpy(dup, buffer_, pos_); - dup[pos_] = '\0'; - } - return dup; - } - - size_t pos() const { return pos_; } - size_t size() const { return size_; } - void reset() { pos_ = 0; } - - private: - template - void AppendPaddedInt(uint64_t absolute_value, bool negate) { - // Need to add 2 to the number of digits to account for minus sign and - // rounding down of digits10. - constexpr auto kMaxDigits = std::numeric_limits::digits10 + 2; - constexpr auto kSizeNeeded = kMaxDigits > padding ? kMaxDigits : padding; - PERFETTO_DCHECK(pos_ + kSizeNeeded <= size_); - - char data[kSizeNeeded]; - - size_t idx; - for (idx = kSizeNeeded - 1; absolute_value >= 10;) { - char digit = absolute_value % 10; - absolute_value /= 10; - data[idx--] = digit + '0'; - } - data[idx--] = static_cast(absolute_value) + '0'; - - if (padding > 0) { - size_t num_digits = kSizeNeeded - 1 - idx; - // std::max() needed to work around GCC not being able to tell that - // padding > 0. - for (size_t i = num_digits; i < std::max(uint64_t{1u}, padding); i++) { - data[idx--] = padchar; - } - } - - if (negate) - buffer_[pos_++] = '-'; - AppendString(&data[idx + 1], kSizeNeeded - idx - 1); - } - - char* buffer_ = nullptr; - size_t size_ = 0; - size_t pos_ = 0; -}; - -} // namespace base -} // namespace perfetto - -#endif // INCLUDE_PERFETTO_EXT_BASE_FIXED_STRING_WRITER_H_ -/* - * Copyright (C) 2026 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. - */ - -// gen_amalgamated expanded: #include "perfetto/ext/base/fixed_string_writer.h" - -#include -#include -#include - -namespace perfetto { -namespace base { - -void FixedStringWriter::AppendHexString(const uint8_t* data, - size_t size, - char separator) { - // Truncate to 64 bytes, as this is the maximum supported by the Linux - // kernel's vsnprintf implementation. - size_t printed_size = std::min(size, size_t{64}); - // Remove trailing separator from calculation if printed_size > 0. - size_t max_chars = printed_size * 3 - (printed_size > 0 ? 1 : 0); - PERFETTO_DCHECK(pos_ + max_chars <= size_); - - if (printed_size) { - AppendPaddedHexInt(data[0], '0', 2); - } - for (size_t pos = 1; pos < printed_size; pos++) { - AppendChar(separator); - AppendPaddedHexInt(data[pos], '0', 2); - } -} - } // namespace base } // namespace perfetto // gen_amalgamated begin source: src/base/getopt_compat.cc @@ -3997,6 +4384,36 @@ const option* LookupShortOpt(const std::vector