Amalgamated source for v49.0
Change-Id: I6dc6868dcb0c8905b0ebb59086fa2d8127e50641
diff --git a/sdk/perfetto.cc b/sdk/perfetto.cc
new file mode 100644
index 0000000..359f4f3
--- /dev/null
+++ b/sdk/perfetto.cc
@@ -0,0 +1,66917 @@
+// 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.
+//
+// This file is automatically generated by gen_amalgamated. Do not edit.
+
+// gen_amalgamated: predefined macros
+#if !defined(PERFETTO_IMPLEMENTATION)
+#define PERFETTO_IMPLEMENTATION
+#endif
+#include "perfetto.h"
+// gen_amalgamated begin source: src/base/default_platform.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/platform.h
+/*
+ * Copyright (C) 2023 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_PLATFORM_H_
+#define INCLUDE_PERFETTO_EXT_BASE_PLATFORM_H_
+
+namespace perfetto {
+namespace base {
+namespace platform {
+
+// Executed before entering a syscall (e.g. poll, read, write etc) which might
+// block.
+// This is overridden in Google internal builds for dealing with userspace
+// scheduling.
+void BeforeMaybeBlockingSyscall();
+
+// Executed after entering a syscall (e.g. poll, read, write etc) which might
+// block.
+// This is overridden in Google internal builds for dealing with userspace
+// scheduling.
+void AfterMaybeBlockingSyscall();
+
+} // namespace platform
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_PLATFORM_H_
+/*
+ * Copyright (C) 2023 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/platform.h"
+
+namespace perfetto {
+namespace base {
+namespace platform {
+
+// This is a no-op outside of Google3 where we have some custom logic to deal
+// with the userspace scheduler.
+void BeforeMaybeBlockingSyscall() {}
+
+// This is a no-op outside of Google3 where we have some custom logic to deal
+// with the userspace scheduler.
+void AfterMaybeBlockingSyscall() {}
+
+} // namespace platform
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/android_utils.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/android_utils.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_ANDROID_UTILS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_ANDROID_UTILS_H_
+
+#include <string>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+namespace perfetto {
+namespace base {
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+
+// Returns the value of the Android system property named `name`. If the
+// property does not exist, returns an empty string (a non-existing property is
+// the same as a property with an empty value for this API).
+std::string GetAndroidProp(const char* name);
+
+#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_ANDROID_UTILS_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/android_utils.h"
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+#include <string>
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/system_properties.h>
+#endif
+
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace base {
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+
+std::string GetAndroidProp(const char* name) {
+ std::string ret;
+#if __ANDROID_API__ >= 26
+ const prop_info* pi = __system_property_find(name);
+ if (!pi) {
+ return ret;
+ }
+ __system_property_read_callback(
+ pi,
+ [](void* dst_void, const char*, const char* value, uint32_t) {
+ std::string& dst = *static_cast<std::string*>(dst_void);
+ dst = value;
+ },
+ &ret);
+#else // __ANDROID_API__ < 26
+ char value_buf[PROP_VALUE_MAX];
+ int len = __system_property_get(name, value_buf);
+ if (len > 0 && static_cast<size_t>(len) < sizeof(value_buf)) {
+ ret = std::string(value_buf, static_cast<size_t>(len));
+ }
+#endif
+ return ret;
+}
+
+#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/base64.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/base64.h
+// gen_amalgamated begin header: include/perfetto/ext/base/string_view.h
+// gen_amalgamated begin header: include/perfetto/ext/base/hash.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_HASH_H_
+#define INCLUDE_PERFETTO_EXT_BASE_HASH_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+
+namespace perfetto {
+namespace base {
+
+// A helper class which computes a 64-bit hash of the input data.
+// The algorithm used is FNV-1a as it is fast and easy to implement and has
+// relatively few collisions.
+// WARNING: This hash function should not be used for any cryptographic purpose.
+class Hasher {
+ public:
+ // Creates an empty hash object
+ constexpr Hasher() = default;
+
+ // Hashes a numeric value.
+ template <
+ typename T,
+ typename std::enable_if<std::is_arithmetic<T>::value, bool>::type = true>
+ void Update(T data) {
+ Update(reinterpret_cast<const char*>(&data), sizeof(data));
+ }
+
+ constexpr void Update(char c) { return Update(&c, 1); }
+
+ // Using the loop instead of "Update(str, strlen(str))" to avoid looping twice
+ constexpr void Update(const char* str) {
+ for (const auto* p = str; *p; ++p)
+ Update(*p);
+ }
+
+ // Hashes a byte array.
+ constexpr void Update(const char* data, size_t size) {
+ for (size_t i = 0; i < size; i++) {
+ result_ ^= static_cast<uint8_t>(data[i]);
+ // Note: Arithmetic overflow of unsigned integers is well defined in C++
+ // standard unlike signed integers.
+ // https://stackoverflow.com/a/41280273
+ result_ *= kFnv1a64Prime;
+ }
+ }
+
+ // Allow hashing anything that has `data` and `size` and has the kHashable
+ // trait (e.g., base::StringView).
+ template <typename T, typename = std::enable_if_t<T::kHashable>>
+ constexpr void Update(const T& t) {
+ if constexpr (std::is_member_function_pointer_v<decltype(&T::data)>) {
+ Update(t.data(), t.size());
+ } else {
+ Update(t.data, t.size);
+ }
+ }
+
+ constexpr void Update(std::string_view s) { Update(s.data(), s.size()); }
+
+ constexpr uint64_t digest() const { return result_; }
+
+ // Usage:
+ // uint64_t hashed_value = Hash::Combine(33, false, "ABC", 458L, 3u, 'x');
+ template <typename... Ts>
+ static constexpr uint64_t Combine(Ts&&... args) {
+ Hasher hasher;
+ hasher.UpdateAll(std::forward<Ts>(args)...);
+ return hasher.digest();
+ }
+
+ // Creates a hasher with `args` already hashed.
+ //
+ // Usage:
+ // Hasher partial = Hash::CreatePartial(33, false, "ABC", 458L);
+ template <typename... Ts>
+ static constexpr Hasher CreatePartial(Ts&&... args) {
+ Hasher hasher;
+ hasher.UpdateAll(std::forward<Ts>(args)...);
+ return hasher;
+ }
+
+ // `hasher.UpdateAll(33, false, "ABC")` is shorthand for:
+ // `hasher.Update(33); hasher.Update(false); hasher.Update("ABC");`
+ constexpr void UpdateAll() {}
+
+ template <typename T, typename... Ts>
+ constexpr void UpdateAll(T&& arg, Ts&&... args) {
+ Update(arg);
+ UpdateAll(std::forward<Ts>(args)...);
+ }
+
+ private:
+ static constexpr uint64_t kFnv1a64OffsetBasis = 0xcbf29ce484222325;
+ static constexpr uint64_t kFnv1a64Prime = 0x100000001b3;
+
+ uint64_t result_ = kFnv1a64OffsetBasis;
+};
+
+// This is for using already-hashed key into std::unordered_map and avoid the
+// cost of re-hashing. Example:
+// unordered_map<uint64_t, Value, AlreadyHashed> my_map.
+template <typename T>
+struct AlreadyHashed {
+ size_t operator()(const T& x) const { return static_cast<size_t>(x); }
+};
+
+// base::Hash uses base::Hasher for integer values and falls base to std::hash
+// for other types. This is needed as std::hash for integers is just the
+// identity function and Perfetto uses open-addressing hash table, which are
+// very sensitive to hash quality and are known to degrade in performance
+// when using std::hash.
+template <typename T>
+struct Hash {
+ // Version for ints, using base::Hasher.
+ template <typename U = T>
+ auto operator()(const U& x) ->
+ typename std::enable_if<std::is_arithmetic<U>::value, size_t>::type
+ const {
+ Hasher hash;
+ hash.Update(x);
+ return static_cast<size_t>(hash.digest());
+ }
+
+ // Version for non-ints, falling back to std::hash.
+ template <typename U = T>
+ auto operator()(const U& x) ->
+ typename std::enable_if<!std::is_arithmetic<U>::value, size_t>::type
+ const {
+ return std::hash<U>()(x);
+ }
+};
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_HASH_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_STRING_VIEW_H_
+#define INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_H_
+
+#include <string.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/hash.h"
+
+namespace perfetto {
+namespace base {
+
+// A string-like object that refers to a non-owned piece of memory.
+// Strings are internally NOT null terminated.
+class StringView {
+ public:
+ // Allow hashing with base::Hash.
+ static constexpr bool kHashable = true;
+ static constexpr size_t npos = static_cast<size_t>(-1);
+
+ StringView() : data_(nullptr), size_(0) {}
+ StringView(const StringView&) = default;
+ StringView& operator=(const StringView&) = default;
+ StringView(const char* data, size_t size) : data_(data), size_(size) {
+ PERFETTO_DCHECK(size == 0 || data != nullptr);
+ }
+
+ // Allow implicit conversion from any class that has a |data| and |size| field
+ // and has the kConvertibleToStringView trait (e.g., protozero::ConstChars).
+ template <typename T, typename = std::enable_if<T::kConvertibleToStringView>>
+ StringView(const T& x) : StringView(x.data, x.size) {
+ PERFETTO_DCHECK(x.size == 0 || x.data != nullptr);
+ }
+
+ // Creates a StringView from a null-terminated C string.
+ // Deliberately not "explicit".
+ StringView(const char* cstr) : data_(cstr), size_(strlen(cstr)) {
+ PERFETTO_DCHECK(cstr != nullptr);
+ }
+
+ // This instead has to be explicit, as creating a StringView out of a
+ // std::string can be subtle.
+ explicit StringView(const std::string& str)
+ : data_(str.data()), size_(str.size()) {}
+
+ bool empty() const { return size_ == 0; }
+ size_t size() const { return size_; }
+ const char* data() const { return data_; }
+ const char* begin() const { return data_; }
+ const char* end() const { return data_ + size_; }
+
+ char at(size_t pos) const {
+ PERFETTO_DCHECK(pos < size_);
+ return data_[pos];
+ }
+
+ size_t find(char c, size_t start_pos = 0) const {
+ for (size_t i = start_pos; i < size_; ++i) {
+ if (data_[i] == c)
+ return i;
+ }
+ return npos;
+ }
+
+ size_t find(const StringView& str, size_t start_pos = 0) const {
+ if (start_pos > size())
+ return npos;
+ auto it = std::search(begin() + start_pos, end(), str.begin(), str.end());
+ size_t pos = static_cast<size_t>(it - begin());
+ return pos + str.size() <= size() ? pos : npos;
+ }
+
+ size_t find(const char* str, size_t start_pos = 0) const {
+ return find(StringView(str), start_pos);
+ }
+
+ size_t rfind(char c) const {
+ for (size_t i = size_; i > 0; --i) {
+ if (data_[i - 1] == c)
+ return i - 1;
+ }
+ return npos;
+ }
+
+ StringView substr(size_t pos, size_t count = npos) const {
+ if (pos >= size_)
+ return StringView("", 0);
+ size_t rcount = std::min(count, size_ - pos);
+ return StringView(data_ + pos, rcount);
+ }
+
+ bool CaseInsensitiveEq(const StringView& other) const {
+ if (size() != other.size())
+ return false;
+ if (size() == 0)
+ return true;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ return _strnicmp(data(), other.data(), size()) == 0;
+#else
+ return strncasecmp(data(), other.data(), size()) == 0;
+#endif
+ }
+
+ bool CaseInsensitiveOneOf(const std::vector<StringView>& others) const {
+ for (const StringView& other : others) {
+ if (CaseInsensitiveEq(other)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool StartsWith(const StringView& other) const {
+ if (other.size() == 0)
+ return true;
+ if (size() == 0)
+ return false;
+ if (other.size() > size())
+ return false;
+ return memcmp(data(), other.data(), other.size()) == 0;
+ }
+
+ bool EndsWith(const StringView& other) const {
+ if (other.size() == 0)
+ return true;
+ if (size() == 0)
+ return false;
+ if (other.size() > size())
+ return false;
+ size_t off = size() - other.size();
+ return memcmp(data() + off, other.data(), other.size()) == 0;
+ }
+
+ std::string ToStdString() const {
+ return size_ == 0 ? "" : std::string(data_, size_);
+ }
+
+ uint64_t Hash() const {
+ base::Hasher hasher;
+ hasher.Update(data_, size_);
+ return hasher.digest();
+ }
+
+ private:
+ const char* data_ = nullptr;
+ size_t size_ = 0;
+};
+
+inline bool operator==(const StringView& x, const StringView& y) {
+ if (x.size() != y.size())
+ return false;
+ if (x.size() == 0)
+ return true;
+ return memcmp(x.data(), y.data(), x.size()) == 0;
+}
+
+inline bool operator!=(const StringView& x, const StringView& y) {
+ return !(x == y);
+}
+
+inline bool operator<(const StringView& x, const StringView& y) {
+ auto size = std::min(x.size(), y.size());
+ if (size == 0)
+ return x.size() < y.size();
+ int result = memcmp(x.data(), y.data(), size);
+ return result < 0 || (result == 0 && x.size() < y.size());
+}
+
+inline bool operator>=(const StringView& x, const StringView& y) {
+ return !(x < y);
+}
+
+inline bool operator>(const StringView& x, const StringView& y) {
+ return y < x;
+}
+
+inline bool operator<=(const StringView& x, const StringView& y) {
+ return !(y < x);
+}
+
+} // namespace base
+} // namespace perfetto
+
+template <>
+struct std::hash<::perfetto::base::StringView> {
+ size_t operator()(const ::perfetto::base::StringView& sv) const {
+ return static_cast<size_t>(sv.Hash());
+ }
+};
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_H_
+// gen_amalgamated begin header: include/perfetto/ext/base/utils.h
+// gen_amalgamated begin header: include/perfetto/ext/base/sys_types.h
+/*
+ * Copyright (C) 2022 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_SYS_TYPES_H_
+#define INCLUDE_PERFETTO_EXT_BASE_SYS_TYPES_H_
+
+// This headers deals with sys types commonly used in the codebase that are
+// missing on Windows.
+
+#include <sys/types.h> // IWYU pragma: export
+#include <cstdint>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_COMPILER_GCC)
+// MinGW has these. clang-cl and MSVC, which use just the Windows SDK, don't.
+using uid_t = int;
+using pid_t = int;
+#endif // !GCC
+
+#if defined(_WIN64)
+using ssize_t = int64_t;
+#else
+using ssize_t = long;
+#endif // _WIN64
+
+#endif // OS_WIN
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && !defined(AID_SHELL)
+// From libcutils' android_filesystem_config.h .
+#define AID_SHELL 2000
+#endif
+
+namespace perfetto {
+namespace base {
+
+// The machine ID used in the tracing core.
+using MachineID = uint32_t;
+// The default value reserved for the host trace.
+constexpr MachineID kDefaultMachineID = 0;
+
+constexpr uid_t kInvalidUid = static_cast<uid_t>(-1);
+constexpr pid_t kInvalidPid = static_cast<pid_t>(-1);
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_SYS_TYPES_H_
+/*
+ * Copyright (C) 2017 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_UTILS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_UTILS_H_
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <atomic>
+#include <functional>
+#include <memory>
+#include <string>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/sys_types.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+// Even if Windows has errno.h, the all syscall-restart behavior does not apply.
+// Trying to handle EINTR can cause more harm than good if errno is left stale.
+// Chromium does the same.
+#define PERFETTO_EINTR(x) (x)
+#else
+#define PERFETTO_EINTR(x) \
+ ([&] { \
+ decltype(x) eintr_wrapper_result; \
+ do { \
+ eintr_wrapper_result = (x); \
+ } while (eintr_wrapper_result == -1 && errno == EINTR); \
+ return eintr_wrapper_result; \
+ }())
+#endif
+
+namespace perfetto {
+namespace base {
+
+namespace internal {
+extern std::atomic<uint32_t> g_cached_page_size;
+uint32_t GetSysPageSizeSlowpath();
+} // namespace internal
+
+// Returns the system's page size. Use this when dealing with mmap, madvise and
+// similar mm-related syscalls.
+// This function might be called in hot paths. Avoid calling getpagesize() all
+// the times, in many implementations getpagesize() calls sysconf() which is
+// not cheap.
+inline uint32_t GetSysPageSize() {
+ const uint32_t page_size =
+ internal::g_cached_page_size.load(std::memory_order_relaxed);
+ return page_size != 0 ? page_size : internal::GetSysPageSizeSlowpath();
+}
+
+template <typename T, size_t TSize>
+constexpr size_t ArraySize(const T (&)[TSize]) {
+ return TSize;
+}
+
+// Function object which invokes 'free' on its parameter, which must be
+// a pointer. Can be used to store malloc-allocated pointers in std::unique_ptr:
+//
+// std::unique_ptr<int, base::FreeDeleter> foo_ptr(
+// static_cast<int*>(malloc(sizeof(int))));
+struct FreeDeleter {
+ inline void operator()(void* ptr) const { free(ptr); }
+};
+
+template <typename T>
+constexpr T AssumeLittleEndian(T value) {
+#if !PERFETTO_IS_LITTLE_ENDIAN()
+ static_assert(false, "Unimplemented on big-endian archs");
+#endif
+ return value;
+}
+
+// Round up |size| to a multiple of |alignment| (must be a power of two).
+inline constexpr size_t AlignUp(size_t size, size_t alignment) {
+ return (size + alignment - 1) & ~(alignment - 1);
+}
+
+// TODO(primiano): clean this up and move all existing usages to the constexpr
+// version above.
+template <size_t alignment>
+constexpr size_t AlignUp(size_t size) {
+ static_assert((alignment & (alignment - 1)) == 0, "alignment must be a pow2");
+ return AlignUp(size, alignment);
+}
+
+inline bool IsAgain(int err) {
+ return err == EAGAIN || err == EWOULDBLOCK;
+}
+
+// setenv(2)-equivalent. Deals with Windows vs Posix discrepancies.
+void SetEnv(const std::string& key, const std::string& value);
+
+// unsetenv(2)-equivalent. Deals with Windows vs Posix discrepancies.
+void UnsetEnv(const std::string& key);
+
+// Calls mallopt(M_PURGE, 0) on Android. Does nothing on other platforms.
+// This forces the allocator to release freed memory. This is used to work
+// around various Scudo inefficiencies. See b/170217718.
+void MaybeReleaseAllocatorMemToOS();
+
+// geteuid() on POSIX OSes, returns 0 on Windows (See comment in utils.cc).
+uid_t GetCurrentUserId();
+
+// Forks the process.
+// Parent: prints the PID of the child, calls |parent_cb| and exits from the
+// process with its return value.
+// Child: redirects stdio onto /dev/null, chdirs into / and returns.
+void Daemonize(std::function<int()> parent_cb);
+
+// Returns the path of the current executable, e.g. /foo/bar/exe.
+std::string GetCurExecutablePath();
+
+// Returns the directory where the current executable lives in, e.g. /foo/bar.
+// This is independent of cwd().
+std::string GetCurExecutableDir();
+
+// Memory returned by AlignedAlloc() must be freed via AlignedFree() not just
+// free. It makes a difference on Windows where _aligned_malloc() and
+// _aligned_free() must be paired.
+// Prefer using the AlignedAllocTyped() below which takes care of the pairing.
+void* AlignedAlloc(size_t alignment, size_t size);
+void AlignedFree(void*);
+
+// Detects Sync-mode MTE (currently being tested in some Android builds).
+// This is known to use extra memory for the stack history buffer.
+bool IsSyncMemoryTaggingEnabled();
+
+// A RAII version of the above, which takes care of pairing Aligned{Alloc,Free}.
+template <typename T>
+struct AlignedDeleter {
+ inline void operator()(T* ptr) const { AlignedFree(ptr); }
+};
+
+// The remove_extent<T> here and below is to allow defining unique_ptr<T[]>.
+// As per https://en.cppreference.com/w/cpp/memory/unique_ptr the Deleter takes
+// always a T*, not a T[]*.
+template <typename T>
+using AlignedUniquePtr =
+ std::unique_ptr<T, AlignedDeleter<typename std::remove_extent<T>::type>>;
+
+template <typename T>
+AlignedUniquePtr<T> AlignedAllocTyped(size_t n_membs) {
+ using TU = typename std::remove_extent<T>::type;
+ return AlignedUniquePtr<T>(
+ static_cast<TU*>(AlignedAlloc(alignof(TU), sizeof(TU) * n_membs)));
+}
+
+// A RAII wrapper to invoke a function when leaving a function/scope.
+template <typename Func>
+class OnScopeExitWrapper {
+ public:
+ explicit OnScopeExitWrapper(Func f) : f_(std::move(f)), active_(true) {}
+ OnScopeExitWrapper(OnScopeExitWrapper&& other) noexcept
+ : f_(std::move(other.f_)), active_(other.active_) {
+ other.active_ = false;
+ }
+ ~OnScopeExitWrapper() {
+ if (active_)
+ f_();
+ }
+
+ private:
+ Func f_;
+ bool active_;
+};
+
+template <typename Func>
+PERFETTO_WARN_UNUSED_RESULT OnScopeExitWrapper<Func> OnScopeExit(Func f) {
+ return OnScopeExitWrapper<Func>(std::move(f));
+}
+
+// Returns a xxd-style hex dump (hex + ascii chars) of the input data.
+std::string HexDump(const void* data, size_t len, size_t bytes_per_line = 16);
+inline std::string HexDump(const std::string& data,
+ size_t bytes_per_line = 16) {
+ return HexDump(data.data(), data.size(), bytes_per_line);
+}
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_UTILS_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_BASE64_H_
+#define INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
+
+#include <optional>
+#include <string>
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h" // For ssize_t.
+
+namespace perfetto {
+namespace base {
+
+// Returns the length of the destination string (included '=' padding).
+// Does NOT include the size of the string null terminator.
+inline size_t Base64EncSize(size_t src_size) {
+ return (src_size + 2) / 3 * 4;
+}
+
+// Returns the upper bound on the length of the destination buffer.
+// The actual decoded length might be <= the number returned here.
+inline size_t Base64DecSize(size_t src_size) {
+ return (src_size + 3) / 4 * 3;
+}
+
+// Does NOT null-terminate |dst|.
+ssize_t Base64Encode(const void* src,
+ size_t src_size,
+ char* dst,
+ size_t dst_size);
+
+std::string Base64Encode(const void* src, size_t src_size);
+
+inline std::string Base64Encode(StringView sv) {
+ return Base64Encode(sv.data(), sv.size());
+}
+
+// Returns -1 in case of failure.
+ssize_t Base64Decode(const char* src,
+ size_t src_size,
+ uint8_t* dst,
+ size_t dst_size);
+
+std::optional<std::string> Base64Decode(const char* src, size_t src_size);
+
+inline std::optional<std::string> Base64Decode(StringView sv) {
+ return Base64Decode(sv.data(), sv.size());
+}
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_BASE64_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/base64.h"
+
+namespace perfetto {
+namespace base {
+
+namespace {
+
+constexpr char kPadding = '=';
+
+constexpr char kEncTable[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static_assert(sizeof(kEncTable) == (1u << 6) + sizeof('\0'), "Bad table size");
+
+// Maps an ASCII character to its 6-bit value. It only contains translations
+// from '+' to 'z'. Supports the standard (+/) and URL-safe (-_) alphabets.
+constexpr uint8_t kX = 0xff; // Value used for invalid characters
+constexpr uint8_t kDecTable[] = {
+ 62, kX, 62, kX, 63, 52, 53, 54, 55, 56, // 00 - 09
+ 57, 58, 59, 60, 61, kX, kX, kX, 0, kX, // 10 - 19
+ kX, kX, 0, 1, 2, 3, 4, 5, 6, 7, // 20 - 29
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, // 30 - 39
+ 18, 19, 20, 21, 22, 23, 24, 25, kX, kX, // 40 - 49
+ kX, kX, 63, kX, 26, 27, 28, 29, 30, 31, // 50 - 59
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, // 60 - 69
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // 70 - 79
+};
+constexpr char kMinDecChar = '+';
+constexpr char kMaxDecChar = 'z';
+static_assert(kMaxDecChar - kMinDecChar <= sizeof(kDecTable), "Bad table size");
+
+inline uint8_t DecodeChar(char c) {
+ if (c < kMinDecChar || c > kMaxDecChar)
+ return kX;
+ return kDecTable[c - kMinDecChar];
+}
+
+} // namespace
+
+ssize_t Base64Encode(const void* src,
+ size_t src_size,
+ char* dst,
+ size_t dst_size) {
+ const size_t padded_dst_size = Base64EncSize(src_size);
+ if (dst_size < padded_dst_size)
+ return -1; // Not enough space in output.
+
+ const uint8_t* rd = static_cast<const uint8_t*>(src);
+ const uint8_t* const end = rd + src_size;
+ size_t wr_size = 0;
+ while (rd < end) {
+ uint8_t s[3]{};
+ s[0] = *(rd++);
+ dst[wr_size++] = kEncTable[s[0] >> 2];
+
+ uint8_t carry0 = static_cast<uint8_t>((s[0] & 0x03) << 4);
+ if (PERFETTO_LIKELY(rd < end)) {
+ s[1] = *(rd++);
+ dst[wr_size++] = kEncTable[carry0 | (s[1] >> 4)];
+ } else {
+ dst[wr_size++] = kEncTable[carry0];
+ dst[wr_size++] = kPadding;
+ dst[wr_size++] = kPadding;
+ break;
+ }
+
+ uint8_t carry1 = static_cast<uint8_t>((s[1] & 0x0f) << 2);
+ if (PERFETTO_LIKELY(rd < end)) {
+ s[2] = *(rd++);
+ dst[wr_size++] = kEncTable[carry1 | (s[2] >> 6)];
+ } else {
+ dst[wr_size++] = kEncTable[carry1];
+ dst[wr_size++] = kPadding;
+ break;
+ }
+
+ dst[wr_size++] = kEncTable[s[2] & 0x3f];
+ }
+ PERFETTO_DCHECK(wr_size == padded_dst_size);
+ return static_cast<ssize_t>(padded_dst_size);
+}
+
+std::string Base64Encode(const void* src, size_t src_size) {
+ std::string dst;
+ dst.resize(Base64EncSize(src_size));
+ auto res = Base64Encode(src, src_size, &dst[0], dst.size());
+ PERFETTO_CHECK(res == static_cast<ssize_t>(dst.size()));
+ return dst;
+}
+
+ssize_t Base64Decode(const char* src,
+ size_t src_size,
+ uint8_t* dst,
+ size_t dst_size) {
+ const size_t min_dst_size = Base64DecSize(src_size);
+ if (dst_size < min_dst_size)
+ return -1;
+
+ const char* rd = src;
+ const char* const end = src + src_size;
+ size_t wr_size = 0;
+
+ char s[4]{};
+ while (rd < end) {
+ uint8_t d[4];
+ for (uint32_t j = 0; j < 4; j++) {
+ // Padding is only feasible for the last 2 chars of each group of 4.
+ s[j] = rd < end ? *(rd++) : (j < 2 ? '\0' : kPadding);
+ d[j] = DecodeChar(s[j]);
+ if (d[j] == kX)
+ return -1; // Invalid input char.
+ }
+ dst[wr_size] = static_cast<uint8_t>((d[0] << 2) | (d[1] >> 4));
+ dst[wr_size + 1] = static_cast<uint8_t>((d[1] << 4) | (d[2] >> 2));
+ dst[wr_size + 2] = static_cast<uint8_t>((d[2] << 6) | (d[3]));
+ wr_size += 3;
+ }
+
+ PERFETTO_CHECK(wr_size <= dst_size);
+ wr_size -= (s[3] == kPadding ? 1 : 0) + (s[2] == kPadding ? 1 : 0);
+ return static_cast<ssize_t>(wr_size);
+}
+
+std::optional<std::string> Base64Decode(const char* src, size_t src_size) {
+ std::string dst;
+ dst.resize(Base64DecSize(src_size));
+ auto res = Base64Decode(src, src_size, reinterpret_cast<uint8_t*>(&dst[0]),
+ dst.size());
+ if (res < 0)
+ return std::nullopt; // Decoding error.
+
+ PERFETTO_CHECK(res <= static_cast<ssize_t>(dst.size()));
+ dst.resize(static_cast<size_t>(res));
+ return std::make_optional(dst);
+}
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/crash_keys.cc
+// gen_amalgamated begin header: 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.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_
+
+#include <algorithm>
+#include <atomic>
+
+#include <stdint.h>
+#include <string.h>
+
+// 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.
+
+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<bool> registered_;
+ std::atomic<Type> type_;
+ const char* const name_;
+ union {
+ std::atomic<char> str_value_[kCrashKeyMaxStrSize];
+ std::atomic<int64_t> int_value_;
+ };
+};
+
+// 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_
+// gen_amalgamated begin header: include/perfetto/ext/base/string_utils.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_STRING_UTILS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_STRING_UTILS_H_
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <charconv>
+#include <cinttypes>
+#include <optional>
+#include <string>
+#include <system_error>
+#include <vector>
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
+
+namespace perfetto {
+namespace base {
+
+inline char Lowercase(char c) {
+ return ('A' <= c && c <= 'Z') ? static_cast<char>(c - ('A' - 'a')) : c;
+}
+
+inline char Uppercase(char c) {
+ return ('a' <= c && c <= 'z') ? static_cast<char>(c + ('A' - 'a')) : c;
+}
+
+inline std::optional<uint32_t> CStringToUInt32(const char* s, int base = 10) {
+ char* endptr = nullptr;
+ auto value = static_cast<uint32_t>(strtoul(s, &endptr, base));
+ return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
+}
+
+inline std::optional<int32_t> CStringToInt32(const char* s, int base = 10) {
+ char* endptr = nullptr;
+ auto value = static_cast<int32_t>(strtol(s, &endptr, base));
+ return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
+}
+
+// Note: it saturates to 7fffffffffffffff if parsing a hex number >= 0x8000...
+inline std::optional<int64_t> CStringToInt64(const char* s, int base = 10) {
+ char* endptr = nullptr;
+ auto value = static_cast<int64_t>(strtoll(s, &endptr, base));
+ return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
+}
+
+inline std::optional<uint64_t> CStringToUInt64(const char* s, int base = 10) {
+ char* endptr = nullptr;
+ auto value = static_cast<uint64_t>(strtoull(s, &endptr, base));
+ return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
+}
+
+double StrToD(const char* nptr, char** endptr);
+
+inline std::optional<double> CStringToDouble(const char* s) {
+ char* endptr = nullptr;
+ double value = StrToD(s, &endptr);
+ std::optional<double> result(std::nullopt);
+ if (*s != '\0' && *endptr == '\0')
+ result = value;
+ return result;
+}
+
+inline std::optional<uint32_t> StringToUInt32(const std::string& s,
+ int base = 10) {
+ return CStringToUInt32(s.c_str(), base);
+}
+
+inline std::optional<int32_t> StringToInt32(const std::string& s,
+ int base = 10) {
+ return CStringToInt32(s.c_str(), base);
+}
+
+inline std::optional<uint64_t> StringToUInt64(const std::string& s,
+ int base = 10) {
+ return CStringToUInt64(s.c_str(), base);
+}
+
+inline std::optional<int64_t> StringToInt64(const std::string& s,
+ int base = 10) {
+ return CStringToInt64(s.c_str(), base);
+}
+
+inline std::optional<double> StringToDouble(const std::string& s) {
+ return CStringToDouble(s.c_str());
+}
+
+template <typename T>
+inline std::optional<T> StringViewToNumber(const base::StringView& sv,
+ int base = 10) {
+ // std::from_chars() does not regonize the leading '+' character and only
+ // recognizes '-' so remove the '+' if it exists to avoid errors and match
+ // the behavior of the other string conversion utilities above.
+ size_t start_offset = !sv.empty() && sv.at(0) == '+' ? 1 : 0;
+ T value;
+ auto result =
+ std::from_chars(sv.begin() + start_offset, sv.end(), value, base);
+ if (result.ec == std::errc() && result.ptr == sv.end()) {
+ return value;
+ } else {
+ return std::nullopt;
+ }
+}
+
+inline std::optional<uint32_t> StringViewToUInt32(const base::StringView& sv,
+ int base = 10) {
+ // std::from_chars() does not recognize the leading '-' character for
+ // unsigned conversions, but strtol does. To Mimic the behavior of strtol,
+ // attempt a signed converion if we see a leading '-', and then cast the
+ // result back to unsigned.
+ if (sv.size() > 0 && sv.at(0) == '-') {
+ return static_cast<std::optional<uint32_t> >(
+ StringViewToNumber<int32_t>(sv, base));
+ } else {
+ return StringViewToNumber<uint32_t>(sv, base);
+ }
+}
+
+inline std::optional<int32_t> StringViewToInt32(const base::StringView& sv,
+ int base = 10) {
+ return StringViewToNumber<int32_t>(sv, base);
+}
+
+inline std::optional<uint64_t> StringViewToUInt64(const base::StringView& sv,
+ int base = 10) {
+ // std::from_chars() does not recognize the leading '-' character for
+ // unsigned conversions, but strtol does. To Mimic the behavior of strtol,
+ // attempt a signed converion if we see a leading '-', and then cast the
+ // result back to unsigned.
+ if (sv.size() > 0 && sv.at(0) == '-') {
+ return static_cast<std::optional<uint64_t> >(
+ StringViewToNumber<int64_t>(sv, base));
+ } else {
+ return StringViewToNumber<uint64_t>(sv, base);
+ }
+}
+
+inline std::optional<int64_t> StringViewToInt64(const base::StringView& sv,
+ int base = 10) {
+ return StringViewToNumber<int64_t>(sv, base);
+}
+
+// TODO: As of Clang 19.0 std::from_chars is unimplemented for type double
+// despite being part of C++17 standard, and already being supported by GCC and
+// MSVC. Enable this once we have double support in Clang.
+// inline std::optional<double> StringViewToDouble(const base::StringView& sv) {
+// return StringViewToNumber<double>(sv);
+// }
+
+bool StartsWith(const std::string& str, const std::string& prefix);
+bool EndsWith(const std::string& str, const std::string& suffix);
+bool StartsWithAny(const std::string& str,
+ const std::vector<std::string>& prefixes);
+bool Contains(const std::string& haystack, const std::string& needle);
+bool Contains(const std::string& haystack, char needle);
+size_t Find(const StringView& needle, const StringView& haystack);
+bool CaseInsensitiveEqual(const std::string& first, const std::string& second);
+std::string Join(const std::vector<std::string>& parts,
+ const std::string& delim);
+std::vector<std::string> 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 TrimWhitespace(const std::string& str);
+std::string ToLower(const std::string& str);
+std::string ToUpper(const std::string& str);
+std::string StripChars(const std::string& str,
+ const std::string& chars,
+ char replacement);
+std::string ToHex(const char* data, size_t size);
+inline std::string ToHex(const std::string& s) {
+ return ToHex(s.c_str(), s.size());
+}
+std::string IntToHexString(uint32_t number);
+std::string Uint64ToHexString(uint64_t number);
+std::string Uint64ToHexStringNoPrefix(uint64_t number);
+std::string ReplaceAll(std::string str,
+ const std::string& to_replace,
+ const std::string& replacement);
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+bool WideToUTF8(const std::wstring& source, std::string& output);
+bool UTF8ToWide(const std::string& source, std::wstring& output);
+#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+// A BSD-style strlcpy without the return value.
+// Copies at most |dst_size|-1 characters. Unlike strncpy, it always \0
+// terminates |dst|, as long as |dst_size| is not 0.
+// Unlike strncpy and like strlcpy it does not zero-pad the rest of |dst|.
+// Returns nothing. The BSD strlcpy returns the size of |src|, which might
+// be > |dst_size|. Anecdotal experience suggests people assume the return value
+// is the number of bytes written in |dst|. That assumption can lead to
+// dangerous bugs.
+// In order to avoid being subtly uncompliant with strlcpy AND avoid misuse,
+// the choice here is to return nothing.
+inline void StringCopy(char* dst, const char* src, size_t dst_size) {
+ for (size_t i = 0; i < dst_size; ++i) {
+ if ((dst[i] = src[i]) == '\0') {
+ return; // We hit and copied the null terminator.
+ }
+ }
+
+ // We were left off at dst_size. We over copied 1 byte. Null terminate.
+ if (PERFETTO_LIKELY(dst_size > 0))
+ dst[dst_size - 1] = 0;
+}
+
+// Like snprintf() but returns the number of chars *actually* written (without
+// counting the null terminator) NOT "the number of chars which would have been
+// written to the final string if enough space had been available".
+// This should be used in almost all cases when the caller uses the return value
+// of snprintf(). If the return value is not used, there is no benefit in using
+// this wrapper, as this just calls snprintf() and mangles the return value.
+// It always null-terminates |dst| (even in case of errors), unless
+// |dst_size| == 0.
+// Examples:
+// SprintfTrunc(x, 4, "123whatever"): returns 3 and writes "123\0".
+// SprintfTrunc(x, 4, "123"): returns 3 and writes "123\0".
+// SprintfTrunc(x, 3, "123"): returns 2 and writes "12\0".
+// SprintfTrunc(x, 2, "123"): returns 1 and writes "1\0".
+// SprintfTrunc(x, 1, "123"): returns 0 and writes "\0".
+// SprintfTrunc(x, 0, "123"): returns 0 and writes nothing.
+// NOTE: This means that the caller has no way to tell when truncation happens
+// vs the edge case of *just* fitting in the buffer.
+size_t SprintfTrunc(char* dst, size_t dst_size, const char* fmt, ...)
+ PERFETTO_PRINTF_FORMAT(3, 4);
+
+// Line number starts from 1
+struct LineWithOffset {
+ base::StringView line;
+ uint32_t line_offset;
+ uint32_t line_num;
+};
+
+// For given string and offset Pfinds a line with character for
+// which offset points, what number is this line (starts from 1), and the offset
+// inside this line. returns std::nullopt if the offset points to
+// line break character or exceeds string length.
+std::optional<LineWithOffset> FindLineWithOffset(base::StringView str,
+ uint32_t offset);
+
+// A helper class to facilitate construction and usage of write-once stack
+// strings.
+// Example usage:
+// StackString<32> x("format %d %s", 42, string_arg);
+// TakeString(x.c_str() | x.string_view() | x.ToStdString());
+// Rather than char x[32] + sprintf.
+// Advantages:
+// - Avoids useless zero-fills caused by people doing `char buf[32] {}` (mainly
+// by fearing unknown snprintf failure modes).
+// - Makes the code more robust in case of snprintf truncations (len() and
+// string_view() will return the truncated length, unlike snprintf).
+template <size_t N>
+class StackString {
+ public:
+ explicit PERFETTO_PRINTF_FORMAT(/* 1=this */ 2, 3)
+ StackString(const char* fmt, ...) {
+ buf_[0] = '\0';
+ va_list args;
+ va_start(args, fmt);
+ int res = vsnprintf(buf_, sizeof(buf_), fmt, args);
+ va_end(args);
+ buf_[sizeof(buf_) - 1] = '\0';
+ len_ = res < 0 ? 0 : std::min(static_cast<size_t>(res), sizeof(buf_) - 1);
+ }
+
+ StringView string_view() const { return StringView(buf_, len_); }
+ std::string ToStdString() const { return std::string(buf_, len_); }
+ const char* c_str() const { return buf_; }
+ size_t len() const { return len_; }
+ char* mutable_data() { return buf_; }
+
+ private:
+ char buf_[N];
+ size_t len_ = 0; // Does not include the \0.
+};
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_STRING_UTILS_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 <string.h>
+
+#include <atomic>
+#include <cinttypes>
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+
+namespace perfetto {
+namespace base {
+
+namespace {
+
+constexpr size_t kMaxKeys = 32;
+
+std::atomic<CrashKey*> g_keys[kMaxKeys]{};
+std::atomic<uint32_t> 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 <Windows.h>
+#include <io.h>
+#else
+#include <signal.h>
+#include <unistd.h>
+#endif
+
+namespace perfetto {
+namespace base {
+
+namespace {
+CtrlCHandlerFunction g_handler = nullptr;
+}
+
+void InstallCtrlCHandler(CtrlCHandlerFunction handler) {
+ PERFETTO_CHECK(g_handler == nullptr);
+ g_handler = handler;
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ auto trampoline = [](DWORD type) -> int {
+ if (type == CTRL_C_EVENT) {
+ g_handler();
+ return true;
+ }
+ return false;
+ };
+ ::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<decltype(sa.sa_flags)>(SA_RESETHAND | SA_RESTART);
+#else // POSIX-compliant
+ sa.sa_flags = static_cast<decltype(sa.sa_flags)>(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/event_fd.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/event_fd.h
+// gen_amalgamated begin header: include/perfetto/ext/base/scoped_file.h
+/*
+ * Copyright (C) 2017 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_SCOPED_FILE_H_
+#define INCLUDE_PERFETTO_EXT_BASE_SCOPED_FILE_H_
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+#include <stdio.h>
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <dirent.h> // For DIR* / opendir().
+#endif
+
+#include <string>
+
+// gen_amalgamated expanded: #include "perfetto/base/export.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/platform_handle.h"
+
+namespace perfetto {
+namespace base {
+
+namespace internal {
+// Used for the most common cases of ScopedResource where there is only one
+// invalid value.
+template <typename T, T InvalidValue>
+struct DefaultValidityChecker {
+ static bool IsValid(T t) { return t != InvalidValue; }
+};
+} // namespace internal
+
+// RAII classes for auto-releasing fds and dirs.
+// if T is a pointer type, InvalidValue must be nullptr. Doing otherwise
+// causes weird unexpected behaviors (See https://godbolt.org/z/5nGMW4).
+template <typename T,
+ int (*CloseFunction)(T),
+ T InvalidValue,
+ bool CheckClose = true,
+ class Checker = internal::DefaultValidityChecker<T, InvalidValue>>
+class ScopedResource {
+ public:
+ using ValidityChecker = Checker;
+ static constexpr T kInvalid = InvalidValue;
+
+ explicit ScopedResource(T t = InvalidValue) : t_(t) {}
+ ScopedResource(ScopedResource&& other) noexcept {
+ t_ = other.t_;
+ other.t_ = InvalidValue;
+ }
+ ScopedResource& operator=(ScopedResource&& other) {
+ reset(other.t_);
+ other.t_ = InvalidValue;
+ return *this;
+ }
+ T get() const { return t_; }
+ T operator*() const { return t_; }
+ explicit operator bool() const { return Checker::IsValid(t_); }
+ void reset(T r = InvalidValue) {
+ if (Checker::IsValid(t_)) {
+ int res = CloseFunction(t_);
+ if (CheckClose)
+ PERFETTO_CHECK(res == 0);
+ }
+ t_ = r;
+ }
+ T release() {
+ T t = t_;
+ t_ = InvalidValue;
+ return t;
+ }
+ ~ScopedResource() { reset(InvalidValue); }
+
+ private:
+ ScopedResource(const ScopedResource&) = delete;
+ ScopedResource& operator=(const ScopedResource&) = delete;
+ T t_;
+};
+
+// Declared in file_utils.h. Forward declared to avoid #include cycles.
+int PERFETTO_EXPORT_COMPONENT CloseFile(int fd);
+
+// Use this for file resources obtained via open() and similar APIs.
+using ScopedFile = ScopedResource<int, CloseFile, -1>;
+using ScopedFstream = ScopedResource<FILE*, fclose, nullptr>;
+
+// Use this for resources that are HANDLE on Windows. See comments in
+// platform_handle.h
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+using ScopedPlatformHandle = ScopedResource<PlatformHandle,
+ ClosePlatformHandle,
+ /*InvalidValue=*/nullptr,
+ /*CheckClose=*/true,
+ PlatformHandleChecker>;
+#else
+// On non-windows systems we alias ScopedPlatformHandle to ScopedFile because
+// they are really the same. This is to allow assignments between the two in
+// Linux-specific code paths that predate ScopedPlatformHandle.
+static_assert(std::is_same<int, PlatformHandle>::value, "");
+using ScopedPlatformHandle = ScopedFile;
+
+// DIR* does not exist on Windows.
+using ScopedDir = ScopedResource<DIR*, closedir, nullptr>;
+#endif
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_SCOPED_FILE_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_;
+
+// 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_EXT_BASE_EVENT_FD_H_
+// gen_amalgamated begin header: 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.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_PIPE_H_
+#define INCLUDE_PERFETTO_EXT_BASE_PIPE_H_
+
+// gen_amalgamated expanded: #include "perfetto/base/platform_handle.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+
+namespace perfetto {
+namespace base {
+
+class Pipe {
+ public:
+ enum Flags {
+ kBothBlock = 0,
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ kBothNonBlock,
+ kRdNonBlock,
+ kWrNonBlock,
+#endif
+ };
+
+ static Pipe Create(Flags = kBothBlock);
+
+ Pipe();
+ Pipe(Pipe&&) noexcept;
+ Pipe& operator=(Pipe&&);
+
+ ScopedPlatformHandle rd;
+ ScopedPlatformHandle wr;
+};
+
+} // namespace base
+} // namespace perfetto
+
+#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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+#include <errno.h>
+#include <stdint.h>
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#include <synchapi.h>
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_QNX)
+#include <unistd.h>
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/eventfd.h>
+#include <unistd.h>
+#else // Mac, Fuchsia and other non-Linux UNIXes
+#include <unistd.h>
+#endif
+
+// 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"
+
+namespace perfetto {
+namespace base {
+
+EventFd::~EventFd() = default;
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+EventFd::EventFd() {
+ event_handle_.reset(
+ CreateEventA(/*lpEventAttributes=*/nullptr, /*bManualReset=*/true,
+ /*bInitialState=*/false, /*bInitialState=*/nullptr));
+}
+
+void EventFd::Notify() {
+ if (!SetEvent(event_handle_.get())) // 0: fail, !0: success, unlike UNIX.
+ PERFETTO_DFATAL("EventFd::Notify()");
+}
+
+void EventFd::Clear() {
+ if (!ResetEvent(event_handle_.get())) // 0: fail, !0: success, unlike UNIX.
+ PERFETTO_DFATAL("EventFd::Clear()");
+}
+
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+
+EventFd::EventFd() {
+ event_handle_.reset(eventfd(/*initval=*/0, EFD_CLOEXEC | EFD_NONBLOCK));
+ PERFETTO_CHECK(event_handle_);
+}
+
+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()");
+}
+
+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()");
+}
+
+#else
+
+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);
+}
+
+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()");
+}
+
+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
+// 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
+/*
+ * 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_BASE_STATUS_H_
+#define INCLUDE_PERFETTO_BASE_STATUS_H_
+
+#include <optional>
+#include <string>
+#include <string_view>
+#include <vector>
+
+// 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 {
+
+// 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());
+ }
+
+ // Copy operations.
+ Status(const Status&) = default;
+ Status& operator=(const Status&) = default;
+
+ // Move operations. The moved-from state is valid but unspecified.
+ Status(Status&&) noexcept = default;
+ Status& operator=(Status&&) = default;
+
+ bool ok() const { return ok_; }
+
+ // 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(); }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // Payload Management APIs
+ //////////////////////////////////////////////////////////////////////////////
+
+ // 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.
+
+ // Gets the payload for the given |type_url| if one exists.
+ //
+ // Will always return std::nullopt if |ok()|.
+ std::optional<std::string_view> GetPayload(std::string_view type_url) const;
+
+ // 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);
+
+ // 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);
+
+ private:
+ struct Payload {
+ std::string type_url;
+ std::string payload;
+ };
+
+ bool ok_ = false;
+ std::string message_;
+ std::vector<Payload> payloads_;
+};
+
+// 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
+
+#endif // INCLUDE_PERFETTO_BASE_STATUS_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_FILE_UTILS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_
+
+#include <fcntl.h> // For mode_t & O_RDONLY/RDWR. Exists also on Windows.
+#include <stddef.h>
+
+#include <optional>
+#include <string>
+#include <vector>
+
+// 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/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+
+namespace perfetto {
+namespace base {
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+using FileOpenMode = int;
+inline constexpr char kDevNull[] = "NUL";
+#else
+using FileOpenMode = mode_t;
+inline constexpr char kDevNull[] = "/dev/null";
+#endif
+
+constexpr FileOpenMode kFileModeInvalid = static_cast<FileOpenMode>(-1);
+
+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);
+
+// 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);
+
+ssize_t WriteAllHandle(PlatformHandle, const void* buf, size_t count);
+
+ScopedFile OpenFile(const std::string& path,
+ int flags,
+ FileOpenMode = kFileModeInvalid);
+ScopedFstream OpenFstream(const char* path, const char* 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).
+bool Mkdir(const std::string& path);
+
+// Calls rmdir() on UNIX, _rmdir() on Windows.
+bool Rmdir(const std::string& 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);
+
+// 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<std::string>& 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<uint64_t> GetFileSize(const std::string& path);
+
+// Returns the size of the open file |fd|, or nullopt in case of error.
+std::optional<uint64_t> GetFileSize(PlatformHandle fd);
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <deque>
+#include <optional>
+#include <string>
+#include <vector>
+
+// 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"
+// gen_amalgamated expanded: #include "perfetto/base/platform_handle.h"
+// gen_amalgamated expanded: #include "perfetto/base/status.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/platform.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#include <direct.h>
+#include <io.h>
+#include <stringapiset.h>
+#else
+#include <dirent.h>
+#include <unistd.h>
+#endif
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#define PERFETTO_SET_FILE_PERMISSIONS
+#include <fcntl.h>
+#include <grp.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+namespace perfetto {
+namespace base {
+namespace {
+constexpr size_t kBufSize = 2048;
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+// Wrap FindClose to: (1) make the return unix-style; (2) deal with stdcall.
+int CloseFindHandle(HANDLE h) {
+ return FindClose(h) ? 0 : -1;
+}
+
+std::optional<std::wstring> ToUtf16(const std::string str) {
+ int len = MultiByteToWideChar(CP_UTF8, 0, str.data(),
+ static_cast<int>(str.size()), nullptr, 0);
+ if (len < 0) {
+ return std::nullopt;
+ }
+ std::vector<wchar_t> tmp;
+ tmp.resize(static_cast<std::vector<wchar_t>::size_type>(len));
+ len =
+ MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()),
+ tmp.data(), static_cast<int>(tmp.size()));
+ if (len < 0) {
+ return std::nullopt;
+ }
+ PERFETTO_CHECK(static_cast<std::vector<wchar_t>::size_type>(len) ==
+ tmp.size());
+ return std::wstring(tmp.data(), tmp.size());
+}
+
+#endif
+
+} // namespace
+
+ssize_t Read(int fd, void* dst, size_t dst_size) {
+ ssize_t ret;
+ platform::BeforeMaybeBlockingSyscall();
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ ret = _read(fd, dst, static_cast<unsigned>(dst_size));
+#else
+ ret = PERFETTO_EINTR(read(fd, dst, dst_size));
+#endif
+ platform::AfterMaybeBlockingSyscall();
+ return ret;
+}
+
+bool ReadFileDescriptor(int fd, std::string* out) {
+ // Do not override existing data in string.
+ size_t i = out->size();
+
+ struct stat buf {};
+ if (fstat(fd, &buf) != -1) {
+ if (buf.st_size > 0)
+ out->resize(i + static_cast<size_t>(buf.st_size));
+ }
+
+ ssize_t bytes_read;
+ for (;;) {
+ if (out->size() < i + kBufSize)
+ out->resize(out->size() + kBufSize);
+
+ bytes_read = Read(fd, &((*out)[i]), kBufSize);
+ if (bytes_read > 0) {
+ i += static_cast<size_t>(bytes_read);
+ } else {
+ out->resize(i);
+ return bytes_read == 0;
+ }
+ }
+}
+
+bool ReadPlatformHandle(PlatformHandle h, std::string* out) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ // Do not override existing data in string.
+ size_t i = out->size();
+
+ for (;;) {
+ if (out->size() < i + kBufSize)
+ out->resize(out->size() + kBufSize);
+ DWORD bytes_read = 0;
+ auto res = ::ReadFile(h, &((*out)[i]), kBufSize, &bytes_read, nullptr);
+ if (res && bytes_read > 0) {
+ i += static_cast<size_t>(bytes_read);
+ } else {
+ out->resize(i);
+ const bool is_eof = res && bytes_read == 0;
+ auto err = res ? 0 : GetLastError();
+ // The "Broken pipe" error on Windows is slighly different than Unix:
+ // On Unix: a "broken pipe" error can happen only on the writer side. On
+ // the reader there is no broken pipe, just a EOF.
+ // On windows: the reader also sees a broken pipe error.
+ // Here we normalize on the Unix behavior, treating broken pipe as EOF.
+ return is_eof || err == ERROR_BROKEN_PIPE;
+ }
+ }
+#else
+ return ReadFileDescriptor(h, out);
+#endif
+}
+
+bool ReadFileStream(FILE* f, std::string* out) {
+ return ReadFileDescriptor(fileno(f), out);
+}
+
+bool ReadFile(const std::string& path, std::string* out) {
+ base::ScopedFile fd = base::OpenFile(path, O_RDONLY);
+ if (!fd)
+ return false;
+
+ return ReadFileDescriptor(*fd, out);
+}
+
+ssize_t WriteAll(int fd, const void* buf, size_t count) {
+ size_t written = 0;
+ while (written < count) {
+ // write() on windows takes an unsigned int size.
+ uint32_t bytes_left = static_cast<uint32_t>(
+ std::min(count - written, static_cast<size_t>(UINT32_MAX)));
+ platform::BeforeMaybeBlockingSyscall();
+ ssize_t wr = PERFETTO_EINTR(
+ write(fd, static_cast<const char*>(buf) + written, bytes_left));
+ platform::AfterMaybeBlockingSyscall();
+ if (wr == 0)
+ break;
+ if (wr < 0)
+ return wr;
+ written += static_cast<size_t>(wr);
+ }
+ return static_cast<ssize_t>(written);
+}
+
+ssize_t WriteAllHandle(PlatformHandle h, const void* buf, size_t count) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ DWORD wsize = 0;
+ if (::WriteFile(h, buf, static_cast<DWORD>(count), &wsize, nullptr)) {
+ return wsize;
+ } else {
+ return -1;
+ }
+#else
+ return WriteAll(h, buf, count);
+#endif
+}
+
+bool FlushFile(int fd) {
+ PERFETTO_DCHECK(fd != 0);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ return !PERFETTO_EINTR(fdatasync(fd));
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ return !PERFETTO_EINTR(_commit(fd));
+#else
+ return !PERFETTO_EINTR(fsync(fd));
+#endif
+}
+
+bool Mkdir(const std::string& path) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ return _mkdir(path.c_str()) == 0;
+#else
+ return mkdir(path.c_str(), 0755) == 0;
+#endif
+}
+
+bool Rmdir(const std::string& path) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ return _rmdir(path.c_str()) == 0;
+#else
+ return rmdir(path.c_str()) == 0;
+#endif
+}
+
+int CloseFile(int fd) {
+ return close(fd);
+}
+
+ScopedFile OpenFile(const std::string& path, int flags, FileOpenMode mode) {
+ // If a new file might be created, ensure that the permissions for the new
+ // file are explicitly specified.
+ PERFETTO_CHECK((flags & O_CREAT) == 0 || mode != kFileModeInvalid);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ // Always use O_BINARY on Windows, to avoid silly EOL translations.
+ ScopedFile fd(_open(path.c_str(), flags | O_BINARY, mode));
+#else
+ // Always open a ScopedFile with O_CLOEXEC so we can safely fork and exec.
+ ScopedFile fd(open(path.c_str(), flags | O_CLOEXEC, mode));
+#endif
+ return fd;
+}
+
+ScopedFstream OpenFstream(const char* path, const char* mode) {
+ ScopedFstream file;
+// On Windows fopen interprets filename using the ANSI or OEM codepage but
+// sqlite3_value_text returns a UTF-8 string. To make sure we interpret the
+// filename correctly we use _wfopen and a UTF-16 string on windows.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ auto w_path = ToUtf16(path);
+ auto w_mode = ToUtf16(mode);
+ if (w_path && w_mode) {
+ file.reset(_wfopen(w_path->c_str(), w_mode->c_str()));
+ }
+#else
+ file.reset(fopen(path, mode));
+#endif
+ return file;
+}
+
+bool FileExists(const std::string& path) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ return _access(path.c_str(), 0) == 0;
+#else
+ return access(path.c_str(), F_OK) == 0;
+#endif
+}
+
+// Declared in base/platform_handle.h.
+int ClosePlatformHandle(PlatformHandle handle) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ // Make the return value UNIX-style.
+ return CloseHandle(handle) ? 0 : -1;
+#else
+ return close(handle);
+#endif
+}
+
+base::Status ListFilesRecursive(const std::string& dir_path,
+ std::vector<std::string>& output) {
+ std::string root_dir_path = dir_path;
+ if (root_dir_path.back() == '\\') {
+ root_dir_path.back() = '/';
+ } else if (root_dir_path.back() != '/') {
+ root_dir_path.push_back('/');
+ }
+
+ // dir_queue contains full paths to the directories. The paths include the
+ // root_dir_path at the beginning and the trailing slash at the end.
+ std::deque<std::string> dir_queue;
+ dir_queue.push_back(root_dir_path);
+
+ while (!dir_queue.empty()) {
+ const std::string cur_dir = std::move(dir_queue.front());
+ dir_queue.pop_front();
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
+ return base::ErrStatus("ListFilesRecursive not supported yet");
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ std::string glob_path = cur_dir + "*";
+ // + 1 because we also have to count the NULL terminator.
+ if (glob_path.length() + 1 > MAX_PATH)
+ return base::ErrStatus("Directory path %s is too long", dir_path.c_str());
+ WIN32_FIND_DATAA ffd;
+
+ base::ScopedResource<HANDLE, CloseFindHandle, nullptr, false,
+ base::PlatformHandleChecker>
+ hFind(FindFirstFileA(glob_path.c_str(), &ffd));
+ if (!hFind) {
+ // For empty directories, there should be at least one entry '.'.
+ // If FindFirstFileA returns INVALID_HANDLE_VALUE, this means directory
+ // couldn't be accessed.
+ return base::ErrStatus("Failed to open directory %s", cur_dir.c_str());
+ }
+ do {
+ if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0)
+ continue;
+ if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ std::string subdir_path = cur_dir + ffd.cFileName + '/';
+ dir_queue.push_back(subdir_path);
+ } else {
+ const std::string full_path = cur_dir + ffd.cFileName;
+ PERFETTO_CHECK(full_path.length() > root_dir_path.length());
+ output.push_back(full_path.substr(root_dir_path.length()));
+ }
+ } while (FindNextFileA(*hFind, &ffd));
+#else
+ ScopedDir dir = ScopedDir(opendir(cur_dir.c_str()));
+ if (!dir) {
+ return base::ErrStatus("Failed to open directory %s", cur_dir.c_str());
+ }
+ for (auto* dirent = readdir(dir.get()); dirent != nullptr;
+ dirent = readdir(dir.get())) {
+ if (strcmp(dirent->d_name, ".") == 0 ||
+ strcmp(dirent->d_name, "..") == 0) {
+ continue;
+ }
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_QNX)
+ struct stat* dirstat;
+ const std::string full_path = cur_dir + dirent->d_name;
+ PERFETTO_CHECK(stat(full_path.c_str(), dirstat) == 0);
+ if (S_ISDIR(dirstat->st_mode)) {
+ dir_queue.push_back(full_path + '/');
+ } else if (S_ISREG(dirstat->st_mode)) {
+ PERFETTO_CHECK(full_path.length() > root_dir_path.length());
+ output.push_back(full_path.substr(root_dir_path.length()));
+ }
+#else
+ if (dirent->d_type == DT_DIR) {
+ dir_queue.push_back(cur_dir + dirent->d_name + '/');
+ } else if (dirent->d_type == DT_REG) {
+ const std::string full_path = cur_dir + dirent->d_name;
+ PERFETTO_CHECK(full_path.length() > root_dir_path.length());
+ output.push_back(full_path.substr(root_dir_path.length()));
+ }
+#endif
+ }
+#endif
+ }
+ return base::OkStatus();
+}
+
+std::string GetFileExtension(const std::string& filename) {
+ auto ext_idx = filename.rfind('.');
+ if (ext_idx == std::string::npos)
+ return std::string();
+ return filename.substr(ext_idx);
+}
+
+base::Status SetFilePermissions(const std::string& file_path,
+ const std::string& group_name_or_id,
+ const std::string& mode_bits) {
+#ifdef PERFETTO_SET_FILE_PERMISSIONS
+ PERFETTO_CHECK(!file_path.empty());
+ PERFETTO_CHECK(!group_name_or_id.empty());
+
+ // Default |group_id| to -1 for not changing the group ownership.
+ gid_t group_id = static_cast<gid_t>(-1);
+ auto maybe_group_id = base::StringToUInt32(group_name_or_id);
+ if (maybe_group_id) { // A numerical group ID.
+ group_id = *maybe_group_id;
+ } else { // A group name.
+ struct group* file_group = nullptr;
+ // Query the group ID of |group|.
+ do {
+ file_group = getgrnam(group_name_or_id.c_str());
+ } while (file_group == nullptr && errno == EINTR);
+ if (file_group == nullptr) {
+ return base::ErrStatus("Failed to get group information of %s ",
+ group_name_or_id.c_str());
+ }
+ group_id = file_group->gr_gid;
+ }
+
+ if (PERFETTO_EINTR(chown(file_path.c_str(), geteuid(), group_id))) {
+ return base::ErrStatus("Failed to chown %s ", file_path.c_str());
+ }
+
+ // |mode| accepts values like "0660" as "rw-rw----" mode bits.
+ auto mode_value = base::StringToInt32(mode_bits, 8);
+ if (!(mode_bits.size() == 4 && mode_value.has_value())) {
+ return base::ErrStatus(
+ "The chmod mode bits must be a 4-digit octal number, e.g. 0660");
+ }
+ if (PERFETTO_EINTR(
+ chmod(file_path.c_str(), static_cast<mode_t>(mode_value.value())))) {
+ return base::ErrStatus("Failed to chmod %s", file_path.c_str());
+ }
+ return base::OkStatus();
+#else
+ base::ignore_result(file_path);
+ base::ignore_result(group_name_or_id);
+ base::ignore_result(mode_bits);
+ return base::ErrStatus(
+ "Setting file permissions is not supported on this platform");
+#endif
+}
+
+std::optional<uint64_t> GetFileSize(const std::string& file_path) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ // This does not use base::OpenFile to avoid getting an exclusive lock.
+ base::ScopedPlatformHandle fd(
+ CreateFileA(file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
+#else
+ base::ScopedFile fd(base::OpenFile(file_path, O_RDONLY | O_CLOEXEC));
+#endif
+ if (!fd) {
+ return std::nullopt;
+ }
+ return GetFileSize(*fd);
+}
+
+std::optional<uint64_t> GetFileSize(PlatformHandle fd) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ LARGE_INTEGER file_size;
+ file_size.QuadPart = 0;
+ if (!GetFileSizeEx(fd, &file_size)) {
+ return std::nullopt;
+ }
+ static_assert(sizeof(decltype(file_size.QuadPart)) <= sizeof(uint64_t));
+ return static_cast<uint64_t>(file_size.QuadPart);
+#else
+ struct stat buf {};
+ if (fstat(fd, &buf) == -1) {
+ return std::nullopt;
+ }
+ static_assert(sizeof(decltype(buf.st_size)) <= sizeof(uint64_t));
+ return static_cast<uint64_t>(buf.st_size);
+#endif
+}
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/getopt_compat.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/getopt_compat.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_GETOPT_COMPAT_H_
+#define INCLUDE_PERFETTO_EXT_BASE_GETOPT_COMPAT_H_
+
+#include <cstddef> // For std::nullptr_t
+
+// No translation units other than base/getopt.h and getopt_compat_unittest.cc
+// should directly include this file. Use base/getopt.h instead.
+
+namespace perfetto {
+namespace base {
+namespace getopt_compat {
+
+// A tiny getopt() replacement for Windows, which doesn't have <getopt.h>.
+// This implementation is based on the subset of features that we use in the
+// Perfetto codebase. It doesn't even try to deal with the full surface of GNU's
+// getopt().
+// Limitations:
+// - getopt_long_only() is not supported.
+// - optional_argument is not supported. That is extremely subtle and caused us
+// problems in the past with GNU's getopt.
+// - It does not reorder non-option arguments. It behaves like MacOS getopt, or
+// GNU's when POSIXLY_CORRECT=1.
+// - Doesn't expose optopt or opterr.
+// - option.flag and longindex are not supported and must be nullptr.
+
+enum {
+ no_argument = 0,
+ required_argument = 1,
+};
+
+struct option {
+ const char* name;
+ int has_arg;
+ std::nullptr_t flag; // Only nullptr is supported.
+ int val;
+};
+
+extern char* optarg;
+extern int optind;
+extern int optopt;
+extern int opterr;
+
+int getopt_long(int argc,
+ char** argv,
+ const char* shortopts,
+ const option* longopts,
+ std::nullptr_t /*longindex is not supported*/);
+
+int getopt(int argc, char** argv, const char* shortopts);
+
+} // namespace getopt_compat
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_GETOPT_COMPAT_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/getopt_compat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <vector>
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace base {
+namespace getopt_compat {
+
+char* optarg = nullptr;
+int optind = 0;
+int optopt = 0;
+int opterr = 1;
+
+namespace {
+
+char* nextchar = nullptr;
+
+const option* LookupLongOpt(const std::vector<option>& opts,
+ const char* name,
+ size_t len) {
+ for (const option& opt : opts) {
+ if (strncmp(opt.name, name, len) == 0 && strlen(opt.name) == len)
+ return &opt;
+ }
+ return nullptr;
+}
+
+const option* LookupShortOpt(const std::vector<option>& opts, char c) {
+ for (const option& opt : opts) {
+ if (!*opt.name && opt.val == c)
+ return &opt;
+ }
+ return nullptr;
+}
+
+bool ParseOpts(const char* shortopts,
+ const option* longopts,
+ std::vector<option>* res) {
+ // Parse long options first.
+ for (const option* lopt = longopts; lopt && lopt->name; lopt++) {
+ PERFETTO_CHECK(lopt->flag == nullptr);
+ PERFETTO_CHECK(lopt->has_arg == no_argument ||
+ lopt->has_arg == required_argument);
+ res->emplace_back(*lopt);
+ }
+
+ // Merge short options.
+ for (const char* sopt = shortopts; sopt && *sopt;) {
+ const size_t idx = static_cast<size_t>(sopt - shortopts);
+ char c = *sopt++;
+ bool valid = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9');
+ if (!valid) {
+ fprintf(stderr,
+ "Error parsing shortopts. Unexpected char '%c' at offset %zu\n",
+ c, idx);
+ return false;
+ }
+ res->emplace_back();
+ option& opt = res->back();
+ opt.name = "";
+ opt.val = c;
+ opt.has_arg = no_argument;
+ if (*sopt == ':') {
+ opt.has_arg = required_argument;
+ ++sopt;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+int getopt_long(int argc,
+ char** argv,
+ const char* shortopts,
+ const option* longopts,
+ std::nullptr_t /*longind*/) {
+ std::vector<option> opts;
+ optarg = nullptr;
+
+ if (optind == 0)
+ optind = 1;
+
+ if (optind >= argc)
+ return -1;
+
+ if (!ParseOpts(shortopts, longopts, &opts))
+ return '?';
+
+ char* arg = argv[optind];
+ optopt = 0;
+
+ if (!nextchar) {
+ // If |nextchar| is null we are NOT in the middle of a short option and we
+ // should parse the next argv.
+ if (strncmp(arg, "--", 2) == 0 && strlen(arg) > 2) {
+ // A --long option.
+ arg += 2;
+ char* sep = strchr(arg, '=');
+ optind++;
+
+ size_t len = sep ? static_cast<size_t>(sep - arg) : strlen(arg);
+ const option* opt = LookupLongOpt(opts, arg, len);
+
+ if (!opt) {
+ if (opterr)
+ fprintf(stderr, "unrecognized option '--%s'\n", arg);
+ return '?';
+ }
+
+ optopt = opt->val;
+ if (opt->has_arg == no_argument) {
+ if (sep) {
+ fprintf(stderr, "option '--%s' doesn't allow an argument\n", arg);
+ return '?';
+ } else {
+ return opt->val;
+ }
+ } else if (opt->has_arg == required_argument) {
+ if (sep) {
+ optarg = sep + 1;
+ return opt->val;
+ } else if (optind >= argc) {
+ if (opterr)
+ fprintf(stderr, "option '--%s' requires an argument\n", arg);
+ return '?';
+ } else {
+ optarg = argv[optind++];
+ return opt->val;
+ }
+ }
+ // has_arg must be either |no_argument| or |required_argument|. We
+ // shoulnd't get here unless the check in ParseOpts() has a bug.
+ PERFETTO_CHECK(false);
+ } // if (arg ~= "--*").
+
+ if (strlen(arg) > 1 && arg[0] == '-' && arg[1] != '-') {
+ // A sequence of short options. Parsing logic continues below.
+ nextchar = &arg[1];
+ }
+ } // if(!nextchar)
+
+ if (nextchar) {
+ // At this point either:
+ // 1. This is the first char of a sequence of short options, and we fell
+ // through here from the lines above.
+ // 2. This is the N (>1) char of a sequence of short options, and we got
+ // here from a new getopt() call to getopt().
+ const char cur_char = *nextchar;
+ PERFETTO_CHECK(cur_char != '\0');
+
+ // Advance the option char in any case, before we start reasoning on them.
+ // if we got to the end of the "-abc" sequence, increment optind so the next
+ // getopt() call resumes from the next argv argument.
+ if (*(++nextchar) == '\0') {
+ nextchar = nullptr;
+ ++optind;
+ }
+
+ const option* opt = LookupShortOpt(opts, cur_char);
+ optopt = cur_char;
+ if (!opt) {
+ if (opterr)
+ fprintf(stderr, "invalid option -- '%c'\n", cur_char);
+ return '?';
+ }
+ if (opt->has_arg == no_argument) {
+ return cur_char;
+ } else if (opt->has_arg == required_argument) {
+ // This is a subtle getopt behavior. Say you call `tar -fx`, there are
+ // two cases:
+ // 1. If 'f' is no_argument then 'x' (and anything else after) is
+ // interpreted as an independent argument (like `tar -f -x`).
+ // 2. If 'f' is required_argument, than everything else after the 'f'
+ // is interpreted as the option argument (like `tar -f x`)
+ if (!nextchar) {
+ // Case 1.
+ if (optind >= argc) {
+ if (opterr)
+ fprintf(stderr, "option requires an argument -- '%c'\n", cur_char);
+ return '?';
+ } else {
+ optarg = argv[optind++];
+ return cur_char;
+ }
+ } else {
+ // Case 2.
+ optarg = nextchar;
+ nextchar = nullptr;
+ optind++;
+ return cur_char;
+ }
+ }
+ PERFETTO_CHECK(false);
+ } // if (nextchar)
+
+ // If we get here, we found the first non-option argument. Stop here.
+
+ if (strcmp(arg, "--") == 0)
+ optind++;
+
+ return -1;
+}
+
+int getopt(int argc, char** argv, const char* shortopts) {
+ return getopt_long(argc, argv, shortopts, nullptr, nullptr);
+}
+
+} // namespace getopt_compat
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/logging.cc
+// gen_amalgamated begin header: src/base/log_ring_buffer.h
+// gen_amalgamated begin header: include/perfetto/ext/base/thread_annotations.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_THREAD_ANNOTATIONS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_THREAD_ANNOTATIONS_H_
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+// Windows TSAN doesn't currently support these annotations.
+#if defined(THREAD_SANITIZER) && !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+extern "C" {
+void AnnotateBenignRaceSized(const char* file,
+ int line,
+ const volatile void* address,
+ size_t size,
+ const char* description);
+}
+
+#define PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(pointer, size, description) \
+ AnnotateBenignRaceSized(__FILE__, __LINE__, pointer, size, description);
+#else // defined(ADDRESS_SANITIZER)
+#define PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(pointer, size, description)
+#endif // defined(ADDRESS_SANITIZER)
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_THREAD_ANNOTATIONS_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 SRC_BASE_LOG_RING_BUFFER_H_
+#define SRC_BASE_LOG_RING_BUFFER_H_
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include <array>
+#include <atomic>
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_annotations.h"
+
+namespace perfetto {
+namespace base {
+
+// Defined out of line because a static constexpr requires static storage if
+// ODR-used, not worth adding a .cc file just for tests.
+constexpr size_t kLogRingBufEntries = 8;
+constexpr size_t kLogRingBufMsgLen = 256;
+
+// A static non-allocating ring-buffer to hold the most recent log events.
+// This class is really an implementation detail of logging.cc. The only reason
+// why is fully defined in a dedicated header is for allowing unittesting,
+// without leaking extra headers into logging.h (which is a high-fanout header).
+// This is used to report the last logs in a crash report when a CHECK/FATAL
+// is encountered.
+// This class has just an Append() method to insert events into the buffer and
+// a Read() to read the events in FIFO order. Read() is non-destructive.
+//
+// Thread safety considerations:
+// - The Append() method can be called concurrently by several threads, unless
+// there are > kLogRingBufEntries concurrent threads. Even if that happens,
+// case some events will contain a mix of strings but the behavior of
+// futher Append() and Read() is still defined.
+// - The Read() method is not thread safe but it's fine in practice. Even if
+// it's called concurrently with other Append(), it only causes some partial
+// events to be emitted in output.
+// In both cases, we never rely purely on \0, all operations are size-bound.
+//
+// See logging_unittest.cc for tests.
+class LogRingBuffer {
+ public:
+ LogRingBuffer() = default;
+ LogRingBuffer(const LogRingBuffer&) = delete;
+ LogRingBuffer& operator=(const LogRingBuffer&) = delete;
+ LogRingBuffer(LogRingBuffer&&) = delete;
+ LogRingBuffer& operator=(LogRingBuffer&&) = delete;
+
+ // This takes three arguments because it fits its only caller (logging.cc).
+ // The args are just concatenated together (plus one space before the msg).
+ void Append(StringView tstamp, StringView source, StringView log_msg) {
+ // Reserve atomically a slot in the ring buffer, so any concurrent Append()
+ // won't overlap (unless too many concurrent Append() happen together).
+ // There is no strict synchronization here, |event_slot_| is atomic only for
+ // the sake of avoiding colliding on the same slot but does NOT guarantee
+ // full consistency and integrity of the log messages written in each slot.
+ // A release-store (or acq+rel) won't be enough for full consistency. Two
+ // threads that race on Append() and take the N+1 and N+2 slots could finish
+ // the write in reverse order. So Read() would need to synchronize with
+ // something else (either a per-slot atomic flag or with a second atomic
+ // counter which is incremented after the snprintf). Both options increase
+ // the cost of Append() with no huge benefits (90% of the perfetto services
+ // where we use it is single thread, and the log ring buffer is disabled
+ // on non-standalone builds like the SDK).
+ uint32_t slot = event_slot_.fetch_add(1, std::memory_order_relaxed);
+ slot = slot % kLogRingBufEntries;
+
+ char* const msg = events_[slot];
+ PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(msg, kLogRingBufMsgLen,
+ "see comments in log_ring_buffer.h")
+ snprintf(msg, kLogRingBufMsgLen, "%.*s%.*s %.*s",
+ static_cast<int>(tstamp.size()), tstamp.data(),
+ static_cast<int>(source.size()), source.data(),
+ static_cast<int>(log_msg.size()), log_msg.data());
+ }
+
+ // Reads back the buffer in FIFO order, up to |len - 1| characters at most
+ // (the -1 is because a NUL terminator is always appended, unless |len| == 0).
+ // The string written in |dst| is guaranteed to be NUL-terminated, even if
+ // |len| < buffer contents length.
+ // Returns the number of bytes written in output, excluding the \0 terminator.
+ size_t Read(char* dst, size_t len) {
+ if (len == 0)
+ return 0;
+ // This is a relaxed-load because we don't need to fully synchronize on the
+ // writing path for the reasons described in the fetch_add() above.
+ const uint32_t event_slot = event_slot_.load(std::memory_order_relaxed);
+ size_t dst_written = 0;
+ for (uint32_t pos = 0; pos < kLogRingBufEntries; ++pos) {
+ const uint32_t slot = (event_slot + pos) % kLogRingBufEntries;
+ const char* src = events_[slot];
+ if (*src == '\0')
+ continue; // Empty slot. Skip.
+ char* const wptr = dst + dst_written;
+ // |src| might not be null terminated. This can happen if some
+ // thread-race happened. Limit the copy length.
+ const size_t limit = std::min(len - dst_written, kLogRingBufMsgLen);
+ for (size_t i = 0; i < limit; ++i) {
+ const char c = src[i];
+ ++dst_written;
+ if (c == '\0' || i == limit - 1) {
+ wptr[i] = '\n';
+ break;
+ }
+ // Skip non-printable ASCII characters to avoid confusing crash reports.
+ // Note that this deliberately mangles \n. Log messages should not have
+ // a \n in the middle and are NOT \n terminated. The trailing \n between
+ // each line is appended by the if () branch above.
+ const bool is_printable = c >= ' ' && c <= '~';
+ wptr[i] = is_printable ? c : '?';
+ }
+ }
+ // Ensure that the output string is null-terminated.
+ PERFETTO_DCHECK(dst_written <= len);
+ if (dst_written == len) {
+ // In case of truncation we replace the last char with \0. But the return
+ // value is the number of chars without \0, hence the --.
+ dst[--dst_written] = '\0';
+ } else {
+ dst[dst_written] = '\0';
+ }
+ return dst_written;
+ }
+
+ private:
+ using EventBuf = char[kLogRingBufMsgLen];
+ EventBuf events_[kLogRingBufEntries]{};
+
+ static_assert((kLogRingBufEntries & (kLogRingBufEntries - 1)) == 0,
+ "kLogRingBufEntries must be a power of two");
+
+ // A monotonically increasing counter incremented on each event written.
+ // It determines which of the kLogRingBufEntries indexes in |events_| should
+ // be used next.
+ // It grows >> kLogRingBufEntries, it's supposed to be always used
+ // mod(kLogRingBufEntries). A static_assert in the .cc file ensures that
+ // kLogRingBufEntries is a power of two so wraps are aligned.
+ std::atomic<uint32_t> event_slot_{};
+};
+
+} // namespace base
+} // namespace perfetto
+
+#endif // SRC_BASE_LOG_RING_BUFFER_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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <unistd.h> // For isatty()
+#endif
+
+#include <atomic>
+#include <memory>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/crash_keys.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
+// gen_amalgamated expanded: #include "src/base/log_ring_buffer.h"
+
+#if PERFETTO_ENABLE_LOG_RING_BUFFER() && PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <android/set_abort_message.h>
+#endif
+
+namespace perfetto {
+namespace base {
+
+namespace {
+const char kReset[] = "\x1b[0m";
+const char kDefault[] = "\x1b[39m";
+const char kDim[] = "\x1b[2m";
+const char kRed[] = "\x1b[31m";
+const char kBoldGreen[] = "\x1b[1m\x1b[32m";
+const char kLightGray[] = "\x1b[90m";
+
+std::atomic<LogMessageCallback> g_log_callback{};
+
+#if PERFETTO_BUILDFLAG(PERFETTO_STDERR_CRASH_DUMP)
+// __attribute__((constructor)) causes a static initializer that automagically
+// early runs this function before the main().
+void PERFETTO_EXPORT_COMPONENT __attribute__((constructor))
+InitDebugCrashReporter() {
+ // This function is defined in debug_crash_stack_trace.cc.
+ // The dynamic initializer is in logging.cc because logging.cc is included
+ // in virtually any target that depends on base. Having it in
+ // debug_crash_stack_trace.cc would require figuring out -Wl,whole-archive
+ // which is not worth it.
+ EnableStacktraceOnCrashForDebug();
+}
+#endif
+
+#if PERFETTO_ENABLE_LOG_RING_BUFFER()
+LogRingBuffer g_log_ring_buffer{};
+
+// This is global to avoid allocating memory or growing too much the stack
+// in MaybeSerializeLastLogsForCrashReporting(), which is called from
+// arbitrary code paths hitting PERFETTO_CHECK()/FATAL().
+char g_crash_buf[kLogRingBufEntries * kLogRingBufMsgLen];
+#endif
+
+} // namespace
+
+void SetLogMessageCallback(LogMessageCallback callback) {
+ g_log_callback.store(callback, std::memory_order_relaxed);
+}
+
+void LogMessage(LogLev level,
+ const char* fname,
+ int line,
+ const char* fmt,
+ ...) {
+ char stack_buf[512];
+ std::unique_ptr<char[]> large_buf;
+ char* log_msg = &stack_buf[0];
+ size_t log_msg_len = 0;
+
+ // By default use a stack allocated buffer because most log messages are quite
+ // short. In rare cases they can be larger (e.g. --help). In those cases we
+ // pay the cost of allocating the buffer on the heap.
+ for (size_t max_len = sizeof(stack_buf);;) {
+ va_list args;
+ va_start(args, fmt);
+ int res = vsnprintf(log_msg, max_len, fmt, args);
+ va_end(args);
+
+ // If for any reason the print fails, overwrite the message but still print
+ // it. The code below will attach the filename and line, which is still
+ // useful.
+ if (res < 0) {
+ snprintf(log_msg, max_len, "%s", "[printf format error]");
+ break;
+ }
+
+ // if res == max_len, vsnprintf saturated the input buffer. Retry with a
+ // larger buffer in that case (within reasonable limits).
+ if (res < static_cast<int>(max_len) || max_len >= 128 * 1024) {
+ // In case of truncation vsnprintf returns the len that "would have been
+ // written if the string was longer", not the actual chars written.
+ log_msg_len = std::min(static_cast<size_t>(res), max_len - 1);
+ break;
+ }
+ max_len *= 4;
+ large_buf.reset(new char[max_len]);
+ log_msg = &large_buf[0];
+ }
+
+ LogMessageCallback cb = g_log_callback.load(std::memory_order_relaxed);
+ if (cb) {
+ cb({level, line, fname, log_msg});
+ return;
+ }
+
+ const char* color = kDefault;
+ switch (level) {
+ case kLogDebug:
+ color = kDim;
+ break;
+ case kLogInfo:
+ color = kDefault;
+ break;
+ case kLogImportant:
+ color = kBoldGreen;
+ break;
+ case kLogError:
+ color = kRed;
+ break;
+ }
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
+ !PERFETTO_BUILDFLAG(PERFETTO_OS_WASM) && \
+ !PERFETTO_BUILDFLAG(PERFETTO_CHROMIUM_BUILD)
+ static const bool use_colors = isatty(STDERR_FILENO);
+#else
+ static const bool use_colors = false;
+#endif
+
+ // Formats file.cc:line as a space-padded fixed width string. If the file name
+ // |fname| is too long, truncate it on the left-hand side.
+ StackString<10> line_str("%d", line);
+
+ // 24 will be the width of the file.cc:line column in the log event.
+ static constexpr size_t kMaxNameAndLine = 24;
+ size_t fname_len = strlen(fname);
+ size_t fname_max = kMaxNameAndLine - line_str.len() - 2; // 2 = ':' + '\0'.
+ size_t fname_offset = fname_len <= fname_max ? 0 : fname_len - fname_max;
+ StackString<kMaxNameAndLine> file_and_line(
+ "%*s:%s", static_cast<int>(fname_max), &fname[fname_offset],
+ line_str.c_str());
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ // Logcat has already timestamping, don't re-emit it.
+ __android_log_print(int{ANDROID_LOG_DEBUG} + level, "perfetto", "%s %s",
+ file_and_line.c_str(), log_msg);
+#endif
+
+ // When printing on stderr, print also the timestamp. We don't really care
+ // about the actual time. We just need some reference clock that can be used
+ // to correlated events across differrent processses (e.g. traced and
+ // traced_probes). The wall time % 1000 is good enough.
+ uint32_t t_ms = static_cast<uint32_t>(GetWallTimeMs().count());
+ uint32_t t_sec = t_ms / 1000;
+ t_ms -= t_sec * 1000;
+ t_sec = t_sec % 1000;
+ StackString<32> timestamp("[%03u.%03u] ", t_sec, t_ms);
+
+ if (use_colors) {
+ fprintf(stderr, "%s%s%s%s %s%s%s\n", kLightGray, timestamp.c_str(),
+ file_and_line.c_str(), kReset, color, log_msg, kReset);
+ } else {
+ fprintf(stderr, "%s%s %s\n", timestamp.c_str(), file_and_line.c_str(),
+ log_msg);
+ }
+
+#if PERFETTO_ENABLE_LOG_RING_BUFFER()
+ // Append the message to the ring buffer for crash reporting postmortems.
+ StringView timestamp_sv = timestamp.string_view();
+ StringView file_and_line_sv = file_and_line.string_view();
+ StringView log_msg_sv(log_msg, static_cast<size_t>(log_msg_len));
+ g_log_ring_buffer.Append(timestamp_sv, file_and_line_sv, log_msg_sv);
+#else
+ ignore_result(log_msg_len);
+#endif
+}
+
+#if PERFETTO_ENABLE_LOG_RING_BUFFER()
+void MaybeSerializeLastLogsForCrashReporting() {
+ // Keep this function minimal. This is called from the watchdog thread, often
+ // when the system is thrashing.
+
+ // This is racy because two threads could hit a CHECK/FATAL at the same time.
+ // But if that happens we have bigger problems, not worth designing around it.
+ // The behaviour is still defined in the race case (the string attached to
+ // the crash report will contain a mixture of log strings).
+ size_t wr = 0;
+ wr += SerializeCrashKeys(&g_crash_buf[wr], sizeof(g_crash_buf) - wr);
+ wr += g_log_ring_buffer.Read(&g_crash_buf[wr], sizeof(g_crash_buf) - wr);
+
+ // Read() null-terminates the string properly. This is just to avoid UB when
+ // two threads race on each other (T1 writes a shorter string, T2
+ // overwrites the \0 writing a longer string. T1 continues here before T2
+ // finishes writing the longer string with the \0 -> boom.
+ g_crash_buf[sizeof(g_crash_buf) - 1] = '\0';
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ // android_set_abort_message() will cause debuggerd to report the message
+ // in the tombstone and in the crash log in logcat.
+ // NOTE: android_set_abort_message() can be called only once. This should
+ // be called only when we are sure we are about to crash.
+ android_set_abort_message(g_crash_buf);
+#else
+ // Print out the message on stderr on Linux/Mac/Win.
+ fputs("\n-----BEGIN PERFETTO PRE-CRASH LOG-----\n", stderr);
+ fputs(g_crash_buf, stderr);
+ fputs("\n-----END PERFETTO PRE-CRASH LOG-----\n", stderr);
+#endif
+}
+#endif // PERFETTO_ENABLE_LOG_RING_BUFFER
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/metatrace.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/metatrace.h
+// gen_amalgamated begin header: include/perfetto/ext/base/metatrace_events.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_METATRACE_EVENTS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_METATRACE_EVENTS_H_
+
+#include <stdint.h>
+
+namespace perfetto {
+namespace metatrace {
+
+enum Tags : uint32_t {
+ TAG_NONE = 0,
+ TAG_ANY = uint32_t(-1),
+ TAG_FTRACE = 1 << 0,
+ TAG_PROC_POLLERS = 1 << 1,
+ TAG_TRACE_WRITER = 1 << 2,
+ TAG_TRACE_SERVICE = 1 << 3,
+ TAG_PRODUCER = 1 << 4,
+};
+
+// The macros below generate matching enums and arrays of string literals.
+// This is to avoid maintaining string maps manually.
+
+// clang-format off
+
+// DO NOT remove or reshuffle items in this list, only append. The ID of these
+// events are an ABI, the trace processor relies on these to open old traces.
+#define PERFETTO_METATRACE_EVENTS(F) \
+ F(EVENT_ZERO_UNUSED), \
+ F(FTRACE_CPU_READER_READ), /*unused*/ \
+ F(FTRACE_DRAIN_CPUS), /*unused*/ \
+ F(FTRACE_UNBLOCK_READERS), /*unused*/ \
+ F(FTRACE_CPU_READ_NONBLOCK), /*unused*/ \
+ F(FTRACE_CPU_READ_BLOCK), /*unused*/ \
+ F(FTRACE_CPU_SPLICE_NONBLOCK), /*unused*/ \
+ F(FTRACE_CPU_SPLICE_BLOCK), /*unused*/ \
+ F(FTRACE_CPU_WAIT_CMD), /*unused*/ \
+ F(FTRACE_CPU_RUN_CYCLE), /*unused*/ \
+ F(FTRACE_CPU_FLUSH), \
+ F(FTRACE_CPU_BUFFER_WATERMARK), \
+ F(READ_SYS_STATS), \
+ F(PS_WRITE_ALL_PROCESSES), \
+ F(PS_ON_PIDS), \
+ F(PS_ON_RENAME_PIDS), \
+ F(PS_WRITE_ALL_PROCESS_STATS), \
+ F(TRACE_WRITER_COMMIT_STARTUP_WRITER_BATCH), \
+ F(FTRACE_READ_TICK), \
+ F(FTRACE_CPU_READ_CYCLE), \
+ F(FTRACE_CPU_READ_BATCH), \
+ F(KALLSYMS_PARSE), \
+ F(PROFILER_READ_TICK), \
+ F(PROFILER_READ_CPU), \
+ F(PROFILER_UNWIND_TICK), \
+ F(PROFILER_UNWIND_SAMPLE), \
+ F(PROFILER_UNWIND_INITIAL_ATTEMPT), \
+ F(PROFILER_UNWIND_ATTEMPT), \
+ F(PROFILER_MAPS_PARSE), \
+ F(PROFILER_MAPS_REPARSE), \
+ F(PROFILER_UNWIND_CACHE_CLEAR)
+
+// Append only, see above.
+//
+// Values that aren't used as counters:
+// * FTRACE_SERVICE_COMMIT_DATA is a bit-packed representation of an event, see
+// tracing_service_impl.cc for the format.
+// * PROFILER_UNWIND_CURRENT_PID represents the PID that is being unwound.
+//
+#define PERFETTO_METATRACE_COUNTERS(F) \
+ F(COUNTER_ZERO_UNUSED),\
+ F(FTRACE_PAGES_DRAINED), \
+ F(PS_PIDS_SCANNED), \
+ F(TRACE_SERVICE_COMMIT_DATA), \
+ F(PROFILER_UNWIND_QUEUE_SZ), \
+ F(PROFILER_UNWIND_CURRENT_PID)
+
+// clang-format on
+
+#define PERFETTO_METATRACE_IDENTITY(name) name
+#define PERFETTO_METATRACE_TOSTRING(name) #name
+
+enum Events : uint16_t {
+ PERFETTO_METATRACE_EVENTS(PERFETTO_METATRACE_IDENTITY),
+ EVENTS_MAX
+};
+constexpr char const* kEventNames[] = {
+ PERFETTO_METATRACE_EVENTS(PERFETTO_METATRACE_TOSTRING)};
+
+enum Counters : uint16_t {
+ PERFETTO_METATRACE_COUNTERS(PERFETTO_METATRACE_IDENTITY),
+ COUNTERS_MAX
+};
+constexpr char const* kCounterNames[] = {
+ PERFETTO_METATRACE_COUNTERS(PERFETTO_METATRACE_TOSTRING)};
+
+inline void SuppressUnusedVarsInAmalgamatedBuild() {
+ (void)kCounterNames;
+ (void)kEventNames;
+}
+
+} // namespace metatrace
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_METATRACE_EVENTS_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_METATRACE_H_
+#define INCLUDE_PERFETTO_EXT_BASE_METATRACE_H_
+
+#include <array>
+#include <atomic>
+#include <functional>
+#include <string>
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/thread_utils.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/metatrace_events.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+
+// A facility to trace execution of the perfetto codebase itself.
+// The meta-tracing framework is organized into three layers:
+//
+// 1. A static ring-buffer in base/ (this file) that supports concurrent writes
+// and a single reader.
+// The responsibility of this layer is to store events and counters as
+// efficiently as possible without re-entering any tracing code.
+// This is really a static-storage-based ring-buffer based on a POD array.
+// This layer does NOT deal with serializing the meta-trace buffer.
+// It posts a task when it's half full and expects something outside of
+// base/ to drain the ring-buffer and serialize it, eventually writing it
+// into the trace itself, before it gets 100% full.
+//
+// 2. A class in tracing/core which takes care of serializing the meta-trace
+// buffer into the trace using a TraceWriter. See metatrace_writer.h .
+//
+// 3. A data source in traced_probes that, when be enabled via the trace config,
+// injects metatrace events into the trace. See metatrace_data_source.h .
+//
+// The available events and tags are defined in metatrace_events.h .
+
+namespace perfetto {
+
+namespace base {
+class TaskRunner;
+} // namespace base
+
+namespace metatrace {
+
+// Meta-tracing is organized in "tags" that can be selectively enabled. This is
+// to enable meta-tracing only of one sub-system. This word has one "enabled"
+// bit for each tag. 0 -> meta-tracing off.
+extern std::atomic<uint32_t> g_enabled_tags;
+
+// Time of the Enable() call. Used as a reference for keeping delta timestmaps
+// in Record.
+extern std::atomic<uint64_t> g_enabled_timestamp;
+
+// Enables meta-tracing for one or more tags. Once enabled it will discard any
+// further Enable() calls and return false until disabled,
+// |read_task| is a closure that will be called enqueued |task_runner| when the
+// meta-tracing ring buffer is half full. The task is expected to read the ring
+// buffer using RingBuffer::GetReadIterator() and serialize the contents onto a
+// file or into the trace itself.
+// Must be called on the |task_runner| passed.
+// |task_runner| must have static lifetime.
+bool Enable(std::function<void()> read_task, base::TaskRunner*, uint32_t tags);
+
+// Disables meta-tracing.
+// Must be called on the same |task_runner| as Enable().
+void Disable();
+
+inline uint64_t TraceTimeNowNs() {
+ return static_cast<uint64_t>(base::GetBootTimeNs().count());
+}
+
+// Returns a relaxed view of whether metatracing is enabled for the given tag.
+// Useful for skipping unnecessary argument computation if metatracing is off.
+inline bool IsEnabled(uint32_t tag) {
+ auto enabled_tags = g_enabled_tags.load(std::memory_order_relaxed);
+ return PERFETTO_UNLIKELY((enabled_tags & tag) != 0);
+}
+
+// Holds the data for a metatrace event or counter.
+struct Record {
+ static constexpr uint16_t kTypeMask = 0x8000;
+ static constexpr uint16_t kTypeCounter = 0x8000;
+ static constexpr uint16_t kTypeEvent = 0;
+
+ uint64_t timestamp_ns() const {
+ auto base_ns = g_enabled_timestamp.load(std::memory_order_relaxed);
+ PERFETTO_DCHECK(base_ns);
+ return base_ns + ((static_cast<uint64_t>(timestamp_ns_high) << 32) |
+ timestamp_ns_low);
+ }
+
+ void set_timestamp(uint64_t ts) {
+ auto t_start = g_enabled_timestamp.load(std::memory_order_relaxed);
+ uint64_t diff = ts - t_start;
+ PERFETTO_DCHECK(diff < (1ull << 48));
+ timestamp_ns_low = static_cast<uint32_t>(diff);
+ timestamp_ns_high = static_cast<uint16_t>(diff >> 32);
+ }
+
+ // We can't just memset() this class because on MSVC std::atomic<> is not
+ // trivially constructible anymore. Also std::atomic<> has a deleted copy
+ // constructor so we cant just do "*this = Record()" either.
+ // See http://bit.ly/339Jlzd .
+ void clear() {
+ this->~Record();
+ new (this) Record();
+ }
+
+ // This field holds the type (counter vs event) in the MSB and event ID (as
+ // defined in metatrace_events.h) in the lowest 15 bits. It is also used also
+ // as a linearization point: this is always written after all the other
+ // fields with a release-store. This is so the reader can determine whether it
+ // can safely process the other event fields after a load-acquire.
+ std::atomic<uint16_t> type_and_id{};
+
+ // Timestamp is stored as a 48-bits value diffed against g_enabled_timestamp.
+ // This gives us 78 hours from Enabled().
+ uint16_t timestamp_ns_high = 0;
+ uint32_t timestamp_ns_low = 0;
+
+ uint32_t thread_id = 0;
+
+ union {
+ // Only one of the two elements can be zero initialized, clang complains
+ // about "initializing multiple members of union" otherwise.
+ uint32_t duration_ns = 0; // If type == event.
+ int32_t counter_value; // If type == counter.
+ };
+};
+
+// Hold the meta-tracing data into a statically allocated array.
+// This class uses static storage (as opposite to being a singleton) to:
+// - Have the guarantee of always valid storage, so that meta-tracing can be
+// safely used in any part of the codebase, including base/ itself.
+// - Avoid barriers that thread-safe static locals would require.
+class RingBuffer {
+ public:
+ static constexpr size_t kCapacity = 4096; // 4096 * 16 bytes = 64K.
+
+ // This iterator is not idempotent and will bump the read index in the buffer
+ // at the end of the reads. There can be only one reader at any time.
+ // Usage: for (auto it = RingBuffer::GetReadIterator(); it; ++it) { it->... }
+ class ReadIterator {
+ public:
+ ReadIterator(ReadIterator&& other) {
+ PERFETTO_DCHECK(other.valid_);
+ cur_ = other.cur_;
+ end_ = other.end_;
+ valid_ = other.valid_;
+ other.valid_ = false;
+ }
+
+ ~ReadIterator() {
+ if (!valid_)
+ return;
+ PERFETTO_DCHECK(cur_ >= RingBuffer::rd_index_);
+ PERFETTO_DCHECK(cur_ <= RingBuffer::wr_index_);
+ RingBuffer::rd_index_.store(cur_, std::memory_order_release);
+ }
+
+ explicit operator bool() const { return cur_ < end_; }
+ const Record* operator->() const { return RingBuffer::At(cur_); }
+ const Record& operator*() const { return *operator->(); }
+
+ // This is for ++it. it++ is deliberately not supported.
+ ReadIterator& operator++() {
+ PERFETTO_DCHECK(cur_ < end_);
+ // Once a record has been read, mark it as free clearing its type_and_id,
+ // so if we encounter it in another read iteration while being written
+ // we know it's not fully written yet.
+ // The memory_order_relaxed below is enough because:
+ // - The reader is single-threaded and doesn't re-read the same records.
+ // - Before starting a read batch, the reader has an acquire barrier on
+ // |rd_index_|.
+ // - After terminating a read batch, the ~ReadIterator dtor updates the
+ // |rd_index_| with a release-store.
+ // - Reader and writer are typically kCapacity/2 apart. So unless an
+ // overrun happens a writer won't reuse a newly released record any time
+ // soon. If an overrun happens, everything is busted regardless.
+ At(cur_)->type_and_id.store(0, std::memory_order_relaxed);
+ ++cur_;
+ return *this;
+ }
+
+ private:
+ friend class RingBuffer;
+ ReadIterator(uint64_t begin, uint64_t end)
+ : cur_(begin), end_(end), valid_(true) {}
+ ReadIterator& operator=(const ReadIterator&) = delete;
+ ReadIterator(const ReadIterator&) = delete;
+
+ uint64_t cur_;
+ uint64_t end_;
+ bool valid_;
+ };
+
+ static Record* At(uint64_t index) {
+ // Doesn't really have to be pow2, but if not the compiler will emit
+ // arithmetic operations to compute the modulo instead of a bitwise AND.
+ static_assert(!(kCapacity & (kCapacity - 1)), "kCapacity must be pow2");
+ PERFETTO_DCHECK(index >= rd_index_);
+ PERFETTO_DCHECK(index <= wr_index_);
+ return &records_[index % kCapacity];
+ }
+
+ // Must be called on the same task runner passed to Enable()
+ static ReadIterator GetReadIterator() {
+ PERFETTO_DCHECK(RingBuffer::IsOnValidTaskRunner());
+ return ReadIterator(rd_index_.load(std::memory_order_acquire),
+ wr_index_.load(std::memory_order_acquire));
+ }
+
+ static Record* AppendNewRecord();
+ static void Reset();
+
+ static bool has_overruns() {
+ return has_overruns_.load(std::memory_order_acquire);
+ }
+
+ // Can temporarily return a value >= kCapacity but is eventually consistent.
+ // This would happen in case of overruns until threads hit the --wr_index_
+ // in AppendNewRecord().
+ static uint64_t GetSizeForTesting() {
+ auto wr_index = wr_index_.load(std::memory_order_relaxed);
+ auto rd_index = rd_index_.load(std::memory_order_relaxed);
+ PERFETTO_DCHECK(wr_index >= rd_index);
+ return wr_index - rd_index;
+ }
+
+ private:
+ friend class ReadIterator;
+
+ // Returns true if the caller is on the task runner passed to Enable().
+ // Used only for DCHECKs.
+ static bool IsOnValidTaskRunner();
+
+ static std::array<Record, kCapacity> records_;
+ static std::atomic<bool> read_task_queued_;
+ static std::atomic<uint64_t> wr_index_;
+ static std::atomic<uint64_t> rd_index_;
+ static std::atomic<bool> has_overruns_;
+ static Record bankruptcy_record_; // Used in case of overruns.
+};
+
+inline void TraceCounter(uint32_t tag, uint16_t id, int32_t value) {
+ // memory_order_relaxed is okay because the storage has static lifetime.
+ // It is safe to accidentally log an event soon after disabling.
+ auto enabled_tags = g_enabled_tags.load(std::memory_order_relaxed);
+ if (PERFETTO_LIKELY((enabled_tags & tag) == 0))
+ return;
+ Record* record = RingBuffer::AppendNewRecord();
+ record->thread_id = static_cast<uint32_t>(base::GetThreadId());
+ record->set_timestamp(TraceTimeNowNs());
+ record->counter_value = value;
+ record->type_and_id.store(Record::kTypeCounter | id,
+ std::memory_order_release);
+}
+
+class ScopedEvent {
+ public:
+ ScopedEvent(uint32_t tag, uint16_t event_id) {
+ auto enabled_tags = g_enabled_tags.load(std::memory_order_relaxed);
+ if (PERFETTO_LIKELY((enabled_tags & tag) == 0))
+ return;
+ event_id_ = event_id;
+ record_ = RingBuffer::AppendNewRecord();
+ record_->thread_id = static_cast<uint32_t>(base::GetThreadId());
+ record_->set_timestamp(TraceTimeNowNs());
+ }
+
+ ~ScopedEvent() {
+ if (PERFETTO_LIKELY(!record_))
+ return;
+ auto now = TraceTimeNowNs();
+ record_->duration_ns = static_cast<uint32_t>(now - record_->timestamp_ns());
+ record_->type_and_id.store(Record::kTypeEvent | event_id_,
+ std::memory_order_release);
+ }
+
+ private:
+ Record* record_ = nullptr;
+ uint16_t event_id_ = 0;
+ ScopedEvent(const ScopedEvent&) = delete;
+ ScopedEvent& operator=(const ScopedEvent&) = delete;
+};
+
+// Boilerplate to derive a unique variable name for the event.
+#define PERFETTO_METATRACE_UID2(a, b) a##b
+#define PERFETTO_METATRACE_UID(x) PERFETTO_METATRACE_UID2(metatrace_, x)
+
+#define PERFETTO_METATRACE_SCOPED(TAG, ID) \
+ ::perfetto::metatrace::ScopedEvent PERFETTO_METATRACE_UID(__COUNTER__)( \
+ ::perfetto::metatrace::TAG, ::perfetto::metatrace::ID)
+
+#define PERFETTO_METATRACE_COUNTER(TAG, ID, VALUE) \
+ ::perfetto::metatrace::TraceCounter(::perfetto::metatrace::TAG, \
+ ::perfetto::metatrace::ID, \
+ static_cast<int32_t>(VALUE))
+
+} // namespace metatrace
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_METATRACE_H_
+// gen_amalgamated begin header: include/perfetto/base/task_runner.h
+/*
+ * Copyright (C) 2017 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_BASE_TASK_RUNNER_H_
+#define INCLUDE_PERFETTO_BASE_TASK_RUNNER_H_
+
+#include <stdint.h>
+
+#include <functional>
+
+// gen_amalgamated expanded: #include "perfetto/base/export.h"
+// gen_amalgamated expanded: #include "perfetto/base/platform_handle.h"
+
+namespace perfetto {
+namespace base {
+
+// A generic interface to allow the library clients to interleave the execution
+// of the tracing internals in their runtime environment.
+// The expectation is that all tasks, which are queued either via PostTask() or
+// AddFileDescriptorWatch(), are executed on the same sequence (either on the
+// same thread, or on a thread pool that gives sequencing guarantees).
+//
+// Tasks are never executed synchronously inside PostTask and there is a full
+// memory barrier between tasks.
+//
+// All methods of this interface can be called from any thread.
+class PERFETTO_EXPORT_COMPONENT TaskRunner {
+ public:
+ virtual ~TaskRunner();
+
+ // Schedule a task for immediate execution. Immediate tasks are always
+ // executed in the order they are posted. Can be called from any thread.
+ virtual void PostTask(std::function<void()>) = 0;
+
+ // Schedule a task for execution after |delay_ms|. Note that there is no
+ // strict ordering guarantee between immediate and delayed tasks. Can be
+ // called from any thread.
+ virtual void PostDelayedTask(std::function<void()>, uint32_t delay_ms) = 0;
+
+ // Schedule a task to run when the handle becomes readable. The same handle
+ // can only be monitored by one function. Note that this function only needs
+ // to be implemented on platforms where the built-in ipc framework is used.
+ // Can be called from any thread.
+ // TODO(skyostil): Refactor this out of the shared interface.
+ virtual void AddFileDescriptorWatch(PlatformHandle,
+ std::function<void()>) = 0;
+
+ // Remove a previously scheduled watch for the handle. If this is run on the
+ // target thread of this TaskRunner, guarantees that the task registered to
+ // this handle will not be executed after this function call.
+ // Can be called from any thread.
+ virtual void RemoveFileDescriptorWatch(PlatformHandle) = 0;
+
+ // Checks if the current thread is the same thread where the TaskRunner's task
+ // run. This allows single threaded task runners (like the ones used in
+ // perfetto) to inform the caller that anything posted will run on the same
+ // thread/sequence. This can allow some callers to skip PostTask and instead
+ // directly execute the code. Can be called from any thread.
+ virtual bool RunsTasksOnCurrentThread() const = 0;
+};
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_BASE_TASK_RUNNER_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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/metatrace.h"
+
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_annotations.h"
+
+namespace perfetto {
+namespace metatrace {
+
+std::atomic<uint32_t> g_enabled_tags{0};
+std::atomic<uint64_t> g_enabled_timestamp{0};
+
+// static members
+std::array<Record, RingBuffer::kCapacity> RingBuffer::records_;
+std::atomic<bool> RingBuffer::read_task_queued_;
+std::atomic<uint64_t> RingBuffer::wr_index_;
+std::atomic<uint64_t> RingBuffer::rd_index_;
+std::atomic<bool> RingBuffer::has_overruns_;
+Record RingBuffer::bankruptcy_record_;
+
+namespace {
+
+// std::function<> is not trivially de/constructible. This struct wraps it in a
+// heap-allocated struct to avoid static initializers.
+struct Delegate {
+ static Delegate* GetInstance() {
+ static Delegate* instance = new Delegate();
+ return instance;
+ }
+
+ base::TaskRunner* task_runner = nullptr;
+ std::function<void()> read_task;
+};
+
+} // namespace
+
+bool Enable(std::function<void()> read_task,
+ base::TaskRunner* task_runner,
+ uint32_t tags) {
+ PERFETTO_DCHECK(read_task);
+ PERFETTO_DCHECK(task_runner->RunsTasksOnCurrentThread());
+ if (g_enabled_tags.load(std::memory_order_acquire))
+ return false;
+
+ Delegate* dg = Delegate::GetInstance();
+ dg->task_runner = task_runner;
+ dg->read_task = std::move(read_task);
+ RingBuffer::Reset();
+ g_enabled_timestamp.store(TraceTimeNowNs(), std::memory_order_relaxed);
+ g_enabled_tags.store(tags, std::memory_order_release);
+ return true;
+}
+
+void Disable() {
+ g_enabled_tags.store(0, std::memory_order_release);
+ Delegate* dg = Delegate::GetInstance();
+ PERFETTO_DCHECK(!dg->task_runner ||
+ dg->task_runner->RunsTasksOnCurrentThread());
+ dg->task_runner = nullptr;
+ dg->read_task = nullptr;
+}
+
+// static
+void RingBuffer::Reset() {
+ bankruptcy_record_.clear();
+ for (Record& record : records_)
+ record.clear();
+ wr_index_ = 0;
+ rd_index_ = 0;
+ has_overruns_ = false;
+ read_task_queued_ = false;
+}
+
+// static
+Record* RingBuffer::AppendNewRecord() {
+ auto wr_index = wr_index_.fetch_add(1, std::memory_order_acq_rel);
+
+ // rd_index can only monotonically increase, we don't care if we read an
+ // older value, we'll just hit the slow-path a bit earlier if it happens.
+ auto rd_index = rd_index_.load(std::memory_order_relaxed);
+
+ PERFETTO_DCHECK(wr_index >= rd_index);
+ auto size = wr_index - rd_index;
+ if (PERFETTO_LIKELY(size < kCapacity / 2))
+ return At(wr_index);
+
+ // Slow-path: Enqueue the read task and handle overruns.
+ bool expected = false;
+ if (RingBuffer::read_task_queued_.compare_exchange_strong(expected, true)) {
+ Delegate* dg = Delegate::GetInstance();
+ if (dg->task_runner) {
+ dg->task_runner->PostTask([] {
+ // Meta-tracing might have been disabled in the meantime.
+ auto read_task = Delegate::GetInstance()->read_task;
+ if (read_task)
+ read_task();
+ RingBuffer::read_task_queued_ = false;
+ });
+ }
+ }
+
+ if (PERFETTO_LIKELY(size < kCapacity))
+ return At(wr_index);
+
+ has_overruns_.store(true, std::memory_order_release);
+ wr_index_.fetch_sub(1, std::memory_order_acq_rel);
+
+ // In the case of overflows, threads will race writing on the same memory
+ // location and TSan will rightly complain. This is fine though because nobody
+ // will read the bankruptcy record and it's designed to contain garbage.
+ PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(&bankruptcy_record_, sizeof(Record),
+ "nothing reads bankruptcy_record_")
+ return &bankruptcy_record_;
+}
+
+// static
+bool RingBuffer::IsOnValidTaskRunner() {
+ auto* task_runner = Delegate::GetInstance()->task_runner;
+ return task_runner && task_runner->RunsTasksOnCurrentThread();
+}
+
+} // namespace metatrace
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/paged_memory.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/paged_memory.h
+// gen_amalgamated begin header: include/perfetto/ext/base/container_annotations.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_CONTAINER_ANNOTATIONS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_CONTAINER_ANNOTATIONS_H_
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+// Windows ASAN doesn't currently support these annotations.
+#if defined(ADDRESS_SANITIZER) && !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
+ !defined(ADDRESS_SANITIZER_WITHOUT_INSTRUMENTATION)
+
+#include <sanitizer/common_interface_defs.h>
+
+#define ANNOTATE_NEW_BUFFER(buffer, capacity, new_size) \
+ if (buffer) { \
+ __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
+ (buffer) + (capacity), \
+ (buffer) + (new_size)); \
+ }
+#define ANNOTATE_DELETE_BUFFER(buffer, capacity, old_size) \
+ if (buffer) { \
+ __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
+ (buffer) + (old_size), \
+ (buffer) + (capacity)); \
+ }
+#define ANNOTATE_CHANGE_SIZE(buffer, capacity, old_size, new_size) \
+ if (buffer) { \
+ __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
+ (buffer) + (old_size), \
+ (buffer) + (new_size)); \
+ }
+#define ANNOTATE_CHANGE_CAPACITY(buffer, old_capacity, buffer_size, \
+ new_capacity) \
+ ANNOTATE_DELETE_BUFFER(buffer, old_capacity, buffer_size); \
+ ANNOTATE_NEW_BUFFER(buffer, new_capacity, buffer_size);
+// Annotations require buffers to begin on an 8-byte boundary.
+#else // defined(ADDRESS_SANITIZER)
+#define ANNOTATE_NEW_BUFFER(buffer, capacity, new_size)
+#define ANNOTATE_DELETE_BUFFER(buffer, capacity, old_size)
+#define ANNOTATE_CHANGE_SIZE(buffer, capacity, old_size, new_size)
+#define ANNOTATE_CHANGE_CAPACITY(buffer, old_capacity, buffer_size, \
+ new_capacity)
+#endif // defined(ADDRESS_SANITIZER)
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_CONTAINER_ANNOTATIONS_H_
+/*
+ * Copyright (C) 2017 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_PAGED_MEMORY_H_
+#define INCLUDE_PERFETTO_EXT_BASE_PAGED_MEMORY_H_
+
+#include <cstddef>
+#include <memory>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/container_annotations.h"
+
+// We need to track the committed size on windows and when ASAN is enabled.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) || defined(ADDRESS_SANITIZER)
+#define TRACK_COMMITTED_SIZE() 1
+#else
+#define TRACK_COMMITTED_SIZE() 0
+#endif
+
+namespace perfetto {
+namespace base {
+
+class PagedMemory {
+ public:
+ // Initializes an invalid PagedMemory pointing to nullptr.
+ PagedMemory();
+
+ ~PagedMemory();
+
+ PagedMemory(PagedMemory&& other) noexcept;
+ PagedMemory& operator=(PagedMemory&& other);
+
+ enum AllocationFlags {
+ // By default, Allocate() crashes if the underlying mmap fails (e.g., if out
+ // of virtual address space). When this flag is provided, an invalid
+ // PagedMemory pointing to nullptr is returned in this case instead.
+ kMayFail = 1 << 0,
+
+ // By default, Allocate() commits the allocated memory immediately. When
+ // this flag is provided, the memory virtual address space may only be
+ // reserved and the user should call EnsureCommitted() before writing to
+ // memory addresses.
+ kDontCommit = 1 << 1,
+ };
+
+ // Allocates |size| bytes using mmap(MAP_ANONYMOUS). The returned memory is
+ // guaranteed to be page-aligned and guaranteed to be zeroed.
+ // For |flags|, see the AllocationFlags enum above.
+ static PagedMemory Allocate(size_t size, int flags = 0);
+
+ // Hint to the OS that the memory range is not needed and can be discarded.
+ // The memory remains accessible and its contents may be retained, or they
+ // may be zeroed. This function may be a NOP on some platforms. Returns true
+ // if implemented.
+ bool AdviseDontNeed(void* p, size_t size);
+
+ // Ensures that at least the first |committed_size| bytes of the allocated
+ // memory region are committed. The implementation may commit memory in larger
+ // chunks above |committed_size|. Crashes if the memory couldn't be committed.
+#if TRACK_COMMITTED_SIZE()
+ void EnsureCommitted(size_t committed_size);
+#else // TRACK_COMMITTED_SIZE()
+ void EnsureCommitted(size_t /*committed_size*/) {}
+#endif // TRACK_COMMITTED_SIZE()
+
+ inline void* Get() const noexcept { return p_; }
+ inline bool IsValid() const noexcept { return !!p_; }
+ inline size_t size() const noexcept { return size_; }
+
+ private:
+ PagedMemory(char* p, size_t size);
+
+ PagedMemory(const PagedMemory&) = delete;
+ // Defaulted for implementation of move constructor + assignment.
+ PagedMemory& operator=(const PagedMemory&) = default;
+
+ char* p_ = nullptr;
+
+ // The size originally passed to Allocate(). The actual virtual memory
+ // reservation will be larger due to: (i) guard pages; (ii) rounding up to
+ // the system page size.
+ size_t size_ = 0;
+
+#if TRACK_COMMITTED_SIZE()
+ size_t committed_size_ = 0u;
+#endif // TRACK_COMMITTED_SIZE()
+};
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_PAGED_MEMORY_H_
+/*
+ * Copyright (C) 2017 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/paged_memory.h"
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#else // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <sys/mman.h>
+#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/container_annotations.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+
+namespace perfetto {
+namespace base {
+
+namespace {
+
+#if TRACK_COMMITTED_SIZE()
+constexpr size_t kCommitChunkSize = 4 * 1024 * 1024; // 4MB
+#endif
+
+size_t RoundUpToSysPageSize(size_t req_size) {
+ const size_t page_size = GetSysPageSize();
+ return (req_size + page_size - 1) & ~(page_size - 1);
+}
+
+size_t GuardSize() {
+ return GetSysPageSize();
+}
+
+} // namespace
+
+// static
+PagedMemory PagedMemory::Allocate(size_t req_size, int flags) {
+ size_t rounded_up_size = RoundUpToSysPageSize(req_size);
+ PERFETTO_CHECK(rounded_up_size >= req_size);
+ size_t outer_size = rounded_up_size + GuardSize() * 2;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ void* ptr = VirtualAlloc(nullptr, outer_size, MEM_RESERVE, PAGE_NOACCESS);
+ if (!ptr && (flags & kMayFail))
+ return PagedMemory();
+ PERFETTO_CHECK(ptr);
+ char* usable_region = reinterpret_cast<char*>(ptr) + GuardSize();
+#else // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ void* ptr = mmap(nullptr, outer_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (ptr == MAP_FAILED && (flags & kMayFail))
+ return PagedMemory();
+ PERFETTO_CHECK(ptr && ptr != MAP_FAILED);
+ char* usable_region = reinterpret_cast<char*>(ptr) + GuardSize();
+ int res = mprotect(ptr, GuardSize(), PROT_NONE);
+ res |= mprotect(usable_region + rounded_up_size, GuardSize(), PROT_NONE);
+ PERFETTO_CHECK(res == 0);
+#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+ auto memory = PagedMemory(usable_region, req_size);
+#if TRACK_COMMITTED_SIZE()
+ size_t initial_commit = req_size;
+ if (flags & kDontCommit)
+ initial_commit = std::min(initial_commit, kCommitChunkSize);
+ memory.EnsureCommitted(initial_commit);
+#endif // TRACK_COMMITTED_SIZE()
+ return memory;
+}
+
+PagedMemory::PagedMemory() {}
+
+// clang-format off
+PagedMemory::PagedMemory(char* p, size_t size) : p_(p), size_(size) {
+ ANNOTATE_NEW_BUFFER(p_, size_, committed_size_)
+}
+
+PagedMemory::PagedMemory(PagedMemory&& other) noexcept {
+ *this = other;
+ other.p_ = nullptr;
+}
+// clang-format on
+
+PagedMemory& PagedMemory::operator=(PagedMemory&& other) {
+ this->~PagedMemory();
+ new (this) PagedMemory(std::move(other));
+ return *this;
+}
+
+PagedMemory::~PagedMemory() {
+ if (!p_)
+ return;
+ PERFETTO_CHECK(size_);
+ char* start = p_ - GuardSize();
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ BOOL res = VirtualFree(start, 0, MEM_RELEASE);
+ PERFETTO_CHECK(res != 0);
+#else // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ const size_t outer_size = RoundUpToSysPageSize(size_) + GuardSize() * 2;
+ int res = munmap(start, outer_size);
+ PERFETTO_CHECK(res == 0);
+#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ ANNOTATE_DELETE_BUFFER(p_, size_, committed_size_)
+}
+
+bool PagedMemory::AdviseDontNeed(void* p, size_t size) {
+ PERFETTO_DCHECK(p_);
+ PERFETTO_DCHECK(p >= p_);
+ PERFETTO_DCHECK(static_cast<char*>(p) + size <= p_ + size_);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) || PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
+ // Discarding pages on Windows has more CPU cost than is justified for the
+ // possible memory savings.
+ return false;
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_QNX)
+ int res = posix_madvise(p, size, POSIX_MADV_DISCARD_NP);
+ PERFETTO_DCHECK(res == 0);
+ return true;
+#else // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) ||
+ // PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
+ // http://man7.org/linux/man-pages/man2/madvise.2.html
+ int res = madvise(p, size, MADV_DONTNEED);
+ PERFETTO_DCHECK(res == 0);
+ return true;
+#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) ||
+ // PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
+}
+
+#if TRACK_COMMITTED_SIZE()
+void PagedMemory::EnsureCommitted(size_t committed_size) {
+ PERFETTO_DCHECK(committed_size <= size_);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ if (committed_size_ >= committed_size)
+ return;
+ // Rounding up.
+ size_t delta = committed_size - committed_size_;
+ size_t num_additional_chunks =
+ (delta + kCommitChunkSize - 1) / kCommitChunkSize;
+ PERFETTO_DCHECK(num_additional_chunks * kCommitChunkSize >= delta);
+ // Don't commit more than the total size.
+ size_t commit_size = std::min(num_additional_chunks * kCommitChunkSize,
+ size_ - committed_size_);
+ void* res = VirtualAlloc(p_ + committed_size_, commit_size, MEM_COMMIT,
+ PAGE_READWRITE);
+ PERFETTO_CHECK(res);
+ ANNOTATE_CHANGE_SIZE(p_, size_, committed_size_,
+ committed_size_ + commit_size)
+ committed_size_ += commit_size;
+#else // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ // mmap commits automatically as needed, so we only track here for ASAN.
+ committed_size = std::max(committed_size_, committed_size);
+ ANNOTATE_CHANGE_SIZE(p_, size_, committed_size_, committed_size)
+ committed_size_ = committed_size;
+#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+}
+#endif // TRACK_COMMITTED_SIZE()
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/periodic_task.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/periodic_task.h
+// gen_amalgamated begin header: include/perfetto/ext/base/thread_checker.h
+/*
+ * Copyright (C) 2017 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_THREAD_CHECKER_H_
+#define INCLUDE_PERFETTO_EXT_BASE_THREAD_CHECKER_H_
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <pthread.h>
+#endif
+#include <atomic>
+
+// gen_amalgamated expanded: #include "perfetto/base/export.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+
+namespace perfetto {
+namespace base {
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+using ThreadID = unsigned long;
+#else
+using ThreadID = pthread_t;
+#endif
+
+class PERFETTO_EXPORT_COMPONENT ThreadChecker {
+ public:
+ ThreadChecker();
+ ~ThreadChecker();
+ ThreadChecker(const ThreadChecker&);
+ ThreadChecker& operator=(const ThreadChecker&);
+ bool CalledOnValidThread() const PERFETTO_WARN_UNUSED_RESULT;
+ void DetachFromThread();
+
+ private:
+ mutable std::atomic<ThreadID> thread_id_;
+};
+
+#if PERFETTO_DCHECK_IS_ON() && !PERFETTO_BUILDFLAG(PERFETTO_CHROMIUM_BUILD)
+// TODO(primiano) Use Chromium's thread checker in Chromium.
+#define PERFETTO_THREAD_CHECKER(name) base::ThreadChecker name;
+#define PERFETTO_DCHECK_THREAD(name) \
+ PERFETTO_DCHECK((name).CalledOnValidThread())
+#define PERFETTO_DETACH_FROM_THREAD(name) (name).DetachFromThread()
+#else
+#define PERFETTO_THREAD_CHECKER(name)
+#define PERFETTO_DCHECK_THREAD(name)
+#define PERFETTO_DETACH_FROM_THREAD(name)
+#endif // PERFETTO_DCHECK_IS_ON()
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_THREAD_CHECKER_H_
+// gen_amalgamated begin header: include/perfetto/ext/base/weak_ptr.h
+/*
+ * Copyright (C) 2017 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_WEAK_PTR_H_
+#define INCLUDE_PERFETTO_EXT_BASE_WEAK_PTR_H_
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"
+
+#include <memory>
+
+namespace perfetto {
+namespace base {
+
+// A simple WeakPtr for single-threaded cases.
+// Generally keep the WeakPtrFactory as last fields in classes: it makes the
+// WeakPtr(s) invalidate as first thing in the class dtor.
+// Usage:
+// class MyClass {
+// MyClass() : weak_factory_(this) {}
+// WeakPtr<MyClass> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
+//
+// private:
+// WeakPtrFactory<MyClass> weak_factory_;
+// }
+//
+// int main() {
+// std::unique_ptr<MyClass> foo(new MyClass);
+// auto wptr = foo.GetWeakPtr();
+// ASSERT_TRUE(wptr);
+// ASSERT_EQ(foo.get(), wptr->get());
+// foo.reset();
+// ASSERT_FALSE(wptr);
+// ASSERT_EQ(nullptr, wptr->get());
+// }
+
+template <typename T>
+class WeakPtrFactory; // Forward declaration, defined below.
+
+template <typename T>
+class WeakPtr {
+ public:
+ WeakPtr() {}
+ WeakPtr(const WeakPtr&) = default;
+ WeakPtr& operator=(const WeakPtr&) = default;
+ WeakPtr(WeakPtr&&) = default;
+ WeakPtr& operator=(WeakPtr&&) = default;
+
+ T* get() const {
+ PERFETTO_DCHECK_THREAD(thread_checker);
+ return handle_ ? *handle_.get() : nullptr;
+ }
+ T* operator->() const { return get(); }
+ T& operator*() const { return *get(); }
+
+ explicit operator bool() const { return !!get(); }
+
+ private:
+ friend class WeakPtrFactory<T>;
+ explicit WeakPtr(const std::shared_ptr<T*>& handle) : handle_(handle) {}
+
+ std::shared_ptr<T*> handle_;
+ PERFETTO_THREAD_CHECKER(thread_checker)
+};
+
+template <typename T>
+class WeakPtrFactory {
+ public:
+ explicit WeakPtrFactory(T* owner) : weak_ptr_(std::make_shared<T*>(owner)) {
+ PERFETTO_DCHECK_THREAD(thread_checker);
+ }
+
+ ~WeakPtrFactory() {
+ PERFETTO_DCHECK_THREAD(thread_checker);
+ *(weak_ptr_.handle_.get()) = nullptr;
+ }
+
+ // Can be safely called on any thread, since it simply copies |weak_ptr_|.
+ // Note that any accesses to the returned pointer need to be made on the
+ // thread that created/reset the factory.
+ WeakPtr<T> GetWeakPtr() const { return weak_ptr_; }
+
+ // Reset the factory to a new owner & thread. May only be called before any
+ // weak pointers were passed out. Future weak pointers will be valid on the
+ // calling thread.
+ void Reset(T* owner) {
+ // Reset thread checker to current thread.
+ PERFETTO_DETACH_FROM_THREAD(thread_checker);
+ PERFETTO_DCHECK_THREAD(thread_checker);
+
+ // We should not have passed out any weak pointers yet at this point.
+ PERFETTO_DCHECK(weak_ptr_.handle_.use_count() == 1);
+
+ weak_ptr_ = WeakPtr<T>(std::make_shared<T*>(owner));
+ }
+
+ private:
+ WeakPtrFactory(const WeakPtrFactory&) = delete;
+ WeakPtrFactory& operator=(const WeakPtrFactory&) = delete;
+
+ WeakPtr<T> weak_ptr_;
+ PERFETTO_THREAD_CHECKER(thread_checker)
+};
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_WEAK_PTR_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_PERIODIC_TASK_H_
+#define INCLUDE_PERFETTO_EXT_BASE_PERIODIC_TASK_H_
+
+#include <functional>
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/weak_ptr.h"
+
+namespace perfetto {
+namespace base {
+
+class TaskRunner;
+
+// A periodic task utility class. It wraps the logic necessary to do periodic
+// tasks using a TaskRunner, taking care of subtleties like ensuring that
+// outstanding tasks are cancelled after reset/dtor.
+// Tasks are aligned on wall time (unless they are |one_shot|). This is to
+// ensure that when using multiple periodic tasks, they happen at the same time,
+// minimizing context switches.
+// On Linux/Android it also supports suspend-aware mode (via timerfd). On other
+// operating systems it falls back to PostDelayedTask, which is not
+// suspend-aware.
+// TODO(primiano): this should probably become a periodic timer scheduler, so we
+// can use one FD for everything rather than one FD per task. For now we take
+// the hit of a FD-per-task to keep this low-risk.
+// TODO(primiano): consider renaming this class to TimerTask. When |one_shot|
+// is set, the "Periodic" part of the class name becomes a lie.
+class PeriodicTask {
+ public:
+ explicit PeriodicTask(base::TaskRunner*);
+ ~PeriodicTask(); // Calls Reset().
+
+ struct Args {
+ uint32_t period_ms = 0;
+ std::function<void()> task = nullptr;
+ bool start_first_task_immediately = false;
+ bool use_suspend_aware_timer = false;
+ bool one_shot = false;
+ };
+
+ void Start(Args);
+
+ // Safe to be called multiple times, even without calling Start():
+ void Reset();
+
+ // No copy or move. WeakPtr-wrapped pointers to |this| are posted on the
+ // task runner, this class is not easily movable.
+ PeriodicTask(const PeriodicTask&) = delete;
+ PeriodicTask& operator=(const PeriodicTask&) = delete;
+ PeriodicTask(PeriodicTask&&) = delete;
+ PeriodicTask& operator=(PeriodicTask&&) = delete;
+
+ base::PlatformHandle timer_fd_for_testing() { return *timer_fd_; }
+
+ private:
+ static void RunTaskAndPostNext(base::WeakPtr<PeriodicTask>,
+ uint32_t generation);
+ void PostNextTask();
+ void ResetTimerFd();
+
+ base::TaskRunner* const task_runner_;
+ Args args_;
+ uint32_t generation_ = 0;
+ base::ScopedPlatformHandle timer_fd_;
+
+ PERFETTO_THREAD_CHECKER(thread_checker_)
+ base::WeakPtrFactory<PeriodicTask> weak_ptr_factory_; // Keep last.
+};
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_PERIODIC_TASK_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/periodic_task.h"
+
+#include <limits>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+ (PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && __ANDROID_API__ >= 19)
+#include <sys/timerfd.h>
+#endif
+
+namespace perfetto {
+namespace base {
+
+namespace {
+
+uint32_t GetNextDelayMs(const TimeMillis& now_ms,
+ const PeriodicTask::Args& args) {
+ if (args.one_shot)
+ return args.period_ms;
+
+ return args.period_ms -
+ static_cast<uint32_t>(now_ms.count() % args.period_ms);
+}
+
+ScopedPlatformHandle CreateTimerFd(const PeriodicTask::Args& args) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+ (PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && __ANDROID_API__ >= 19)
+ ScopedPlatformHandle tfd(
+ timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK));
+ uint32_t phase_ms = GetNextDelayMs(GetBootTimeMs(), args);
+
+ struct itimerspec its {};
+ // The "1 +" is to make sure that we never pass a zero it_value in the
+ // unlikely case of phase_ms being 0. That would cause the timer to be
+ // considered disarmed by timerfd_settime.
+ its.it_value.tv_sec = static_cast<time_t>(phase_ms / 1000u);
+ its.it_value.tv_nsec = 1 + static_cast<long>((phase_ms % 1000u) * 1000000u);
+ if (args.one_shot) {
+ its.it_interval.tv_sec = 0;
+ its.it_interval.tv_nsec = 0;
+ } else {
+ const uint32_t period_ms = args.period_ms;
+ its.it_interval.tv_sec = static_cast<time_t>(period_ms / 1000u);
+ its.it_interval.tv_nsec = static_cast<long>((period_ms % 1000u) * 1000000u);
+ }
+ if (timerfd_settime(*tfd, 0, &its, nullptr) < 0)
+ return ScopedPlatformHandle();
+ return tfd;
+#else
+ ignore_result(args);
+ return ScopedPlatformHandle();
+#endif
+}
+
+} // namespace
+
+PeriodicTask::PeriodicTask(TaskRunner* task_runner)
+ : task_runner_(task_runner), weak_ptr_factory_(this) {}
+
+PeriodicTask::~PeriodicTask() {
+ Reset();
+}
+
+void PeriodicTask::Start(Args args) {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ Reset();
+ if (args.period_ms == 0 || !args.task) {
+ PERFETTO_DCHECK(args.period_ms > 0);
+ PERFETTO_DCHECK(args.task);
+ return;
+ }
+ args_ = std::move(args);
+ if (args_.use_suspend_aware_timer) {
+ timer_fd_ = CreateTimerFd(args_);
+ if (timer_fd_) {
+ auto weak_this = weak_ptr_factory_.GetWeakPtr();
+ task_runner_->AddFileDescriptorWatch(
+ *timer_fd_,
+ std::bind(PeriodicTask::RunTaskAndPostNext, weak_this, generation_));
+ } else {
+ PERFETTO_DPLOG("timerfd not supported, falling back on PostDelayedTask");
+ }
+ } // if (use_suspend_aware_timer).
+
+ if (!timer_fd_)
+ PostNextTask();
+
+ if (args_.start_first_task_immediately)
+ args_.task();
+}
+
+void PeriodicTask::PostNextTask() {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ PERFETTO_DCHECK(args_.period_ms > 0);
+ PERFETTO_DCHECK(!timer_fd_);
+ uint32_t delay_ms = GetNextDelayMs(GetWallTimeMs(), args_);
+ auto weak_this = weak_ptr_factory_.GetWeakPtr();
+ task_runner_->PostDelayedTask(
+ std::bind(PeriodicTask::RunTaskAndPostNext, weak_this, generation_),
+ delay_ms);
+}
+
+// static
+// This function can be called in two ways (both from the TaskRunner):
+// 1. When using a timerfd, this task is registered as a FD watch.
+// 2. When using PostDelayedTask, this is the task posted on the TaskRunner.
+void PeriodicTask::RunTaskAndPostNext(WeakPtr<PeriodicTask> thiz,
+ uint32_t generation) {
+ if (!thiz || !thiz->args_.task || generation != thiz->generation_)
+ return; // Destroyed or Reset() in the meanwhile.
+ PERFETTO_DCHECK_THREAD(thiz->thread_checker_);
+ if (thiz->timer_fd_) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ PERFETTO_FATAL("timerfd for periodic tasks unsupported on Windows");
+#else
+ // If we are using a timerfd there is no need to repeatedly call
+ // PostDelayedTask(). The kernel will wakeup the timer fd periodically. We
+ // just need to read() it.
+ uint64_t ignored = 0;
+ errno = 0;
+ auto rsize = Read(*thiz->timer_fd_, &ignored, sizeof(&ignored));
+ if (rsize != sizeof(uint64_t)) {
+ if (errno == EAGAIN)
+ return; // A spurious wakeup. Rare, but can happen, just ignore.
+ PERFETTO_PLOG("read(timerfd) failed, falling back on PostDelayedTask");
+ thiz->ResetTimerFd();
+ }
+#endif
+ }
+
+ // Create a copy of the task to deal with either:
+ // 1. one_shot causing a Reset().
+ // 2. task() invoking internally Reset().
+ // That would cause a reset of the args_.task itself, which would invalidate
+ // the task bind state while we are invoking it.
+ auto task = thiz->args_.task;
+
+ // The repetition of the if() is to deal with the ResetTimerFd() case above.
+ if (thiz->args_.one_shot) {
+ thiz->Reset();
+ } else if (!thiz->timer_fd_) {
+ thiz->PostNextTask();
+ }
+
+ task();
+}
+
+void PeriodicTask::Reset() {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ ++generation_;
+ args_ = Args();
+ PERFETTO_DCHECK(!args_.task);
+ ResetTimerFd();
+}
+
+void PeriodicTask::ResetTimerFd() {
+ if (!timer_fd_)
+ return;
+ task_runner_->RemoveFileDescriptorWatch(*timer_fd_);
+ timer_fd_.reset();
+}
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/pipe.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/pipe.h"
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+#include <fcntl.h> // For O_BINARY (Windows) and F_SETxx (UNIX)
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#include <namedpipeapi.h>
+#else
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace base {
+
+Pipe::Pipe() = default;
+Pipe::Pipe(Pipe&&) noexcept = default;
+Pipe& Pipe::operator=(Pipe&&) = default;
+
+Pipe Pipe::Create(Flags flags) {
+ PlatformHandle fds[2];
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ PERFETTO_CHECK(::CreatePipe(&fds[0], &fds[1], /*lpPipeAttributes=*/nullptr,
+ 0 /*default size*/));
+#else
+ PERFETTO_CHECK(pipe(fds) == 0);
+ PERFETTO_CHECK(fcntl(fds[0], F_SETFD, FD_CLOEXEC) == 0);
+ PERFETTO_CHECK(fcntl(fds[1], F_SETFD, FD_CLOEXEC) == 0);
+#endif
+ Pipe p;
+ p.rd.reset(fds[0]);
+ p.wr.reset(fds[1]);
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ if (flags == kBothNonBlock || flags == kRdNonBlock) {
+ int cur_flags = fcntl(*p.rd, F_GETFL, 0);
+ PERFETTO_CHECK(cur_flags >= 0);
+ PERFETTO_CHECK(fcntl(*p.rd, F_SETFL, cur_flags | O_NONBLOCK) == 0);
+ }
+
+ if (flags == kBothNonBlock || flags == kWrNonBlock) {
+ int cur_flags = fcntl(*p.wr, F_GETFL, 0);
+ PERFETTO_CHECK(cur_flags >= 0);
+ PERFETTO_CHECK(fcntl(*p.wr, F_SETFL, cur_flags | O_NONBLOCK) == 0);
+ }
+#else
+ PERFETTO_CHECK(flags == kBothBlock);
+#endif
+ return p;
+}
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/scoped_mmap.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/scoped_mmap.h
+/*
+ * Copyright (C) 2024 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_SCOPED_MMAP_H_
+#define INCLUDE_PERFETTO_EXT_BASE_SCOPED_MMAP_H_
+
+#include <cstddef>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#define PERFETTO_HAS_MMAP() 1
+#else
+#define PERFETTO_HAS_MMAP() 0
+#endif
+
+namespace perfetto::base {
+
+// RAII wrapper that holds ownership of an mmap()d area and of a file. Calls
+// unmap() and close() on destruction.
+class ScopedMmap {
+ public:
+ // Creates a memory mapping for the first `length` bytes of `file`.
+ static ScopedMmap FromHandle(base::ScopedPlatformHandle file, size_t length);
+
+ ScopedMmap() {}
+ ~ScopedMmap();
+ ScopedMmap(ScopedMmap&& other) noexcept;
+
+ ScopedMmap& operator=(ScopedMmap&& other) noexcept;
+
+ // Returns a pointer to the mapped memory area. Only valid if `IsValid()` is
+ // true.
+ void* data() const { return ptr_; }
+
+ // Returns true if this object contains a successfully mapped area.
+ bool IsValid() const { return ptr_ != nullptr; }
+
+ // Returns the length of the mapped area.
+ size_t length() const { return length_; }
+
+ // Unmaps the area and closes the file. Returns false if this held a mmap()d
+ // area and unmapping failed. In any case, after this method, `IsValid()` will
+ // return false.
+ bool reset() noexcept;
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ // Takes ownership of an mmap()d area that starts at `data`, `size` bytes
+ // long. `data` should not be MAP_FAILED.
+ static ScopedMmap InheritMmappedRange(void* data, size_t size);
+#endif
+
+ private:
+ ScopedMmap(const ScopedMmap&) = delete;
+ ScopedMmap& operator=(const ScopedMmap&) = delete;
+
+ size_t length_ = 0;
+ void* ptr_ = nullptr;
+ ScopedPlatformHandle file_;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ ScopedPlatformHandle map_;
+#endif
+};
+
+// Tries to open `fname` and maps its first `length` bytes in memory.
+ScopedMmap ReadMmapFilePart(const char* fname, size_t length);
+
+// Tries to open `fname` and maps the whole file into memory.
+ScopedMmap ReadMmapWholeFile(const char* fname);
+
+} // namespace perfetto::base
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_SCOPED_MMAP_H_
+/*
+ * Copyright (C) 2024 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/scoped_mmap.h"
+
+#include <utility>
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#include <sys/mman.h>
+#include <unistd.h>
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#endif
+
+namespace perfetto::base {
+namespace {
+
+ScopedPlatformHandle OpenFileForMmap(const char* fname) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ return OpenFile(fname, O_RDONLY);
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ // This does not use base::OpenFile to avoid getting an exclusive lock.
+ return ScopedPlatformHandle(CreateFileA(fname, GENERIC_READ, FILE_SHARE_READ,
+ nullptr, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, nullptr));
+#else
+ // mmap is not supported. Do not even open the file.
+ base::ignore_result(fname);
+ return ScopedPlatformHandle();
+#endif
+}
+
+} // namespace
+
+ScopedMmap::ScopedMmap(ScopedMmap&& other) noexcept {
+ *this = std::move(other);
+}
+
+ScopedMmap& ScopedMmap::operator=(ScopedMmap&& other) noexcept {
+ if (this == &other) {
+ return *this;
+ }
+ reset();
+ std::swap(ptr_, other.ptr_);
+ std::swap(length_, other.length_);
+ std::swap(file_, other.file_);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ std::swap(map_, other.map_);
+#endif
+ return *this;
+}
+
+ScopedMmap::~ScopedMmap() {
+ reset();
+}
+
+// static
+ScopedMmap ScopedMmap::FromHandle(base::ScopedPlatformHandle file,
+ size_t length) {
+ ScopedMmap ret;
+ if (!file) {
+ return ret;
+ }
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ void* ptr = mmap(nullptr, length, PROT_READ, MAP_PRIVATE, *file, 0);
+ if (ptr != MAP_FAILED) {
+ ret.ptr_ = ptr;
+ ret.length_ = length;
+ ret.file_ = std::move(file);
+ }
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ ScopedPlatformHandle map(
+ CreateFileMapping(*file, nullptr, PAGE_READONLY, 0, 0, nullptr));
+ if (!map) {
+ return ret;
+ }
+ void* ptr = MapViewOfFile(*map, FILE_MAP_READ, 0, 0, length);
+ if (ptr != nullptr) {
+ ret.ptr_ = ptr;
+ ret.length_ = length;
+ ret.file_ = std::move(file);
+ ret.map_ = std::move(map);
+ }
+#else
+ base::ignore_result(length);
+#endif
+ return ret;
+}
+
+bool ScopedMmap::reset() noexcept {
+ bool ret = true;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ if (ptr_ != nullptr) {
+ ret = munmap(ptr_, length_) == 0;
+ }
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ if (ptr_ != nullptr) {
+ ret = UnmapViewOfFile(ptr_);
+ }
+ map_.reset();
+#endif
+ ptr_ = nullptr;
+ length_ = 0;
+ file_.reset();
+ return ret;
+}
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+// static
+ScopedMmap ScopedMmap::InheritMmappedRange(void* data, size_t size) {
+ ScopedMmap ret;
+ ret.ptr_ = data;
+ ret.length_ = size;
+ return ret;
+}
+#endif
+
+ScopedMmap ReadMmapFilePart(const char* fname, size_t length) {
+ return ScopedMmap::FromHandle(OpenFileForMmap(fname), length);
+}
+
+ScopedMmap ReadMmapWholeFile(const char* fname) {
+ ScopedPlatformHandle file = OpenFileForMmap(fname);
+ if (!file) {
+ return ScopedMmap();
+ }
+ std::optional<uint64_t> file_size = GetFileSize(file.get());
+ if (!file_size.has_value()) {
+ return ScopedMmap();
+ }
+ size_t size = static_cast<size_t>(*file_size);
+ if (static_cast<uint64_t>(size) != *file_size) {
+ return ScopedMmap();
+ }
+ return ScopedMmap::FromHandle(std::move(file), size);
+}
+
+} // namespace perfetto::base
+// gen_amalgamated begin source: src/base/status.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/base/status.h"
+
+#include <algorithm>
+#include <cstdarg>
+#include <cstdio>
+#include <string>
+#include <utility>
+
+namespace perfetto::base {
+
+Status ErrStatus(const char* format, ...) {
+ std::string buf;
+ buf.resize(1024);
+ for (;;) {
+ va_list ap;
+ va_start(ap, format);
+ int N = vsnprintf(buf.data(), buf.size() - 1, format, ap);
+ va_end(ap);
+
+ if (N <= 0) {
+ buf = "[printf format error]";
+ break;
+ }
+
+ auto sN = static_cast<size_t>(N);
+ if (sN > buf.size() - 1) {
+ // Indicates that the string was truncated and sN is the "number of
+ // non-null bytes which would be needed to fit the result". This is the
+ // C99 standard behaviour in the case of truncation. In that case, resize
+ // the buffer to match the returned value (with + 1 for the null
+ // terminator) and try again.
+ buf.resize(sN + 1);
+ continue;
+ }
+ if (sN == buf.size() - 1) {
+ // Indicates that the string was likely truncated and sN is just the
+ // number of bytes written into the string. This is the behaviour of
+ // non-standard compilers (MSVC) etc. In that case, just double the
+ // storage and try again.
+ buf.resize(sN * 2);
+ continue;
+ }
+
+ // Otherwise, indicates the string was written successfully: we need to
+ // resize to match the number of non-null bytes and return.
+ buf.resize(sN);
+ break;
+ }
+ return Status(std::move(buf));
+}
+
+std::optional<std::string_view> Status::GetPayload(
+ std::string_view type_url) const {
+ if (ok()) {
+ return std::nullopt;
+ }
+ for (const auto& kv : payloads_) {
+ if (kv.type_url == type_url) {
+ return kv.payload;
+ }
+ }
+ return std::nullopt;
+}
+
+void Status::SetPayload(std::string_view type_url, std::string value) {
+ if (ok()) {
+ return;
+ }
+ for (auto& kv : payloads_) {
+ if (kv.type_url == type_url) {
+ kv.payload = value;
+ return;
+ }
+ }
+ payloads_.push_back(Payload{std::string(type_url), std::move(value)});
+}
+
+bool Status::ErasePayload(std::string_view type_url) {
+ if (ok()) {
+ return false;
+ }
+ auto it = std::remove_if(
+ payloads_.begin(), payloads_.end(),
+ [type_url](const Payload& p) { return p.type_url == type_url; });
+ bool erased = it != payloads_.end();
+ payloads_.erase(it, payloads_.end());
+ return erased;
+}
+
+} // namespace perfetto::base
+// gen_amalgamated begin source: src/base/string_splitter.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/string_splitter.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_STRING_SPLITTER_H_
+#define INCLUDE_PERFETTO_EXT_BASE_STRING_SPLITTER_H_
+
+#include <string>
+
+namespace perfetto {
+namespace base {
+
+// 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:
+ // 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,
+ };
+
+ // 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);
+
+ // 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);
+
+ // 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<size_t>(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_STRING_SPLITTER_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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_splitter.h"
+
+#include <utility>
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace base {
+
+StringSplitter::StringSplitter(std::string str,
+ char delimiter,
+ EmptyTokenMode empty_token_mode)
+ : str_(std::move(str)),
+ delimiter_(delimiter),
+ empty_token_mode_(empty_token_mode) {
+ // It's legal to access str[str.size()] in C++11 (it always returns \0),
+ // hence the +1 (which becomes just size() after the -1 in Initialize()).
+ Initialize(&str_[0], str_.size() + 1);
+}
+
+StringSplitter::StringSplitter(char* str,
+ size_t size,
+ char delimiter,
+ EmptyTokenMode empty_token_mode)
+ : delimiter_(delimiter), empty_token_mode_(empty_token_mode) {
+ Initialize(str, size);
+}
+
+StringSplitter::StringSplitter(StringSplitter* outer,
+ char delimiter,
+ EmptyTokenMode empty_token_mode)
+ : delimiter_(delimiter), empty_token_mode_(empty_token_mode) {
+ Initialize(outer->cur_token(), outer->cur_token_size() + 1);
+}
+
+void StringSplitter::Initialize(char* str, size_t size) {
+ PERFETTO_DCHECK(!size || str);
+ next_ = str;
+ end_ = str + size;
+ cur_ = nullptr;
+ cur_size_ = 0;
+ if (size)
+ next_[size - 1] = '\0';
+}
+
+bool StringSplitter::Next() {
+ for (; next_ < end_; next_++) {
+ if (*next_ == delimiter_ &&
+ empty_token_mode_ == EmptyTokenMode::DISALLOW_EMPTY_TOKENS) {
+ // If empty tokens are disallowed, find fist non-delimiter character.
+ continue;
+ }
+ cur_ = next_;
+ for (;; next_++) {
+ if (*next_ == delimiter_) {
+ cur_size_ = static_cast<size_t>(next_ - cur_);
+ *(next_++) = '\0';
+ break;
+ }
+ if (*next_ == '\0') {
+ cur_size_ = static_cast<size_t>(next_ - cur_);
+ next_ = end_;
+ break;
+ }
+ }
+ if (*cur_ || empty_token_mode_ == EmptyTokenMode::ALLOW_EMPTY_TOKENS)
+ return true;
+ break;
+ }
+ cur_ = nullptr;
+ cur_size_ = 0;
+ return false;
+}
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/string_utils.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+
+#include <locale.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <algorithm>
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#include <xlocale.h>
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#endif
+
+#include <cinttypes>
+
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace base {
+
+// Locale-independant as possible version of strtod.
+double StrToD(const char* nptr, char** endptr) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ static auto c_locale = newlocale(LC_ALL, "C", nullptr);
+ return strtod_l(nptr, endptr, c_locale);
+#else
+ return strtod(nptr, endptr);
+#endif
+}
+
+bool StartsWith(const std::string& str, const std::string& prefix) {
+ return str.compare(0, prefix.length(), prefix) == 0;
+}
+
+bool StartsWithAny(const std::string& str,
+ const std::vector<std::string>& prefixes) {
+ return std::any_of(
+ prefixes.begin(), prefixes.end(),
+ [&str](const std::string& prefix) { return StartsWith(str, prefix); });
+}
+
+bool EndsWith(const std::string& str, const std::string& suffix) {
+ if (suffix.size() > str.size())
+ return false;
+ return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
+}
+
+bool Contains(const std::string& haystack, const std::string& needle) {
+ return haystack.find(needle) != std::string::npos;
+}
+
+bool Contains(const std::string& haystack, const char needle) {
+ return haystack.find(needle) != std::string::npos;
+}
+
+size_t Find(const StringView& needle, const StringView& haystack) {
+ if (needle.empty())
+ return 0;
+ if (needle.size() > haystack.size())
+ return std::string::npos;
+ for (size_t i = 0; i < haystack.size() - (needle.size() - 1); ++i) {
+ if (strncmp(haystack.data() + i, needle.data(), needle.size()) == 0)
+ return i;
+ }
+ return std::string::npos;
+}
+
+bool CaseInsensitiveEqual(const std::string& first, const std::string& second) {
+ return first.size() == second.size() &&
+ std::equal(
+ first.begin(), first.end(), second.begin(),
+ [](char a, char b) { return Lowercase(a) == Lowercase(b); });
+}
+
+std::string Join(const std::vector<std::string>& parts,
+ const std::string& delim) {
+ std::string acc;
+ for (size_t i = 0; i < parts.size(); ++i) {
+ acc += parts[i];
+ if (i + 1 != parts.size()) {
+ acc += delim;
+ }
+ }
+ return acc;
+}
+
+std::vector<std::string> SplitString(const std::string& text,
+ const std::string& delimiter) {
+ PERFETTO_CHECK(!delimiter.empty());
+
+ std::vector<std::string> output;
+ size_t start = 0;
+ size_t next;
+ for (;;) {
+ next = std::min(text.find(delimiter, start), text.size());
+ if (next > start)
+ output.emplace_back(&text[start], next - start);
+ start = next + delimiter.size();
+ if (start >= text.size())
+ break;
+ }
+ return output;
+}
+
+std::string TrimWhitespace(const std::string& str) {
+ std::string whitespaces = "\t\n ";
+
+ size_t front_idx = str.find_first_not_of(whitespaces);
+ std::string front_trimmed =
+ front_idx == std::string::npos ? "" : str.substr(front_idx);
+
+ size_t end_idx = front_trimmed.find_last_not_of(whitespaces);
+ return end_idx == std::string::npos ? ""
+ : front_trimmed.substr(0, end_idx + 1);
+}
+
+std::string StripPrefix(const std::string& str, const std::string& prefix) {
+ return StartsWith(str, prefix) ? str.substr(prefix.size()) : str;
+}
+
+std::string StripSuffix(const std::string& str, const std::string& suffix) {
+ return EndsWith(str, suffix) ? str.substr(0, str.size() - suffix.size())
+ : str;
+}
+
+std::string ToUpper(const std::string& str) {
+ // Don't use toupper(), it depends on the locale.
+ std::string res(str);
+ auto end = res.end();
+ for (auto c = res.begin(); c != end; ++c)
+ *c = Uppercase(*c);
+ return res;
+}
+
+std::string ToLower(const std::string& str) {
+ // Don't use tolower(), it depends on the locale.
+ std::string res(str);
+ auto end = res.end();
+ for (auto c = res.begin(); c != end; ++c)
+ *c = Lowercase(*c);
+ return res;
+}
+
+std::string ToHex(const char* data, size_t size) {
+ std::string hex(2 * size + 1, 'x');
+ for (size_t i = 0; i < size; ++i) {
+ // snprintf prints 3 characters, the two hex digits and a null byte. As we
+ // write left to right, we keep overwriting the nullbytes, except for the
+ // last call to snprintf.
+ snprintf(&(hex[2 * i]), 3, "%02hhx", data[i]);
+ }
+ // Remove the trailing nullbyte produced by the last snprintf.
+ hex.resize(2 * size);
+ return hex;
+}
+
+std::string IntToHexString(uint32_t number) {
+ size_t max_size = 11; // Max uint32 is 0xFFFFFFFF + 1 for null byte.
+ std::string buf;
+ buf.resize(max_size);
+ size_t final_len = SprintfTrunc(&buf[0], max_size, "0x%02x", number);
+ buf.resize(static_cast<size_t>(final_len)); // Cuts off the final null byte.
+ return buf;
+}
+
+std::string Uint64ToHexString(uint64_t number) {
+ return "0x" + Uint64ToHexStringNoPrefix(number);
+}
+
+std::string Uint64ToHexStringNoPrefix(uint64_t number) {
+ size_t max_size = 17; // Max uint64 is FFFFFFFFFFFFFFFF + 1 for null byte.
+ std::string buf;
+ buf.resize(max_size);
+ size_t final_len = SprintfTrunc(&buf[0], max_size, "%" PRIx64 "", number);
+ buf.resize(static_cast<size_t>(final_len)); // Cuts off the final null byte.
+ return buf;
+}
+
+std::string StripChars(const std::string& str,
+ const std::string& chars,
+ char replacement) {
+ std::string res(str);
+ const char* start = res.c_str();
+ const char* remove = chars.c_str();
+ for (const char* c = strpbrk(start, remove); c; c = strpbrk(c + 1, remove))
+ res[static_cast<uintptr_t>(c - start)] = replacement;
+ return res;
+}
+
+std::string ReplaceAll(std::string str,
+ const std::string& to_replace,
+ const std::string& replacement) {
+ PERFETTO_CHECK(!to_replace.empty());
+ size_t pos = 0;
+ while ((pos = str.find(to_replace, pos)) != std::string::npos) {
+ str.replace(pos, to_replace.length(), replacement);
+ pos += replacement.length();
+ }
+ return str;
+}
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+bool WideToUTF8(const std::wstring& source, std::string& output) {
+ if (source.empty() ||
+ source.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
+ return false;
+ }
+ int size = ::WideCharToMultiByte(CP_UTF8, 0, &source[0],
+ static_cast<int>(source.size()), nullptr, 0,
+ nullptr, nullptr);
+ output.assign(static_cast<size_t>(size), '\0');
+ if (::WideCharToMultiByte(CP_UTF8, 0, &source[0],
+ static_cast<int>(source.size()), &output[0], size,
+ nullptr, nullptr) != size) {
+ return false;
+ }
+ return true;
+}
+#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+bool UTF8ToWide(const std::string& source, std::wstring& output) {
+ if (source.empty() ||
+ source.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
+ return false;
+ }
+ int size = ::MultiByteToWideChar(CP_UTF8, 0, &source[0],
+ static_cast<int>(source.size()), nullptr, 0);
+ output.assign(static_cast<size_t>(size), L'\0');
+ if (::MultiByteToWideChar(CP_UTF8, 0, &source[0],
+ static_cast<int>(source.size()), &output[0],
+ size) != size) {
+ return false;
+ }
+ return true;
+}
+#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+size_t SprintfTrunc(char* dst, size_t dst_size, const char* fmt, ...) {
+ if (PERFETTO_UNLIKELY(dst_size == 0))
+ return 0;
+
+ va_list args;
+ va_start(args, fmt);
+ int src_size = vsnprintf(dst, dst_size, fmt, args);
+ va_end(args);
+
+ if (PERFETTO_UNLIKELY(src_size <= 0)) {
+ dst[0] = '\0';
+ return 0;
+ }
+
+ size_t res;
+ if (PERFETTO_LIKELY(src_size < static_cast<int>(dst_size))) {
+ // Most common case.
+ res = static_cast<size_t>(src_size);
+ } else {
+ // Truncation case.
+ res = dst_size - 1;
+ }
+
+ PERFETTO_DCHECK(res < dst_size);
+ PERFETTO_DCHECK(dst[res] == '\0');
+ return res;
+}
+
+std::optional<LineWithOffset> FindLineWithOffset(base::StringView str,
+ uint32_t offset) {
+ static constexpr char kNewLine = '\n';
+ uint32_t line_offset = 0;
+ uint32_t line_count = 1;
+ for (uint32_t i = 0; i < str.size(); ++i) {
+ if (str.at(i) == kNewLine) {
+ line_offset = i + 1;
+ line_count++;
+ continue;
+ }
+ if (i == offset) {
+ size_t end_offset = str.find(kNewLine, i);
+ if (end_offset == std::string::npos) {
+ end_offset = str.size();
+ }
+ base::StringView line = str.substr(line_offset, end_offset - line_offset);
+ return LineWithOffset{line, offset - line_offset, line_count};
+ }
+ }
+ return std::nullopt;
+}
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/string_view.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
+
+namespace perfetto {
+namespace base {
+
+// Without ignoring this warning we get the message:
+// error: out-of-line definition of constexpr static data member is redundant
+// in C++17 and is deprecated
+// when using clang-cl in Windows.
+#if defined(__GNUC__) // GCC & clang
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated"
+#endif // __GNUC__
+
+// static
+constexpr size_t StringView::npos;
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/string_view_splitter.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/string_view_splitter.h
+/*
+ * Copyright (C) 2024 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_STRING_VIEW_SPLITTER_H_
+#define INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_SPLITTER_H_
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
+
+namespace perfetto {
+namespace base {
+
+// C++ version of strtok(). Splits a StringView without making copies or any
+// heap allocations. 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 StringViewSplitter {
+ public:
+ // 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,
+ };
+
+ // Can take ownership of the string if passed via std::move(), e.g.:
+ // StringViewSplitter(std::move(str), '\n');
+ StringViewSplitter(base::StringView,
+ char delimiter,
+ EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);
+
+ // Splits the current token from an outer StringViewSplitter instance. This is
+ // to chain splitters as follows: for (base::StringViewSplitter lines(x,
+ // '\n'); ss.Next();)
+ // for (base::StringViewSplitter words(&lines, ' '); words.Next();)
+ StringViewSplitter(StringViewSplitter*,
+ 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()), and the empty string if no more tokens are found.
+ base::StringView NextToken() { return Next() ? cur_token() : ""; }
+
+ // Returns the current token iff last call to Next() returned true.
+ // In all other cases (before the 1st call to Next() and after Next() returns
+ // false) returns the empty string.
+ base::StringView cur_token() { return cur_; }
+
+ // Returns the remainder of the current input string that has not yet been
+ // tokenized.
+ base::StringView remainder() { return next_; }
+
+ private:
+ StringViewSplitter(const StringViewSplitter&) = delete;
+ StringViewSplitter& operator=(const StringViewSplitter&) = delete;
+ void Initialize(base::StringView);
+
+ base::StringView str_;
+ base::StringView cur_;
+ base::StringView next_;
+ bool end_of_input_;
+ const char delimiter_;
+ const EmptyTokenMode empty_token_mode_;
+};
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_SPLITTER_H_
+/*
+ * Copyright (C) 2024 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/string_view_splitter.h"
+
+#include <utility>
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace base {
+
+StringViewSplitter::StringViewSplitter(base::StringView str,
+ char delimiter,
+ EmptyTokenMode empty_token_mode)
+ : str_(std::move(str)),
+ delimiter_(delimiter),
+ empty_token_mode_(empty_token_mode) {
+ Initialize(str);
+}
+
+StringViewSplitter::StringViewSplitter(StringViewSplitter* outer,
+ char delimiter,
+ EmptyTokenMode empty_token_mode)
+ : delimiter_(delimiter), empty_token_mode_(empty_token_mode) {
+ Initialize(outer->cur_token());
+}
+
+void StringViewSplitter::Initialize(base::StringView str) {
+ next_ = str;
+ cur_ = "";
+ end_of_input_ = false;
+}
+
+bool StringViewSplitter::Next() {
+ if (end_of_input_) {
+ cur_ = next_ = "";
+ return false;
+ }
+
+ size_t substr_start = 0;
+ if (empty_token_mode_ == EmptyTokenMode::DISALLOW_EMPTY_TOKENS) {
+ while (substr_start < next_.size() &&
+ next_.at(substr_start) == delimiter_) {
+ substr_start++;
+ }
+ }
+
+ if (substr_start >= next_.size()) {
+ end_of_input_ = true;
+ cur_ = next_ = "";
+ return !cur_.empty() ||
+ empty_token_mode_ == EmptyTokenMode::ALLOW_EMPTY_TOKENS;
+ }
+
+ size_t delimiter_start = next_.find(delimiter_, substr_start);
+ if (delimiter_start == base::StringView::npos) {
+ cur_ = next_.substr(substr_start);
+ next_ = "";
+ end_of_input_ = true;
+ return !cur_.empty() ||
+ empty_token_mode_ == EmptyTokenMode::ALLOW_EMPTY_TOKENS;
+ }
+
+ size_t delimiter_end = delimiter_start + 1;
+
+ if (empty_token_mode_ == EmptyTokenMode::DISALLOW_EMPTY_TOKENS) {
+ while (delimiter_end < next_.size() &&
+ next_.at(delimiter_end) == delimiter_) {
+ delimiter_end++;
+ }
+ if (delimiter_end >= next_.size()) {
+ end_of_input_ = true;
+ }
+ }
+
+ cur_ = next_.substr(substr_start, delimiter_start - substr_start);
+ next_ = next_.substr(delimiter_end);
+
+ return !cur_.empty() ||
+ empty_token_mode_ == EmptyTokenMode::ALLOW_EMPTY_TOKENS;
+}
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/temp_file.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/temp_file.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_TEMP_FILE_H_
+#define INCLUDE_PERFETTO_EXT_BASE_TEMP_FILE_H_
+
+#include <string>
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+
+namespace perfetto {
+namespace base {
+
+std::string GetSysTempDir();
+
+class TempFile {
+ public:
+ static TempFile CreateUnlinked();
+ static TempFile Create();
+
+ TempFile(TempFile&&) noexcept;
+ TempFile& operator=(TempFile&&);
+ ~TempFile();
+
+ const std::string& path() const { return path_; }
+ int fd() const { return *fd_; }
+ int operator*() const { return *fd_; }
+
+ // Unlinks the file from the filesystem but keeps the fd() open.
+ // It is safe to call this multiple times.
+ void Unlink();
+
+ // Releases the underlying file descriptor. Will unlink the file from the
+ // filesystem if it was created via CreateUnlinked().
+ ScopedFile ReleaseFD();
+
+ private:
+ TempFile();
+ TempFile(const TempFile&) = delete;
+ TempFile& operator=(const TempFile&) = delete;
+
+ ScopedFile fd_;
+ std::string path_;
+};
+
+class TempDir {
+ public:
+ static TempDir Create();
+
+ TempDir(TempDir&&) noexcept;
+ TempDir& operator=(TempDir&&);
+ ~TempDir();
+
+ const std::string& path() const { return path_; }
+
+ private:
+ TempDir();
+ TempDir(const TempDir&) = delete;
+ TempDir& operator=(const TempDir&) = delete;
+
+ std::string path_;
+};
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_TEMP_FILE_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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/temp_file.h"
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#include <direct.h>
+#include <fileapi.h>
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+
+namespace perfetto {
+namespace base {
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+namespace {
+std::string GetTempFilePathWin() {
+ std::string tmplt = GetSysTempDir() + "\\perfetto-XXXXXX";
+ StackString<255> name("%s\\perfetto-XXXXXX", GetSysTempDir().c_str());
+ PERFETTO_CHECK(_mktemp_s(name.mutable_data(), name.len() + 1) == 0);
+ return name.ToStdString();
+}
+} // namespace
+#endif
+
+std::string GetSysTempDir() {
+ const char* tmpdir = nullptr;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ if ((tmpdir = getenv("TMP")))
+ return tmpdir;
+ if ((tmpdir = getenv("TEMP")))
+ return tmpdir;
+ return "C:\\TEMP";
+#else
+ if ((tmpdir = getenv("TMPDIR")))
+ return base::StripSuffix(tmpdir, "/");
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ return "/data/local/tmp";
+#else
+ return "/tmp";
+#endif // !OS_ANDROID
+#endif // !OS_WIN
+}
+
+// static
+TempFile TempFile::Create() {
+ TempFile temp_file;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ temp_file.path_ = GetTempFilePathWin();
+ // Several tests want to read-back the temp file while still open. On Windows,
+ // that requires FILE_SHARE_READ. FILE_SHARE_READ is NOT settable when using
+ // the POSIX-compat equivalent function _open(). Hence the CreateFileA +
+ // _open_osfhandle dance here.
+ HANDLE h =
+ ::CreateFileA(temp_file.path_.c_str(), GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_DELETE | FILE_SHARE_READ, nullptr, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_TEMPORARY, nullptr);
+ PERFETTO_CHECK(PlatformHandleChecker::IsValid(h));
+ // According to MSDN, when using _open_osfhandle the caller must not call
+ // CloseHandle(). Ownership is moved to the file descriptor, which then needs
+ // to be closed with just with _close().
+ temp_file.fd_.reset(_open_osfhandle(reinterpret_cast<intptr_t>(h), 0));
+#else
+ temp_file.path_ = GetSysTempDir() + "/perfetto-XXXXXXXX";
+ temp_file.fd_.reset(mkstemp(&temp_file.path_[0]));
+#endif
+ if (PERFETTO_UNLIKELY(!temp_file.fd_)) {
+ PERFETTO_FATAL("Could not create temp file %s", temp_file.path_.c_str());
+ }
+ return temp_file;
+}
+
+// static
+TempFile TempFile::CreateUnlinked() {
+ TempFile temp_file = TempFile::Create();
+ temp_file.Unlink();
+ return temp_file;
+}
+
+TempFile::TempFile() = default;
+
+TempFile::~TempFile() {
+ Unlink();
+}
+
+ScopedFile TempFile::ReleaseFD() {
+ Unlink();
+ return std::move(fd_);
+}
+
+void TempFile::Unlink() {
+ if (path_.empty())
+ return;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ // If the FD is still open DeleteFile will mark the file as pending deletion
+ // and delete it only when the process exists.
+ PERFETTO_CHECK(DeleteFileA(path_.c_str()));
+#else
+ PERFETTO_CHECK(unlink(path_.c_str()) == 0);
+#endif
+ path_.clear();
+}
+
+TempFile::TempFile(TempFile&&) noexcept = default;
+TempFile& TempFile::operator=(TempFile&&) = default;
+
+// static
+TempDir TempDir::Create() {
+ TempDir temp_dir;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ temp_dir.path_ = GetTempFilePathWin();
+ PERFETTO_CHECK(_mkdir(temp_dir.path_.c_str()) == 0);
+#else
+ temp_dir.path_ = GetSysTempDir() + "/perfetto-XXXXXXXX";
+ PERFETTO_CHECK(mkdtemp(&temp_dir.path_[0]));
+#endif
+ return temp_dir;
+}
+
+TempDir::TempDir() = default;
+TempDir::TempDir(TempDir&&) noexcept = default;
+TempDir& TempDir::operator=(TempDir&&) = default;
+
+TempDir::~TempDir() {
+ if (path_.empty())
+ return; // For objects that get std::move()d.
+ PERFETTO_CHECK(Rmdir(path_));
+}
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/thread_checker.cc
+/*
+ * Copyright (C) 2017 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/thread_checker.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#endif
+
+namespace perfetto {
+namespace base {
+
+namespace {
+constexpr ThreadID kDetached{};
+
+ThreadID CurrentThreadId() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ return ::GetCurrentThreadId();
+#else
+ return pthread_self();
+#endif
+}
+} // namespace
+
+ThreadChecker::ThreadChecker() {
+ thread_id_.store(CurrentThreadId());
+}
+
+ThreadChecker::~ThreadChecker() = default;
+
+ThreadChecker::ThreadChecker(const ThreadChecker& other) {
+ thread_id_ = other.thread_id_.load();
+}
+
+ThreadChecker& ThreadChecker::operator=(const ThreadChecker& other) {
+ thread_id_ = other.thread_id_.load();
+ return *this;
+}
+
+bool ThreadChecker::CalledOnValidThread() const {
+ auto self = CurrentThreadId();
+
+ // Will re-attach if previously detached using DetachFromThread().
+ auto prev_value = kDetached;
+ if (thread_id_.compare_exchange_strong(prev_value, self))
+ return true;
+ return prev_value == self;
+}
+
+void ThreadChecker::DetachFromThread() {
+ thread_id_.store(kDetached);
+}
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/thread_utils.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/thread_utils.h
+/*
+ * 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.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
+
+#include <string>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+// gen_amalgamated expanded: #include "perfetto/base/export.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#include <pthread.h>
+#include <string.h>
+#include <algorithm>
+#endif
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/prctl.h>
+#endif
+
+// Internal implementation utils that aren't as widely useful/supported as
+// base/thread_utils.h.
+
+namespace perfetto {
+namespace base {
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+// Sets the "comm" of the calling thread to the first 15 chars of the given
+// string.
+inline bool MaybeSetThreadName(const std::string& name) {
+ char buf[16] = {};
+ StringCopy(buf, name.c_str(), sizeof(buf));
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ return pthread_setname_np(buf) == 0;
+#else
+ return pthread_setname_np(pthread_self(), buf) == 0;
+#endif
+}
+
+inline bool GetThreadName(std::string& out_result) {
+ char buf[16] = {};
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ if (prctl(PR_GET_NAME, buf) != 0)
+ return false;
+#else
+ if (pthread_getname_np(pthread_self(), buf, sizeof(buf)) != 0)
+ return false;
+#endif
+ out_result = std::string(buf);
+ return true;
+}
+
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+PERFETTO_EXPORT_COMPONENT bool MaybeSetThreadName(const std::string& name);
+PERFETTO_EXPORT_COMPONENT bool GetThreadName(std::string& out_result);
+
+#else
+inline bool MaybeSetThreadName(const std::string&) {
+ return false;
+}
+inline bool GetThreadName(std::string&) {
+ return false;
+}
+#endif
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
+/*
+ * Copyright (C) 2022 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/base/thread_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_utils.h"
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+#include <zircon/process.h>
+#include <zircon/syscalls.h>
+#include <zircon/types.h>
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#endif
+
+namespace perfetto {
+namespace base {
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+static PlatformThreadId ResolveThreadId() {
+ zx_info_handle_basic_t basic;
+ return (zx_object_get_info(zx_thread_self(), ZX_INFO_HANDLE_BASIC, &basic,
+ sizeof(basic), nullptr, nullptr) == ZX_OK)
+ ? basic.koid
+ : ZX_KOID_INVALID;
+}
+PlatformThreadId GetThreadId() {
+ thread_local static PlatformThreadId thread_id = ResolveThreadId();
+ return thread_id;
+}
+
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+// The SetThreadDescription API was brought in version 1607 of Windows 10.
+typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread,
+ PCWSTR lpThreadDescription);
+
+// The SetThreadDescription API was brought in version 1607 of Windows 10.
+typedef HRESULT(WINAPI* GetThreadDescription)(HANDLE hThread,
+ PWSTR* ppszThreadDescription);
+
+bool MaybeSetThreadName(const std::string& name) {
+ // The SetThreadDescription API works even if no debugger is attached.
+ static auto set_thread_description_func =
+ reinterpret_cast<SetThreadDescription>(
+ reinterpret_cast<void*>(::GetProcAddress(
+ ::GetModuleHandleA("Kernel32.dll"), "SetThreadDescription")));
+ if (!set_thread_description_func) {
+ return false;
+ }
+ std::wstring wide_thread_name;
+ if (!UTF8ToWide(name, wide_thread_name)) {
+ return false;
+ }
+ HRESULT result = set_thread_description_func(::GetCurrentThread(),
+ wide_thread_name.c_str());
+ return !FAILED(result);
+}
+
+bool GetThreadName(std::string& out_result) {
+ static auto get_thread_description_func =
+ reinterpret_cast<GetThreadDescription>(
+ reinterpret_cast<void*>(::GetProcAddress(
+ ::GetModuleHandleA("Kernel32.dll"), "GetThreadDescription")));
+ if (!get_thread_description_func) {
+ return false;
+ }
+ wchar_t* wide_thread_name;
+ HRESULT result =
+ get_thread_description_func(::GetCurrentThread(), &wide_thread_name);
+ if (SUCCEEDED(result)) {
+ bool success = WideToUTF8(std::wstring(wide_thread_name), out_result);
+ LocalFree(wide_thread_name);
+ return success;
+ }
+ return false;
+}
+
+#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/time.cc
+/*
+ * 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.
+ */
+
+#include <atomic>
+
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#else
+#include <unistd.h>
+#endif
+
+namespace perfetto {
+namespace base {
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#if !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+namespace {
+
+// Returns the current value of the performance counter.
+int64_t QPCNowRaw() {
+ LARGE_INTEGER perf_counter_now = {};
+ // According to the MSDN documentation for QueryPerformanceCounter(), this
+ // will never fail on systems that run XP or later.
+ // https://msdn.microsoft.com/library/windows/desktop/ms644904.aspx
+ ::QueryPerformanceCounter(&perf_counter_now);
+ return perf_counter_now.QuadPart;
+}
+
+double TSCTicksPerSecond() {
+ // The value returned by QueryPerformanceFrequency() cannot be used as the TSC
+ // frequency, because there is no guarantee that the TSC frequency is equal to
+ // the performance counter frequency.
+ // The TSC frequency is cached in a static variable because it takes some time
+ // to compute it.
+ static std::atomic<double> tsc_ticks_per_second = 0;
+ double value = tsc_ticks_per_second.load(std::memory_order_relaxed);
+ if (value != 0)
+ return value;
+
+ // Increase the thread priority to reduces the chances of having a context
+ // switch during a reading of the TSC and the performance counter.
+ const int previous_priority = ::GetThreadPriority(::GetCurrentThread());
+ ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+
+ // The first time that this function is called, make an initial reading of the
+ // TSC and the performance counter. Initialization of static variable is
+ // thread-safe. Threads can race initializing tsc_initial vs
+ // perf_counter_initial, although they should be storing very similar values.
+
+ static const uint64_t tsc_initial = __rdtsc();
+ static const int64_t perf_counter_initial = QPCNowRaw();
+
+ // Make a another reading of the TSC and the performance counter every time
+ // that this function is called.
+ const uint64_t tsc_now = __rdtsc();
+ const int64_t perf_counter_now = QPCNowRaw();
+
+ // Reset the thread priority.
+ ::SetThreadPriority(::GetCurrentThread(), previous_priority);
+
+ // Make sure that at least 50 ms elapsed between the 2 readings. The first
+ // time that this function is called, we don't expect this to be the case.
+ // Note: The longer the elapsed time between the 2 readings is, the more
+ // accurate the computed TSC frequency will be. The 50 ms value was
+ // chosen because local benchmarks show that it allows us to get a
+ // stddev of less than 1 tick/us between multiple runs.
+ // Note: According to the MSDN documentation for QueryPerformanceFrequency(),
+ // this will never fail on systems that run XP or later.
+ // https://msdn.microsoft.com/library/windows/desktop/ms644905.aspx
+ LARGE_INTEGER perf_counter_frequency = {};
+ ::QueryPerformanceFrequency(&perf_counter_frequency);
+ PERFETTO_CHECK(perf_counter_now >= perf_counter_initial);
+ const int64_t perf_counter_ticks = perf_counter_now - perf_counter_initial;
+ const double elapsed_time_seconds =
+ static_cast<double>(perf_counter_ticks) /
+ static_cast<double>(perf_counter_frequency.QuadPart);
+
+ constexpr double kMinimumEvaluationPeriodSeconds = 0.05;
+ if (elapsed_time_seconds < kMinimumEvaluationPeriodSeconds)
+ return 0;
+
+ // Compute the frequency of the TSC.
+ PERFETTO_CHECK(tsc_now >= tsc_initial);
+ const uint64_t tsc_ticks = tsc_now - tsc_initial;
+ // Racing with another thread to write |tsc_ticks_per_second| is benign
+ // because both threads will write a valid result.
+ tsc_ticks_per_second.store(
+ static_cast<double>(tsc_ticks) / elapsed_time_seconds,
+ std::memory_order_relaxed);
+
+ return tsc_ticks_per_second.load(std::memory_order_relaxed);
+}
+
+} // namespace
+#endif // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+
+TimeNanos GetWallTimeNs() {
+ LARGE_INTEGER freq;
+ ::QueryPerformanceFrequency(&freq);
+ LARGE_INTEGER counter;
+ ::QueryPerformanceCounter(&counter);
+ double elapsed_nanoseconds = (1e9 * static_cast<double>(counter.QuadPart)) /
+ static_cast<double>(freq.QuadPart);
+ return TimeNanos(static_cast<uint64_t>(elapsed_nanoseconds));
+}
+
+TimeNanos GetThreadCPUTimeNs() {
+#if PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+ // QueryThreadCycleTime versus TSCTicksPerSecond doesn't have much relation to
+ // actual elapsed time on Windows on Arm, because QueryThreadCycleTime is
+ // backed by the actual number of CPU cycles executed, rather than a
+ // constant-rate timer like Intel. To work around this, use GetThreadTimes
+ // (which isn't as accurate but is meaningful as a measure of elapsed
+ // per-thread time).
+ FILETIME dummy, kernel_ftime, user_ftime;
+ ::GetThreadTimes(GetCurrentThread(), &dummy, &dummy, &kernel_ftime,
+ &user_ftime);
+ uint64_t kernel_time =
+ kernel_ftime.dwHighDateTime * 0x100000000 + kernel_ftime.dwLowDateTime;
+ uint64_t user_time =
+ user_ftime.dwHighDateTime * 0x100000000 + user_ftime.dwLowDateTime;
+
+ return TimeNanos((kernel_time + user_time) * 100);
+#else // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+ // Get the number of TSC ticks used by the current thread.
+ ULONG64 thread_cycle_time = 0;
+ ::QueryThreadCycleTime(GetCurrentThread(), &thread_cycle_time);
+
+ // Get the frequency of the TSC.
+ const double tsc_ticks_per_second = TSCTicksPerSecond();
+ if (tsc_ticks_per_second == 0)
+ return TimeNanos();
+
+ // Return the CPU time of the current thread.
+ const double thread_time_seconds =
+ static_cast<double>(thread_cycle_time) / tsc_ticks_per_second;
+ constexpr int64_t kNanosecondsPerSecond = 1000 * 1000 * 1000;
+ return TimeNanos(
+ static_cast<int64_t>(thread_time_seconds * kNanosecondsPerSecond));
+#endif // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+}
+
+void SleepMicroseconds(unsigned interval_us) {
+ // The Windows Sleep function takes a millisecond count. Round up so that
+ // short sleeps don't turn into a busy wait. Note that the sleep granularity
+ // on Windows can dynamically vary from 1 ms to ~16 ms, so don't count on this
+ // being a short sleep.
+ ::Sleep(static_cast<DWORD>((interval_us + 999) / 1000));
+}
+
+void InitializeTime() {
+#if !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+ // Make an early first call to TSCTicksPerSecond() to start 50 ms elapsed time
+ // (see comment in TSCTicksPerSecond()).
+ TSCTicksPerSecond();
+#endif // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+}
+
+#else // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+void SleepMicroseconds(unsigned interval_us) {
+ ::usleep(static_cast<useconds_t>(interval_us));
+}
+
+void InitializeTime() {}
+
+#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+std::string GetTimeFmt(const std::string& fmt) {
+ time_t raw_time;
+ time(&raw_time);
+ struct tm local_tm;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ PERFETTO_CHECK(localtime_s(&local_tm, &raw_time) == 0);
+#else
+ tzset();
+ PERFETTO_CHECK(localtime_r(&raw_time, &local_tm) != nullptr);
+#endif
+ char buf[128];
+ PERFETTO_CHECK(strftime(buf, 80, fmt.c_str(), &local_tm) > 0);
+ return buf;
+}
+
+std::optional<int32_t> GetTimezoneOffsetMins() {
+ std::string tz = GetTimeFmt("%z");
+ if (tz.size() != 5 || (tz[0] != '+' && tz[0] != '-'))
+ return std::nullopt;
+ char sign = '\0';
+ int32_t hh = 0;
+ int32_t mm = 0;
+ if (sscanf(tz.c_str(), "%c%2d%2d", &sign, &hh, &mm) != 3)
+ return std::nullopt;
+ return (hh * 60 + mm) * (sign == '-' ? -1 : 1);
+}
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/utils.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+
+#include <string>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/pipe.h"
+// gen_amalgamated expanded: #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_LINUX_BUT_NOT_QNX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/prctl.h>
+
+#ifndef PR_GET_TAGGED_ADDR_CTRL
+#define PR_GET_TAGGED_ADDR_CTRL 56
+#endif
+
+#ifndef PR_TAGGED_ADDR_ENABLE
+#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
+#endif
+
+#ifndef PR_MTE_TCF_SYNC
+#define PR_MTE_TCF_SYNC (1UL << 1)
+#endif
+
+#endif // OS_LINUX | OS_ANDROID
+
+#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
+
+#ifdef M_PURGE_ALL
+#define PERFETTO_M_PURGE_ALL M_PURGE_ALL
+#else
+// Only available in in-tree builds and on newer SDKs.
+#define PERFETTO_M_PURGE_ALL -104
+#endif // M_PURGE
+
+namespace {
+extern "C" {
+using MalloptType = int (*)(int, int);
+}
+} // namespace
+#endif // OS_ANDROID
+
+namespace {
+
+#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
+
+// Preserve the %rbx register via %rdi to work around a clang bug
+// https://bugs.llvm.org/show_bug.cgi?id=17907 (%rbx in an output constraint
+// is not considered a clobbered register).
+#define PERFETTO_GETCPUID(a, b, c, d, a_inp, c_inp) \
+ asm("mov %%rbx, %%rdi\n" \
+ "cpuid\n" \
+ "xchg %%rdi, %%rbx\n" \
+ : "=a"(a), "=D"(b), "=c"(c), "=d"(d) \
+ : "a"(a_inp), "2"(c_inp))
+
+uint32_t GetXCR0EAX() {
+ uint32_t eax = 0, edx = 0;
+ asm("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0));
+ return eax;
+}
+
+// If we are building with -msse4 check that the CPU actually supports it.
+// This file must be kept in sync with gn/standalone/BUILD.gn.
+void PERFETTO_EXPORT_COMPONENT __attribute__((constructor))
+CheckCpuOptimizations() {
+ uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
+ PERFETTO_GETCPUID(eax, ebx, ecx, edx, 1, 0);
+
+ static constexpr uint64_t xcr0_xmm_mask = 0x2;
+ static constexpr uint64_t xcr0_ymm_mask = 0x4;
+ static constexpr uint64_t xcr0_avx_mask = xcr0_xmm_mask | xcr0_ymm_mask;
+
+ const bool have_popcnt = ecx & (1u << 23);
+ const bool have_sse4_2 = ecx & (1u << 20);
+ const bool have_avx =
+ // Does the OS save/restore XMM and YMM state?
+ (ecx & (1u << 27)) && // OS support XGETBV.
+ (ecx & (1u << 28)) && // AVX supported in hardware
+ ((GetXCR0EAX() & xcr0_avx_mask) == xcr0_avx_mask);
+
+ // Get level 7 features (eax = 7 and ecx= 0), to check for AVX2 support.
+ // (See Intel 64 and IA-32 Architectures Software Developer's Manual
+ // Volume 2A: Instruction Set Reference, A-M CPUID).
+ PERFETTO_GETCPUID(eax, ebx, ecx, edx, 7, 0);
+ const bool have_avx2 = have_avx && ((ebx >> 5) & 0x1);
+ const bool have_bmi = (ebx >> 3) & 0x1;
+ const bool have_bmi2 = (ebx >> 8) & 0x1;
+
+ if (!have_sse4_2 || !have_popcnt || !have_avx2 || !have_bmi || !have_bmi2) {
+ fprintf(
+ stderr,
+ "This executable requires a x86_64 cpu that supports SSE4.2, BMI2 and "
+ "AVX2.\n"
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ "On MacOS, this might be caused by running x86_64 binaries on arm64.\n"
+ "See https://github.com/google/perfetto/issues/294 for more.\n"
+#endif
+ "Rebuild with enable_perfetto_x64_cpu_opt=false.\n");
+ _exit(126);
+ }
+}
+#endif
+
+} // namespace
+
+namespace perfetto {
+namespace base {
+
+namespace internal {
+
+std::atomic<uint32_t> g_cached_page_size{0};
+
+uint32_t GetSysPageSizeSlowpath() {
+ uint32_t page_size = 0;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ const int page_size_int = getpagesize();
+ // If sysconf() fails for obscure reasons (e.g. SELinux denial) assume the
+ // page size is 4KB. This is to avoid regressing subtle SDK usages, as old
+ // versions of this code had a static constant baked in.
+ page_size = static_cast<uint32_t>(page_size_int > 0 ? page_size_int : 4096);
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ page_size = static_cast<uint32_t>(vm_page_size);
+#else
+ page_size = 4096;
+#endif
+
+ PERFETTO_CHECK(page_size > 0 && page_size % 4096 == 0);
+
+ // Races here are fine because any thread will write the same value.
+ g_cached_page_size.store(page_size, std::memory_order_relaxed);
+ return page_size;
+}
+
+} // namespace internal
+
+void MaybeReleaseAllocatorMemToOS() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ // mallopt() on Android requires SDK level 26. Many targets and embedders
+ // still depend on a lower SDK level. Given mallopt() is a quite simple API,
+ // use reflection to do this rather than bumping the SDK level for all
+ // embedders. This keeps the behavior of standalone builds aligned with
+ // in-tree builds.
+ static MalloptType mallopt_fn =
+ reinterpret_cast<MalloptType>(dlsym(RTLD_DEFAULT, "mallopt"));
+ if (!mallopt_fn)
+ return;
+ if (mallopt_fn(PERFETTO_M_PURGE_ALL, 0) == 0) {
+ mallopt_fn(PERFETTO_M_PURGE, 0);
+ }
+#endif
+}
+
+uid_t GetCurrentUserId() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ return geteuid();
+#else
+ // TODO(primiano): On Windows we could hash the current user SID and derive a
+ // numeric user id [1]. It is not clear whether we need that. Right now that
+ // would not bring any benefit. Returning 0 unil we can prove we need it.
+ // [1]:https://android-review.googlesource.com/c/platform/external/perfetto/+/1513879/25/src/base/utils.cc
+ return 0;
+#endif
+}
+
+void SetEnv(const std::string& key, const std::string& value) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ PERFETTO_CHECK(::_putenv_s(key.c_str(), value.c_str()) == 0);
+#else
+ PERFETTO_CHECK(::setenv(key.c_str(), value.c_str(), /*overwrite=*/true) == 0);
+#endif
+}
+
+void UnsetEnv(const std::string& key) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ PERFETTO_CHECK(::_putenv_s(key.c_str(), "") == 0);
+#else
+ PERFETTO_CHECK(::unsetenv(key.c_str()) == 0);
+#endif
+}
+
+void Daemonize(std::function<int()> parent_cb) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ Pipe pipe = Pipe::Create(Pipe::kBothBlock);
+ pid_t pid;
+ switch (pid = fork()) {
+ case -1:
+ PERFETTO_FATAL("fork");
+ case 0: {
+ PERFETTO_CHECK(setsid() != -1);
+ base::ignore_result(chdir("/"));
+ base::ScopedFile null = base::OpenFile("/dev/null", O_RDONLY);
+ PERFETTO_CHECK(null);
+ PERFETTO_CHECK(dup2(*null, STDIN_FILENO) != -1);
+ PERFETTO_CHECK(dup2(*null, STDOUT_FILENO) != -1);
+ PERFETTO_CHECK(dup2(*null, STDERR_FILENO) != -1);
+ // Do not accidentally close stdin/stdout/stderr.
+ if (*null <= 2)
+ null.release();
+ WriteAll(*pipe.wr, "1", 1);
+ break;
+ }
+ default: {
+ // Wait for the child process to have reached the setsid() call. This is
+ // to avoid that 'adb shell perfetto -D' destroys the terminal (hence
+ // sending a SIGHUP to the child) before the child has detached from the
+ // terminal (see b/238644870).
+
+ // This is to unblock the read() below (with EOF, which will fail the
+ // CHECK) in the unlikely case of the child crashing before WriteAll("1").
+ pipe.wr.reset();
+ char one = '\0';
+ PERFETTO_CHECK(Read(*pipe.rd, &one, sizeof(one)) == 1 && one == '1');
+ printf("%d\n", pid);
+ int err = parent_cb();
+ exit(err);
+ }
+ }
+#else
+ // Avoid -Wunreachable warnings.
+ if (reinterpret_cast<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
+}
+
+bool IsSyncMemoryTaggingEnabled() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ // Compute only once per lifetime of the process.
+ static bool cached_value = [] {
+ const int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ if (res < 0)
+ return false;
+ const uint32_t actl = static_cast<uint32_t>(res);
+ return (actl & PR_TAGGED_ADDR_ENABLE) && (actl & PR_MTE_TCF_SYNC);
+ }();
+ return cached_value;
+#else
+ return false;
+#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
+// gen_amalgamated begin source: src/base/uuid.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/uuid.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_UUID_H_
+#define INCLUDE_PERFETTO_EXT_BASE_UUID_H_
+
+#include <string.h>
+#include <array>
+#include <cstdint>
+#include <optional>
+#include <string>
+
+// gen_amalgamated expanded: #include "perfetto/base/export.h"
+
+namespace perfetto {
+namespace base {
+
+class PERFETTO_EXPORT_COMPONENT Uuid {
+ public:
+ explicit Uuid(const std::string& s);
+ explicit Uuid(int64_t lsb, int64_t msb);
+ Uuid();
+
+ std::array<uint8_t, 16>* data() { return &data_; }
+ const std::array<uint8_t, 16>* data() const { return &data_; }
+
+ bool operator==(const Uuid& other) const { return data_ == other.data_; }
+
+ bool operator!=(const Uuid& other) const { return !(*this == other); }
+
+ explicit operator bool() const { return *this != Uuid(); }
+
+ int64_t msb() const {
+ int64_t result;
+ memcpy(&result, data_.data() + 8, 8);
+ return result;
+ }
+
+ int64_t lsb() const {
+ int64_t result;
+ memcpy(&result, data_.data(), 8);
+ return result;
+ }
+
+ void set_lsb_msb(int64_t lsb, int64_t msb) {
+ set_lsb(lsb);
+ set_msb(msb);
+ }
+ void set_msb(int64_t msb) { memcpy(data_.data() + 8, &msb, 8); }
+ void set_lsb(int64_t lsb) { memcpy(data_.data(), &lsb, 8); }
+
+ std::string ToString() const;
+ std::string ToPrettyString() const;
+
+ private:
+ std::array<uint8_t, 16> data_{};
+};
+
+Uuid Uuidv4();
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_UUID_H_
+// gen_amalgamated begin header: include/perfetto/ext/base/no_destructor.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_NO_DESTRUCTOR_H_
+#define INCLUDE_PERFETTO_EXT_BASE_NO_DESTRUCTOR_H_
+
+#include <new>
+#include <utility>
+
+namespace perfetto {
+namespace base {
+
+// Wrapper that can hold an object of type T, without invoking the contained
+// object's destructor when being destroyed. Useful for creating statics while
+// avoiding static destructors.
+//
+// Stores the object inline, and therefore doesn't incur memory allocation and
+// pointer indirection overheads.
+//
+// Example of use:
+//
+// const std::string& GetStr() {
+// static base::NoDestructor<std::string> s("hello");
+// return s.ref();
+// }
+//
+template <typename T>
+class NoDestructor {
+ public:
+ // Forward arguments to T's constructor. Note that this doesn't cover
+ // construction from initializer lists.
+ template <typename... Args>
+ explicit NoDestructor(Args&&... args) {
+ new (storage_) T(std::forward<Args>(args)...);
+ }
+
+ NoDestructor(const NoDestructor&) = delete;
+ NoDestructor& operator=(const NoDestructor&) = delete;
+ NoDestructor(NoDestructor&&) = delete;
+ NoDestructor& operator=(NoDestructor&&) = delete;
+
+ ~NoDestructor() = default;
+
+ /* To avoid type-punned pointer strict aliasing warnings on GCC6 and below
+ * these need to be split over two lines. If they are collapsed onto one line.
+ * return reinterpret_cast<const T*>(storage_);
+ * The error fires.
+ */
+ const T& ref() const {
+ auto* const cast = reinterpret_cast<const T*>(storage_);
+ return *cast;
+ }
+ T& ref() {
+ auto* const cast = reinterpret_cast<T*>(storage_);
+ return *cast;
+ }
+
+ private:
+ alignas(T) char storage_[sizeof(T)];
+};
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_NO_DESTRUCTOR_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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/uuid.h"
+
+#include <mutex>
+#include <random>
+
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/no_destructor.h"
+
+namespace perfetto {
+namespace base {
+namespace {
+
+constexpr char kHexmap[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+} // namespace
+
+// A globally unique 128-bit number.
+// In the early days of perfetto we were (sorta) respecting rfc4122. Later we
+// started replacing the LSB of the UUID with the statsd subscription ID in
+// other parts of the codebase (see perfetto_cmd.cc) for the convenience of
+// trace lookups, so rfc4122 made no sense as it just reduced entropy.
+Uuid Uuidv4() {
+ // Mix different sources of entropy to reduce the chances of collisions.
+ // Only using boot time is not enough. Under the assumption that most traces
+ // are started around the same time at boot, within a 1s window, the birthday
+ // paradox gives a chance of 90% collisions with 70k traces over a 1e9 space
+ // (Number of ns in a 1s window).
+ // &kHexmap >> 14 is used to feed use indirectly ASLR as a source of entropy.
+ // We deliberately don't use /dev/urandom as that might block for
+ // unpredictable time if the system is idle.
+ // The UUID does NOT need to be cryptographically secure, but random enough
+ // to avoid collisions across a large number of devices.
+ static std::minstd_rand rng(
+ static_cast<uint32_t>(static_cast<uint64_t>(GetBootTimeNs().count()) ^
+ static_cast<uint64_t>(GetWallTimeNs().count()) ^
+ (reinterpret_cast<uintptr_t>(&kHexmap) >> 14)));
+ Uuid uuid;
+ auto& data = *uuid.data();
+
+ // std::random is not thread safe and users of this class might mistakenly
+ // assume Uuidv4() is thread_safe because from the outside looks like a
+ // local object.
+ static base::NoDestructor<std::mutex> rand_mutex;
+ std::unique_lock<std::mutex> rand_lock(rand_mutex.ref());
+
+ for (size_t i = 0; i < sizeof(data);) {
+ // Note: the 32-th bit of rng() is always 0 as minstd_rand operates modulo
+ // 2**31. Fill in blocks of 16b rather than 32b to not lose 1b of entropy.
+ const auto rnd_data = static_cast<uint16_t>(rng());
+ memcpy(&data[i], &rnd_data, sizeof(rnd_data));
+ i += sizeof(rnd_data);
+ }
+
+ return uuid;
+}
+
+Uuid::Uuid() {}
+
+Uuid::Uuid(const std::string& s) {
+ PERFETTO_CHECK(s.size() == data_.size());
+ memcpy(data_.data(), s.data(), s.size());
+}
+
+Uuid::Uuid(int64_t lsb, int64_t msb) {
+ set_lsb_msb(lsb, msb);
+}
+
+std::string Uuid::ToString() const {
+ return std::string(reinterpret_cast<const char*>(data_.data()), data_.size());
+}
+
+std::string Uuid::ToPrettyString() const {
+ std::string s(data_.size() * 2 + 4, '-');
+ // Format is 123e4567-e89b-12d3-a456-426655443322.
+ size_t j = 0;
+ for (size_t i = 0; i < data_.size(); ++i) {
+ if (i == 4 || i == 6 || i == 8 || i == 10)
+ j++;
+ s[2 * i + j] = kHexmap[(data_[data_.size() - i - 1] & 0xf0) >> 4];
+ s[2 * i + 1 + j] = kHexmap[(data_[data_.size() - i - 1] & 0x0f)];
+ }
+ return s;
+}
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/virtual_destructors.cc
+/*
+ * Copyright (C) 2017 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/base/task_runner.h"
+
+// This translation unit contains the definitions for the destructor of pure
+// virtual interfaces for the current build target. The alternative would be
+// introducing a one-liner .cc file for each pure virtual interface, which is
+// overkill. This is for compliance with -Wweak-vtables.
+
+namespace perfetto {
+namespace base {
+
+TaskRunner::~TaskRunner() = default;
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/waitable_event.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/waitable_event.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_WAITABLE_EVENT_H_
+#define INCLUDE_PERFETTO_EXT_BASE_WAITABLE_EVENT_H_
+
+// gen_amalgamated expanded: #include "perfetto/base/thread_annotations.h"
+
+#include <stdint.h>
+
+#include <condition_variable>
+#include <mutex>
+
+namespace perfetto {
+namespace base {
+
+// A waitable event for cross-thread synchronization.
+// All methods on this class can be called from any thread.
+class WaitableEvent {
+ public:
+ WaitableEvent();
+ ~WaitableEvent();
+ WaitableEvent(const WaitableEvent&) = delete;
+ WaitableEvent operator=(const WaitableEvent&) = delete;
+
+ // Synchronously block until the event is notified `notification` times.
+ void Wait(uint64_t notifications = 1);
+
+ // Signal the event, waking up blocked waiters.
+ void Notify();
+
+ private:
+ std::mutex mutex_;
+ std::condition_variable event_ PERFETTO_GUARDED_BY(mutex_);
+ uint64_t notifications_ PERFETTO_GUARDED_BY(mutex_) = 0;
+};
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_WAITABLE_EVENT_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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/waitable_event.h"
+
+namespace perfetto {
+namespace base {
+
+WaitableEvent::WaitableEvent() = default;
+WaitableEvent::~WaitableEvent() = default;
+
+void WaitableEvent::Wait(uint64_t notifications)
+ PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
+ // 'std::unique_lock' lock doesn't work well with thread annotations
+ // (see https://github.com/llvm/llvm-project/issues/63239),
+ // so we suppress thread safety static analysis for this method.
+ std::unique_lock<std::mutex> lock(mutex_);
+ return event_.wait(lock, [&]() PERFETTO_EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
+ return notifications_ >= notifications;
+ });
+}
+
+void WaitableEvent::Notify() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ ++notifications_;
+ event_.notify_all();
+}
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/watchdog_posix.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/watchdog.h
+// gen_amalgamated begin header: include/perfetto/ext/base/watchdog_noop.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_WATCHDOG_NOOP_H_
+#define INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_NOOP_H_
+
+#include <stdint.h>
+
+namespace perfetto {
+namespace base {
+
+enum class WatchdogCrashReason; // Defined in watchdog.h.
+
+class Watchdog {
+ public:
+ class Timer {
+ public:
+ // Define an empty dtor to avoid "unused variable" errors on the call site.
+ Timer() {}
+ Timer(const Timer&) {}
+ ~Timer() {}
+ };
+ static Watchdog* GetInstance() {
+ static Watchdog* watchdog = new Watchdog();
+ return watchdog;
+ }
+ Timer CreateFatalTimer(uint32_t /*ms*/, WatchdogCrashReason) {
+ return Timer();
+ }
+ void Start() {}
+ void SetMemoryLimit(uint64_t /*bytes*/, uint32_t /*window_ms*/) {}
+ void SetCpuLimit(uint32_t /*percentage*/, uint32_t /*window_ms*/) {}
+};
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_NOOP_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_WATCHDOG_H_
+#define INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_H_
+
+#include <functional>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+// The POSIX watchdog is only supported on Linux and Android in non-embedder
+// builds.
+#if PERFETTO_BUILDFLAG(PERFETTO_WATCHDOG)
+// gen_amalgamated expanded: #include "perfetto/ext/base/watchdog_posix.h"
+#else
+// gen_amalgamated expanded: #include "perfetto/ext/base/watchdog_noop.h"
+#endif
+
+namespace perfetto {
+namespace base {
+
+// Used only to add more details to crash reporting.
+enum class WatchdogCrashReason {
+ kUnspecified = 0,
+ kCpuGuardrail = 1,
+ kMemGuardrail = 2,
+ kTaskRunnerHung = 3,
+ kTraceDidntStop = 4,
+};
+
+// Make the limits more relaxed on desktop, where multi-GB traces are likely.
+// Multi-GB traces can take bursts of cpu time to write into disk at the end of
+// the trace.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+constexpr uint32_t kWatchdogDefaultCpuLimit = 75;
+constexpr uint32_t kWatchdogDefaultCpuWindow = 5 * 60 * 1000; // 5 minutes.
+#else
+constexpr uint32_t kWatchdogDefaultCpuLimit = 90;
+constexpr uint32_t kWatchdogDefaultCpuWindow = 10 * 60 * 1000; // 10 minutes.
+#endif
+
+// The default memory margin we give to our processes. This is used as as a
+// constant to put on top of the trace buffers.
+constexpr uint64_t kWatchdogDefaultMemorySlack = 32 * 1024 * 1024; // 32 MiB.
+constexpr uint32_t kWatchdogDefaultMemoryWindow = 30 * 1000; // 30 seconds.
+
+inline void RunTaskWithWatchdogGuard(const std::function<void()>& task) {
+ // The longest duration allowed for a single task within the TaskRunner.
+ // Exceeding this limit will trigger program termination.
+ constexpr int64_t kWatchdogMillis = 180000; // 180s
+
+ Watchdog::Timer handle = base::Watchdog::GetInstance()->CreateFatalTimer(
+ kWatchdogMillis, WatchdogCrashReason::kTaskRunnerHung);
+ task();
+
+ // Suppress unused variable warnings in the client library amalgamated build.
+ (void)kWatchdogDefaultCpuLimit;
+ (void)kWatchdogDefaultCpuWindow;
+ (void)kWatchdogDefaultMemorySlack;
+ (void)kWatchdogDefaultMemoryWindow;
+}
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/platform.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/watchdog.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_WATCHDOG)
+
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/timerfd.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <cinttypes>
+#include <fstream>
+#include <thread>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/thread_utils.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/crash_keys.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+
+namespace perfetto {
+namespace base {
+
+namespace {
+
+constexpr uint32_t kDefaultPollingInterval = 30 * 1000;
+
+base::CrashKey g_crash_key_reason("wdog_reason");
+
+bool IsMultipleOf(uint32_t number, uint32_t divisor) {
+ return number >= divisor && number % divisor == 0;
+}
+
+double MeanForArray(const uint64_t array[], size_t size) {
+ uint64_t total = 0;
+ for (size_t i = 0; i < size; i++) {
+ total += array[i];
+ }
+ return static_cast<double>(total / size);
+}
+
+} // namespace
+
+bool ReadProcStat(int fd, ProcStat* out) {
+ char c[512];
+ size_t c_pos = 0;
+ while (c_pos < sizeof(c) - 1) {
+ ssize_t rd = PERFETTO_EINTR(read(fd, c + c_pos, sizeof(c) - c_pos));
+ if (rd < 0) {
+ PERFETTO_ELOG("Failed to read stat file to enforce resource limits.");
+ return false;
+ }
+ if (rd == 0)
+ break;
+ c_pos += static_cast<size_t>(rd);
+ }
+ PERFETTO_CHECK(c_pos < sizeof(c));
+ c[c_pos] = '\0';
+
+ if (sscanf(c,
+ "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu "
+ "%lu %*d %*d %*d %*d %*d %*d %*u %*u %ld",
+ &out->utime, &out->stime, &out->rss_pages) != 3) {
+ PERFETTO_ELOG("Invalid stat format: %s", c);
+ return false;
+ }
+ return true;
+}
+
+Watchdog::Watchdog(uint32_t polling_interval_ms)
+ : polling_interval_ms_(polling_interval_ms) {}
+
+Watchdog::~Watchdog() {
+ if (!thread_.joinable()) {
+ PERFETTO_DCHECK(!enabled_);
+ return;
+ }
+ PERFETTO_DCHECK(enabled_);
+ enabled_ = false;
+
+ // Rearm the timer to 1ns from now. This will cause the watchdog thread to
+ // wakeup from the poll() and see |enabled_| == false.
+ // This code path is used only in tests. In production code the watchdog is
+ // a singleton and is never destroyed.
+ struct itimerspec ts {};
+ ts.it_value.tv_sec = 0;
+ ts.it_value.tv_nsec = 1;
+ timerfd_settime(*timer_fd_, /*flags=*/0, &ts, nullptr);
+
+ thread_.join();
+}
+
+Watchdog* Watchdog::GetInstance() {
+ static Watchdog* watchdog = new Watchdog(kDefaultPollingInterval);
+ return watchdog;
+}
+
+// Can be called from any thread.
+Watchdog::Timer Watchdog::CreateFatalTimer(uint32_t ms,
+ WatchdogCrashReason crash_reason) {
+ if (!enabled_.load(std::memory_order_relaxed))
+ return Watchdog::Timer(this, 0, crash_reason);
+
+ return Watchdog::Timer(this, ms, crash_reason);
+}
+
+// Can be called from any thread.
+void Watchdog::AddFatalTimer(TimerData timer) {
+ std::lock_guard<std::mutex> guard(mutex_);
+ timers_.emplace_back(std::move(timer));
+ RearmTimerFd_Locked();
+}
+
+// Can be called from any thread.
+void Watchdog::RemoveFatalTimer(TimerData timer) {
+ std::lock_guard<std::mutex> guard(mutex_);
+ for (auto it = timers_.begin(); it != timers_.end(); it++) {
+ if (*it == timer) {
+ timers_.erase(it);
+ break; // Remove only one. Doesn't matter which one.
+ }
+ }
+ RearmTimerFd_Locked();
+}
+
+void Watchdog::RearmTimerFd_Locked() {
+ if (!enabled_)
+ return;
+ auto it = std::min_element(timers_.begin(), timers_.end());
+
+ // We use one timerfd to handle all the oustanding |timers_|. Keep it armed
+ // to the task expiring soonest.
+ struct itimerspec ts {};
+ if (it != timers_.end()) {
+ ts.it_value = ToPosixTimespec(it->deadline);
+ }
+ // If |timers_| is empty (it == end()) |ts.it_value| will remain
+ // zero-initialized and that will disarm the timer in the call below.
+ int res = timerfd_settime(*timer_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
+ PERFETTO_DCHECK(res == 0);
+}
+
+void Watchdog::Start() {
+ std::lock_guard<std::mutex> guard(mutex_);
+ if (thread_.joinable()) {
+ PERFETTO_DCHECK(enabled_);
+ } else {
+ PERFETTO_DCHECK(!enabled_);
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ // Kick the thread to start running but only on Android or Linux.
+ timer_fd_.reset(
+ timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK));
+ if (!timer_fd_) {
+ PERFETTO_PLOG(
+ "timerfd_create failed, the Perfetto watchdog is not available");
+ return;
+ }
+ enabled_ = true;
+ RearmTimerFd_Locked(); // Deal with timers created before Start().
+ thread_ = std::thread(&Watchdog::ThreadMain, this);
+#endif
+ }
+}
+
+void Watchdog::SetMemoryLimit(uint64_t bytes, uint32_t window_ms) {
+ // Update the fields under the lock.
+ std::lock_guard<std::mutex> guard(mutex_);
+
+ PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) || bytes == 0);
+
+ size_t size = bytes == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
+ memory_window_bytes_.Reset(size);
+ memory_limit_bytes_ = bytes;
+}
+
+void Watchdog::SetCpuLimit(uint32_t percentage, uint32_t window_ms) {
+ std::lock_guard<std::mutex> guard(mutex_);
+
+ PERFETTO_CHECK(percentage <= 100);
+ PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) ||
+ percentage == 0);
+
+ size_t size = percentage == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
+ cpu_window_time_ticks_.Reset(size);
+ cpu_limit_percentage_ = percentage;
+}
+
+void Watchdog::ThreadMain() {
+ // Register crash keys explicitly to avoid running out of slots at crash time.
+ g_crash_key_reason.Register();
+
+ base::ScopedFile stat_fd(base::OpenFile("/proc/self/stat", O_RDONLY));
+ if (!stat_fd) {
+ PERFETTO_ELOG("Failed to open stat file to enforce resource limits.");
+ return;
+ }
+
+ PERFETTO_DCHECK(timer_fd_);
+
+ constexpr uint8_t kFdCount = 1;
+ struct pollfd fds[kFdCount]{};
+ fds[0].fd = *timer_fd_;
+ fds[0].events = POLLIN;
+
+ for (;;) {
+ // We use the poll() timeout to drive the periodic ticks for the cpu/memory
+ // checks. The only other case when the poll() unblocks is when we crash
+ // (or have to quit via enabled_ == false, but that happens only in tests).
+ platform::BeforeMaybeBlockingSyscall();
+ auto ret = poll(fds, kFdCount, static_cast<int>(polling_interval_ms_));
+ platform::AfterMaybeBlockingSyscall();
+ if (!enabled_)
+ return;
+ if (ret < 0) {
+ if (errno == ENOMEM || errno == EINTR) {
+ // Should happen extremely rarely.
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ continue;
+ }
+ PERFETTO_FATAL("watchdog poll() failed");
+ }
+
+ // If we get here either:
+ // 1. poll() timed out, in which case we should process cpu/mem guardrails.
+ // 2. A timer expired, in which case we shall crash.
+
+ uint64_t expired = 0; // Must be exactly 8 bytes.
+ auto res = PERFETTO_EINTR(read(*timer_fd_, &expired, sizeof(expired)));
+ PERFETTO_DCHECK((res < 0 && (errno == EAGAIN)) ||
+ (res == sizeof(expired) && expired > 0));
+ const auto now = GetWallTimeMs();
+
+ // Check if any of the timers expired.
+ int tid_to_kill = 0;
+ WatchdogCrashReason crash_reason{};
+ {
+ std::lock_guard<std::mutex> guard(mutex_);
+ for (const auto& timer : timers_) {
+ if (now >= timer.deadline) {
+ tid_to_kill = timer.thread_id;
+ crash_reason = timer.crash_reason;
+ break;
+ }
+ }
+ }
+
+ if (tid_to_kill)
+ SerializeLogsAndKillThread(tid_to_kill, crash_reason);
+
+ // Check CPU and memory guardrails (if enabled).
+ lseek(stat_fd.get(), 0, SEEK_SET);
+ ProcStat stat;
+ if (!ReadProcStat(stat_fd.get(), &stat))
+ continue;
+ uint64_t cpu_time = stat.utime + stat.stime;
+ uint64_t rss_bytes =
+ static_cast<uint64_t>(stat.rss_pages) * base::GetSysPageSize();
+
+ bool threshold_exceeded = false;
+ {
+ std::lock_guard<std::mutex> guard(mutex_);
+ if (CheckMemory_Locked(rss_bytes) && !IsSyncMemoryTaggingEnabled()) {
+ threshold_exceeded = true;
+ crash_reason = WatchdogCrashReason::kMemGuardrail;
+ } else if (CheckCpu_Locked(cpu_time)) {
+ threshold_exceeded = true;
+ crash_reason = WatchdogCrashReason::kCpuGuardrail;
+ }
+ }
+
+ if (threshold_exceeded)
+ SerializeLogsAndKillThread(getpid(), crash_reason);
+ }
+}
+
+void Watchdog::SerializeLogsAndKillThread(int tid,
+ WatchdogCrashReason crash_reason) {
+ g_crash_key_reason.Set(static_cast<int>(crash_reason));
+
+ // We are about to die. Serialize the logs into the crash buffer so the
+ // debuggerd crash handler picks them up and attaches to the bugreport.
+ // In the case of a PERFETTO_CHECK/PERFETTO_FATAL this is done in logging.h.
+ // But in the watchdog case, we don't hit that codepath and must do ourselves.
+ MaybeSerializeLastLogsForCrashReporting();
+
+ // Send a SIGABRT to the thread that armed the timer. This is to see the
+ // callstack of the thread that is stuck in a long task rather than the
+ // watchdog thread.
+ if (syscall(__NR_tgkill, getpid(), tid, SIGABRT) < 0) {
+ // At this point the process must die. If for any reason the tgkill doesn't
+ // work (e.g. the thread has disappeared), force a crash from here.
+ abort();
+ }
+
+ if (disable_kill_failsafe_for_testing_)
+ return;
+
+ // The tgkill() above will take some milliseconds to cause a crash, as it
+ // involves the kernel to queue the SIGABRT on the target thread (often the
+ // main thread, which is != watchdog thread) and do a scheduling round.
+ // If something goes wrong though (the target thread has signals masked or
+ // is stuck in an uninterruptible+wakekill syscall) force quit from this
+ // thread.
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+ abort();
+}
+
+bool Watchdog::CheckMemory_Locked(uint64_t rss_bytes) {
+ if (memory_limit_bytes_ == 0)
+ return false;
+
+ // Add the current stat value to the ring buffer and check that the mean
+ // remains under our threshold.
+ if (memory_window_bytes_.Push(rss_bytes)) {
+ if (memory_window_bytes_.Mean() >
+ static_cast<double>(memory_limit_bytes_)) {
+ PERFETTO_ELOG(
+ "Memory watchdog trigger. Memory window of %f bytes is above the "
+ "%" PRIu64 " bytes limit.",
+ memory_window_bytes_.Mean(), memory_limit_bytes_);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Watchdog::CheckCpu_Locked(uint64_t cpu_time) {
+ if (cpu_limit_percentage_ == 0)
+ return false;
+
+ // Add the cpu time to the ring buffer.
+ if (cpu_window_time_ticks_.Push(cpu_time)) {
+ // Compute the percentage over the whole window and check that it remains
+ // under the threshold.
+ uint64_t difference_ticks = cpu_window_time_ticks_.NewestWhenFull() -
+ cpu_window_time_ticks_.OldestWhenFull();
+ double window_interval_ticks =
+ (static_cast<double>(WindowTimeForRingBuffer(cpu_window_time_ticks_)) /
+ 1000.0) *
+ static_cast<double>(sysconf(_SC_CLK_TCK));
+ double percentage = static_cast<double>(difference_ticks) /
+ static_cast<double>(window_interval_ticks) * 100;
+ if (percentage > cpu_limit_percentage_) {
+ PERFETTO_ELOG("CPU watchdog trigger. %f%% CPU use is above the %" PRIu32
+ "%% CPU limit.",
+ percentage, cpu_limit_percentage_);
+ return true;
+ }
+ }
+ return false;
+}
+
+uint32_t Watchdog::WindowTimeForRingBuffer(const WindowedInterval& window) {
+ return static_cast<uint32_t>(window.size() - 1) * polling_interval_ms_;
+}
+
+bool Watchdog::WindowedInterval::Push(uint64_t sample) {
+ // Add the sample to the current position in the ring buffer.
+ buffer_[position_] = sample;
+
+ // Update the position with next one circularily.
+ position_ = (position_ + 1) % size_;
+
+ // Set the filled flag the first time we wrap.
+ filled_ = filled_ || position_ == 0;
+ return filled_;
+}
+
+double Watchdog::WindowedInterval::Mean() const {
+ return MeanForArray(buffer_.get(), size_);
+}
+
+void Watchdog::WindowedInterval::Clear() {
+ position_ = 0;
+ buffer_.reset(new uint64_t[size_]());
+}
+
+void Watchdog::WindowedInterval::Reset(size_t new_size) {
+ position_ = 0;
+ size_ = new_size;
+ buffer_.reset(new_size == 0 ? nullptr : new uint64_t[new_size]());
+}
+
+Watchdog::Timer::Timer(Watchdog* watchdog,
+ uint32_t ms,
+ WatchdogCrashReason crash_reason)
+ : watchdog_(watchdog) {
+ if (!ms)
+ return; // No-op timer created when the watchdog is disabled.
+ timer_data_.deadline = GetWallTimeMs() + std::chrono::milliseconds(ms);
+ timer_data_.thread_id = GetThreadId();
+ timer_data_.crash_reason = crash_reason;
+ PERFETTO_DCHECK(watchdog_);
+ watchdog_->AddFatalTimer(timer_data_);
+}
+
+Watchdog::Timer::~Timer() {
+ if (timer_data_.deadline.count())
+ watchdog_->RemoveFatalTimer(timer_data_);
+}
+
+Watchdog::Timer::Timer(Timer&& other) noexcept {
+ watchdog_ = std::move(other.watchdog_);
+ other.watchdog_ = nullptr;
+ timer_data_ = std::move(other.timer_data_);
+ other.timer_data_ = TimerData();
+}
+
+} // namespace base
+} // namespace perfetto
+
+#endif // PERFETTO_BUILDFLAG(PERFETTO_WATCHDOG)
+// gen_amalgamated begin source: src/base/weak_runner.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/weak_runner.h
+/*
+ * Copyright (C) 2024 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_WEAK_RUNNER_H_
+#define INCLUDE_PERFETTO_EXT_BASE_WEAK_RUNNER_H_
+
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+
+namespace perfetto::base {
+
+class TaskRunner;
+
+// This is a wrapper around a `base::TaskRunner*`. It is intended to be used by
+// classes that want to post tasks on themselves. When the object is destroyed,
+// all posted tasks become noops.
+//
+// A class that embeds a WeakRunner can safely capture `this` on the posted
+// tasks.
+class WeakRunner {
+ public:
+ explicit WeakRunner(base::TaskRunner* task_runner);
+ ~WeakRunner();
+ base::TaskRunner* task_runner() const { return task_runner_; }
+
+ // Schedules `f` for immediate execution. `f` will not be executed is `*this`
+ // is destroyed.
+ //
+ // Can be called from any thread, but the caller needs to make sure that
+ // `*this` is alive while `PostTask` is running: this is not obvious when
+ // multiple threads are involved.
+ void PostTask(std::function<void()> f) const;
+
+ // Schedules `f` for execution after |delay_ms|.
+ // Can be called from any thread, but the caller needs to make sure that
+ // `*this` is alive while `PostDelayedTask` is running: this is not obvious
+ // when multiple threads are involved.
+ void PostDelayedTask(std::function<void()> f, uint32_t delay_ms) const;
+
+ private:
+ base::TaskRunner* const task_runner_;
+ std::shared_ptr<bool> destroyed_;
+};
+
+} // namespace perfetto::base
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_WEAK_RUNNER_H_
+/*
+ * Copyright (C) 2024 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/weak_runner.h"
+
+// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
+
+namespace perfetto::base {
+
+WeakRunner::WeakRunner(base::TaskRunner* task_runner)
+ : task_runner_(task_runner), destroyed_(std::make_shared<bool>(false)) {}
+
+WeakRunner::~WeakRunner() {
+ *destroyed_ = true;
+}
+
+void WeakRunner::PostTask(std::function<void()> f) const {
+ task_runner_->PostTask([destroyed = destroyed_, f = std::move(f)]() {
+ if (*destroyed) {
+ return;
+ }
+ f();
+ });
+}
+
+void WeakRunner::PostDelayedTask(std::function<void()> f,
+ uint32_t delay_ms) const {
+ task_runner_->PostDelayedTask(
+ [destroyed = destroyed_, f = std::move(f)]() {
+ if (*destroyed) {
+ return;
+ }
+ f();
+ },
+ delay_ms);
+}
+
+} // namespace perfetto::base
+// gen_amalgamated begin source: src/base/thread_task_runner.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/thread_task_runner.h
+// gen_amalgamated begin header: include/perfetto/ext/base/unix_task_runner.h
+/*
+ * Copyright (C) 2017 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_UNIX_TASK_RUNNER_H_
+#define INCLUDE_PERFETTO_EXT_BASE_UNIX_TASK_RUNNER_H_
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/base/thread_annotations.h"
+// gen_amalgamated expanded: #include "perfetto/base/thread_utils.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/event_fd.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"
+
+#include <chrono>
+#include <deque>
+#include <map>
+#include <mutex>
+#include <vector>
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <poll.h>
+#endif
+
+namespace perfetto {
+namespace base {
+
+// Runs a task runner on the current thread.
+//
+// Implementation note: we currently assume (and enforce in debug builds) that
+// Run() is called from the thread that constructed the UnixTaskRunner. This is
+// not strictly necessary, and we could instead track the thread that invokes
+// Run(). However, a related property that *might* be important to enforce is
+// that the destructor runs on the task-running thread. Otherwise, if there are
+// still-pending tasks at the time of destruction, we would destroy those
+// outside of the task thread (which might be unexpected to the caller). On the
+// other hand, the std::function task interface discourages use of any
+// resource-owning tasks (as the callable needs to be copyable), so this might
+// not be important in practice.
+//
+// TODO(rsavitski): consider adding a thread-check in the destructor, after
+// auditing existing usages.
+// TODO(primiano): rename this to TaskRunnerImpl. The "Unix" part is misleading
+// now as it supports also Windows.
+class UnixTaskRunner : public TaskRunner {
+ public:
+ UnixTaskRunner();
+ ~UnixTaskRunner() override;
+
+ // Start executing tasks. Doesn't return until Quit() is called. Run() may be
+ // called multiple times on the same task runner.
+ void Run();
+ void Quit();
+
+ // Checks whether there are any pending immediate tasks to run. Note that
+ // delayed tasks don't count even if they are due to run.
+ bool IsIdleForTesting();
+
+ // Pretends (for the purposes of running delayed tasks) that time advanced by
+ // `ms`.
+ void AdvanceTimeForTesting(uint32_t ms);
+
+ // TaskRunner implementation:
+ void PostTask(std::function<void()>) override;
+ void PostDelayedTask(std::function<void()>, uint32_t delay_ms) override;
+ void AddFileDescriptorWatch(PlatformHandle, std::function<void()>) override;
+ void RemoveFileDescriptorWatch(PlatformHandle) override;
+ bool RunsTasksOnCurrentThread() const override;
+
+ // Returns true if the task runner is quitting, or has quit and hasn't been
+ // restarted since. Exposed primarily for ThreadTaskRunner, not necessary for
+ // normal use of this class.
+ bool QuitCalled();
+
+ private:
+ void WakeUp();
+ void UpdateWatchTasksLocked() PERFETTO_EXCLUSIVE_LOCKS_REQUIRED(lock_);
+ int GetDelayMsToNextTaskLocked() const
+ PERFETTO_EXCLUSIVE_LOCKS_REQUIRED(lock_);
+ void RunImmediateAndDelayedTask();
+ void PostFileDescriptorWatches(uint64_t windows_wait_result);
+ void RunFileDescriptorWatch(PlatformHandle);
+
+ ThreadChecker thread_checker_;
+ std::atomic<PlatformThreadId> created_thread_id_ = GetThreadId();
+
+ EventFd event_;
+
+// The array of fds/handles passed to poll(2) / WaitForMultipleObjects().
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ std::vector<PlatformHandle> poll_fds_;
+#else
+ std::vector<struct pollfd> poll_fds_;
+#endif
+
+ std::mutex lock_;
+
+ std::deque<std::function<void()>> immediate_tasks_ PERFETTO_GUARDED_BY(lock_);
+ std::multimap<TimeMillis, std::function<void()>> delayed_tasks_
+ PERFETTO_GUARDED_BY(lock_);
+ bool quit_ PERFETTO_GUARDED_BY(lock_) = false;
+ TimeMillis advanced_time_for_testing_ PERFETTO_GUARDED_BY(lock_) =
+ TimeMillis(0);
+
+ struct WatchTask {
+ std::function<void()> callback;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ // On UNIX systems we make the FD number negative in |poll_fds_| to avoid
+ // polling it again until the queued task runs. On Windows we can't do that.
+ // Instead we keep track of its state here.
+ bool pending = false;
+#else
+ size_t poll_fd_index; // Index into |poll_fds_|.
+#endif
+ };
+
+ std::map<PlatformHandle, WatchTask> watch_tasks_ PERFETTO_GUARDED_BY(lock_);
+ bool watch_tasks_changed_ PERFETTO_GUARDED_BY(lock_) = false;
+};
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_UNIX_TASK_RUNNER_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_THREAD_TASK_RUNNER_H_
+#define INCLUDE_PERFETTO_EXT_BASE_THREAD_TASK_RUNNER_H_
+
+#include <functional>
+#include <thread>
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/unix_task_runner.h"
+
+namespace perfetto {
+namespace base {
+
+// A UnixTaskRunner backed by a dedicated task thread. Shuts down the runner and
+// joins the thread upon destruction. Can be moved to transfer ownership.
+//
+// Guarantees that:
+// * the UnixTaskRunner will be constructed and destructed on the task thread.
+// * the task thread will live for the lifetime of the UnixTaskRunner.
+//
+class PERFETTO_EXPORT_COMPONENT ThreadTaskRunner : public TaskRunner {
+ public:
+ static ThreadTaskRunner CreateAndStart(const std::string& name = "") {
+ return ThreadTaskRunner(name);
+ }
+
+ ThreadTaskRunner(const ThreadTaskRunner&) = delete;
+ ThreadTaskRunner& operator=(const ThreadTaskRunner&) = delete;
+
+ ThreadTaskRunner(ThreadTaskRunner&&) noexcept;
+ ThreadTaskRunner& operator=(ThreadTaskRunner&&);
+ ~ThreadTaskRunner() override;
+
+ // Executes the given function on the task runner thread and blocks the caller
+ // thread until the function has run.
+ void PostTaskAndWaitForTesting(std::function<void()>);
+
+ // Can be called from another thread to get the CPU time of the thread the
+ // task-runner is executing on.
+ uint64_t GetThreadCPUTimeNsForTesting();
+
+ // Returns a pointer to the UnixTaskRunner, which is valid for the lifetime of
+ // this ThreadTaskRunner object (unless this object is moved-from, in which
+ // case the pointer remains valid for the lifetime of the new owning
+ // ThreadTaskRunner).
+ //
+ // Warning: do not call Quit() on the returned runner pointer, the termination
+ // should be handled exclusively by this class' destructor.
+ UnixTaskRunner* get() const { return task_runner_; }
+
+ // TaskRunner implementation.
+ // These methods just proxy to the underlying task_runner_.
+ void PostTask(std::function<void()>) override;
+ void PostDelayedTask(std::function<void()>, uint32_t delay_ms) override;
+ void AddFileDescriptorWatch(PlatformHandle, std::function<void()>) override;
+ void RemoveFileDescriptorWatch(PlatformHandle) override;
+ bool RunsTasksOnCurrentThread() const override;
+
+ private:
+ explicit ThreadTaskRunner(const std::string& name);
+ void RunTaskThread(std::function<void(UnixTaskRunner*)> initializer);
+
+ std::thread thread_;
+ std::string name_;
+ UnixTaskRunner* task_runner_ = nullptr;
+};
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_THREAD_TASK_RUNNER_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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_task_runner.h"
+
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <thread>
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/unix_task_runner.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/prctl.h>
+#endif
+
+namespace perfetto {
+namespace base {
+
+ThreadTaskRunner::ThreadTaskRunner(ThreadTaskRunner&& other) noexcept
+ : thread_(std::move(other.thread_)), task_runner_(other.task_runner_) {
+ other.task_runner_ = nullptr;
+}
+
+ThreadTaskRunner& ThreadTaskRunner::operator=(ThreadTaskRunner&& other) {
+ this->~ThreadTaskRunner();
+ new (this) ThreadTaskRunner(std::move(other));
+ return *this;
+}
+
+ThreadTaskRunner::~ThreadTaskRunner() {
+ if (task_runner_) {
+ PERFETTO_CHECK(!task_runner_->QuitCalled());
+ task_runner_->Quit();
+
+ PERFETTO_DCHECK(thread_.joinable());
+ }
+ if (thread_.joinable())
+ thread_.join();
+}
+
+ThreadTaskRunner::ThreadTaskRunner(const std::string& name) : name_(name) {
+ std::mutex init_lock;
+ std::condition_variable init_cv;
+
+ std::function<void(UnixTaskRunner*)> initializer =
+ [this, &init_lock, &init_cv](UnixTaskRunner* task_runner) {
+ std::lock_guard<std::mutex> lock(init_lock);
+ task_runner_ = task_runner;
+ // Notify while still holding the lock, as init_cv ceases to exist as
+ // soon as the main thread observes a non-null task_runner_, and it can
+ // wake up spuriously (i.e. before the notify if we had unlocked before
+ // notifying).
+ init_cv.notify_one();
+ };
+
+ thread_ = std::thread(&ThreadTaskRunner::RunTaskThread, this,
+ std::move(initializer));
+
+ std::unique_lock<std::mutex> lock(init_lock);
+ init_cv.wait(lock, [this] { return !!task_runner_; });
+}
+
+void ThreadTaskRunner::RunTaskThread(
+ std::function<void(UnixTaskRunner*)> initializer) {
+ if (!name_.empty()) {
+ base::MaybeSetThreadName(name_);
+ }
+
+ UnixTaskRunner task_runner;
+ task_runner.PostTask(std::bind(std::move(initializer), &task_runner));
+ task_runner.Run();
+}
+
+void ThreadTaskRunner::PostTaskAndWaitForTesting(std::function<void()> fn) {
+ std::mutex mutex;
+ std::condition_variable cv;
+
+ std::unique_lock<std::mutex> lock(mutex);
+ bool done = false;
+ task_runner_->PostTask([&mutex, &cv, &done, &fn] {
+ fn();
+
+ std::lock_guard<std::mutex> inner_lock(mutex);
+ done = true;
+ cv.notify_one();
+ });
+ cv.wait(lock, [&done] { return done; });
+}
+
+uint64_t ThreadTaskRunner::GetThreadCPUTimeNsForTesting() {
+ uint64_t thread_time_ns = 0;
+ PostTaskAndWaitForTesting([&thread_time_ns] {
+ thread_time_ns = static_cast<uint64_t>(base::GetThreadCPUTimeNs().count());
+ });
+ return thread_time_ns;
+}
+
+void ThreadTaskRunner::PostTask(std::function<void()> task) {
+ task_runner_->PostTask(std::move(task));
+}
+
+void ThreadTaskRunner::PostDelayedTask(std::function<void()> task,
+ uint32_t delay_ms) {
+ task_runner_->PostDelayedTask(std::move(task), delay_ms);
+}
+
+void ThreadTaskRunner::AddFileDescriptorWatch(
+ PlatformHandle handle,
+ std::function<void()> watch_task) {
+ task_runner_->AddFileDescriptorWatch(handle, std::move(watch_task));
+}
+
+void ThreadTaskRunner::RemoveFileDescriptorWatch(PlatformHandle handle) {
+ task_runner_->RemoveFileDescriptorWatch(handle);
+}
+
+bool ThreadTaskRunner::RunsTasksOnCurrentThread() const {
+ return task_runner_->RunsTasksOnCurrentThread();
+}
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/unix_task_runner.cc
+/*
+ * Copyright (C) 2017 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/base/build_config.h"
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/platform.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/unix_task_runner.h"
+
+#include <errno.h>
+#include <stdlib.h>
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#include <synchapi.h>
+#else
+#include <unistd.h>
+#endif
+
+#include <algorithm>
+#include <limits>
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/watchdog.h"
+
+namespace perfetto {
+namespace base {
+
+UnixTaskRunner::UnixTaskRunner() {
+ AddFileDescriptorWatch(event_.fd(), [] {
+ // Not reached -- see PostFileDescriptorWatches().
+ PERFETTO_DFATAL("Should be unreachable.");
+ });
+}
+
+UnixTaskRunner::~UnixTaskRunner() = default;
+
+void UnixTaskRunner::WakeUp() {
+ event_.Notify();
+}
+
+void UnixTaskRunner::Run() {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ created_thread_id_.store(GetThreadId(), std::memory_order_relaxed);
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ quit_ = false;
+ }
+ for (;;) {
+ int poll_timeout_ms;
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ if (quit_)
+ return;
+ poll_timeout_ms = GetDelayMsToNextTaskLocked();
+ UpdateWatchTasksLocked();
+ }
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ DWORD timeout =
+ poll_timeout_ms >= 0 ? static_cast<DWORD>(poll_timeout_ms) : INFINITE;
+ DWORD ret =
+ WaitForMultipleObjects(static_cast<DWORD>(poll_fds_.size()),
+ &poll_fds_[0], /*bWaitAll=*/false, timeout);
+ // Unlike poll(2), WaitForMultipleObjects() returns only *one* handle in the
+ // set, even when >1 is signalled. In order to avoid starvation,
+ // PostFileDescriptorWatches() will WaitForSingleObject() each other handle
+ // to ensure fairness. |ret| here is passed just to avoid an extra
+ // WaitForSingleObject() for the one handle that WaitForMultipleObject()
+ // returned.
+ PostFileDescriptorWatches(ret);
+#else
+ platform::BeforeMaybeBlockingSyscall();
+ int ret = PERFETTO_EINTR(poll(
+ &poll_fds_[0], static_cast<nfds_t>(poll_fds_.size()), poll_timeout_ms));
+ platform::AfterMaybeBlockingSyscall();
+ PERFETTO_CHECK(ret >= 0);
+ PostFileDescriptorWatches(0 /*ignored*/);
+#endif
+
+ // To avoid starvation we always interleave all types of tasks -- immediate,
+ // delayed and file descriptor watches.
+ RunImmediateAndDelayedTask();
+ }
+}
+
+void UnixTaskRunner::Quit() {
+ std::lock_guard<std::mutex> lock(lock_);
+ quit_ = true;
+ WakeUp();
+}
+
+bool UnixTaskRunner::QuitCalled() {
+ std::lock_guard<std::mutex> lock(lock_);
+ return quit_;
+}
+
+bool UnixTaskRunner::IsIdleForTesting() {
+ std::lock_guard<std::mutex> lock(lock_);
+ return immediate_tasks_.empty();
+}
+
+void UnixTaskRunner::AdvanceTimeForTesting(uint32_t ms) {
+ std::lock_guard<std::mutex> lock(lock_);
+ advanced_time_for_testing_ += TimeMillis(ms);
+}
+
+void UnixTaskRunner::UpdateWatchTasksLocked() {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ if (!watch_tasks_changed_)
+ return;
+ watch_tasks_changed_ = false;
+#endif
+ poll_fds_.clear();
+ for (auto& it : watch_tasks_) {
+ PlatformHandle handle = it.first;
+ WatchTask& watch_task = it.second;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ if (!watch_task.pending)
+ poll_fds_.push_back(handle);
+#else
+ watch_task.poll_fd_index = poll_fds_.size();
+ poll_fds_.push_back({handle, POLLIN | POLLHUP, 0});
+#endif
+ }
+}
+
+void UnixTaskRunner::RunImmediateAndDelayedTask() {
+ // If locking overhead becomes an issue, add a separate work queue.
+ std::function<void()> immediate_task;
+ std::function<void()> delayed_task;
+ TimeMillis now = GetWallTimeMs();
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ if (!immediate_tasks_.empty()) {
+ immediate_task = std::move(immediate_tasks_.front());
+ immediate_tasks_.pop_front();
+ }
+ if (!delayed_tasks_.empty()) {
+ auto it = delayed_tasks_.begin();
+ if (now + advanced_time_for_testing_ >= it->first) {
+ delayed_task = std::move(it->second);
+ delayed_tasks_.erase(it);
+ }
+ }
+ }
+
+ errno = 0;
+ if (immediate_task)
+ RunTaskWithWatchdogGuard(immediate_task);
+ errno = 0;
+ if (delayed_task)
+ RunTaskWithWatchdogGuard(delayed_task);
+}
+
+void UnixTaskRunner::PostFileDescriptorWatches(uint64_t windows_wait_result) {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ for (size_t i = 0; i < poll_fds_.size(); i++) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ const PlatformHandle handle = poll_fds_[i];
+ // |windows_wait_result| is the result of WaitForMultipleObjects() call. If
+ // one of the objects was signalled, it will have a value between
+ // [0, poll_fds_.size()].
+ if (i != windows_wait_result &&
+ WaitForSingleObject(handle, 0) != WAIT_OBJECT_0) {
+ continue;
+ }
+#else
+ base::ignore_result(windows_wait_result);
+ const PlatformHandle handle = poll_fds_[i].fd;
+ if (!(poll_fds_[i].revents & (POLLIN | POLLHUP)))
+ continue;
+ poll_fds_[i].revents = 0;
+#endif
+
+ // The wake-up event is handled inline to avoid an infinite recursion of
+ // posted tasks.
+ if (handle == event_.fd()) {
+ event_.Clear();
+ continue;
+ }
+
+ // Binding to |this| is safe since we are the only object executing the
+ // task.
+ PostTask(std::bind(&UnixTaskRunner::RunFileDescriptorWatch, this, handle));
+
+ // Flag the task as pending.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ // On Windows this is done by marking the WatchTask entry as pending. This
+ // is more expensive than Linux as requires rebuilding the |poll_fds_|
+ // vector on each call. There doesn't seem to be a good alternative though.
+ auto it = watch_tasks_.find(handle);
+ PERFETTO_CHECK(it != watch_tasks_.end());
+ PERFETTO_DCHECK(!it->second.pending);
+ it->second.pending = true;
+#else
+ // On UNIX systems instead, we just make the fd negative while its task is
+ // pending. This makes poll(2) ignore the fd.
+ PERFETTO_DCHECK(poll_fds_[i].fd >= 0);
+ poll_fds_[i].fd = -poll_fds_[i].fd;
+#endif
+ }
+}
+
+void UnixTaskRunner::RunFileDescriptorWatch(PlatformHandle fd) {
+ std::function<void()> task;
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ auto it = watch_tasks_.find(fd);
+ if (it == watch_tasks_.end())
+ return;
+ WatchTask& watch_task = it->second;
+
+ // Make poll(2) pay attention to the fd again. Since another thread may have
+ // updated this watch we need to refresh the set first.
+ UpdateWatchTasksLocked();
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ // On Windows we manually track the presence of outstanding tasks for the
+ // watch. The UpdateWatchTasksLocked() in the Run() loop will re-add the
+ // task to the |poll_fds_| vector.
+ PERFETTO_DCHECK(watch_task.pending);
+ watch_task.pending = false;
+#else
+ size_t fd_index = watch_task.poll_fd_index;
+ PERFETTO_DCHECK(fd_index < poll_fds_.size());
+ PERFETTO_DCHECK(::abs(poll_fds_[fd_index].fd) == fd);
+ poll_fds_[fd_index].fd = fd;
+#endif
+ task = watch_task.callback;
+ }
+ errno = 0;
+ RunTaskWithWatchdogGuard(task);
+}
+
+int UnixTaskRunner::GetDelayMsToNextTaskLocked() const {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ if (!immediate_tasks_.empty())
+ return 0;
+ if (!delayed_tasks_.empty()) {
+ TimeMillis diff = delayed_tasks_.begin()->first - GetWallTimeMs() -
+ advanced_time_for_testing_;
+ return std::max(0, static_cast<int>(diff.count()));
+ }
+ return -1;
+}
+
+void UnixTaskRunner::PostTask(std::function<void()> task) {
+ bool was_empty;
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ was_empty = immediate_tasks_.empty();
+ immediate_tasks_.push_back(std::move(task));
+ }
+ if (was_empty)
+ WakeUp();
+}
+
+void UnixTaskRunner::PostDelayedTask(std::function<void()> task,
+ uint32_t delay_ms) {
+ TimeMillis runtime = GetWallTimeMs() + TimeMillis(delay_ms);
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ delayed_tasks_.insert(
+ std::make_pair(runtime + advanced_time_for_testing_, std::move(task)));
+ }
+ WakeUp();
+}
+
+void UnixTaskRunner::AddFileDescriptorWatch(PlatformHandle fd,
+ std::function<void()> task) {
+ PERFETTO_DCHECK(PlatformHandleChecker::IsValid(fd));
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ PERFETTO_DCHECK(!watch_tasks_.count(fd));
+ WatchTask& watch_task = watch_tasks_[fd];
+ watch_task.callback = std::move(task);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ watch_task.pending = false;
+#else
+ watch_task.poll_fd_index = SIZE_MAX;
+#endif
+ watch_tasks_changed_ = true;
+ }
+ WakeUp();
+}
+
+void UnixTaskRunner::RemoveFileDescriptorWatch(PlatformHandle fd) {
+ PERFETTO_DCHECK(PlatformHandleChecker::IsValid(fd));
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ PERFETTO_DCHECK(watch_tasks_.count(fd));
+ watch_tasks_.erase(fd);
+ watch_tasks_changed_ = true;
+ }
+ // No need to schedule a wake-up for this.
+}
+
+bool UnixTaskRunner::RunsTasksOnCurrentThread() const {
+ return GetThreadId() == created_thread_id_.load(std::memory_order_relaxed);
+}
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/subprocess.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/subprocess.h
+/*
+ * 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.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_
+
+#include <condition_variable>
+#include <functional>
+#include <initializer_list>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+#include <thread>
+#include <vector>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/platform_handle.h"
+// gen_amalgamated expanded: #include "perfetto/base/proc_utils.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/scoped_file.h"
+
+namespace perfetto {
+namespace base {
+
+// Handles creation and lifecycle management of subprocesses, taking care of
+// all subtleties involved in handling processes on UNIX.
+// This class allows to deal with macro two use-cases:
+// 1) fork() + exec() equivalent: for spawning a brand new process image.
+// This happens when |args.exec_cmd| is not empty.
+// This is safe to use even in a multi-threaded environment.
+// 2) fork(): for spawning a process and running a function.
+// This happens when |args.posix_entrypoint_for_testing| is not empty.
+// This is intended only for tests as it is extremely subtle.
+// This mode must be used with extreme care. Before the entrypoint is
+// invoked all file descriptors other than stdin/out/err and the ones
+// specified in |args.preserve_fds| will be closed, to avoid each process
+// retaining a dupe of other subprocesses pipes. This however means that
+// any non trivial calls (including logging) must be avoided as they might
+// refer to FDs that are now closed. The entrypoint should really be used
+// just to signal a pipe or similar for synchronizing sequencing in tests.
+
+//
+// This class allows to control stdin/out/err pipe redirection and takes care
+// of keeping all the pipes pumped (stdin) / drained (stdout/err), in a similar
+// fashion of python's subprocess.Communicate()
+// stdin: is always piped and closed once the |args.input| buffer is written.
+// stdout/err can be either:
+// - dup()ed onto the parent process stdout/err.
+// - redirected onto /dev/null.
+// - piped onto a buffer (see output() method). There is only one output
+// buffer in total. If both stdout and stderr are set to kBuffer mode, they
+// will be merged onto the same. There doesn't seem any use case where they
+// are needed distinctly.
+//
+// Some caveats worth mentioning:
+// - It always waitpid()s, to avoid leaving zombies around. If the process is
+// not terminated by the time the destructor is reached, the dtor will
+// send a SIGKILL and wait for the termination.
+// - After fork()-ing it will close all file descriptors, preserving only
+// stdin/out/err and the fds listed in |args.preserve_fds|.
+// - On Linux/Android, the child process will be SIGKILL-ed if the calling
+// thread exists, even if the Subprocess is std::move()-d onto another thread.
+// This happens by virtue PR_SET_PDEATHSIG, which is used to avoid that
+// child processes are leaked in the case of a crash of the parent (frequent
+// in tests). However, the child process might still be leaked if execing
+// a setuid/setgid binary (see man 2 prctl).
+//
+// Usage:
+// base::Subprocess p({"/bin/cat", "-"});
+// (or equivalently:
+// base::Subprocess p;
+// p.args.exec_cmd.push_back("/bin/cat");
+// p.args.exec_cmd.push_back("-");
+// )
+// p.args.stdout_mode = base::Subprocess::kBuffer;
+// p.args.stderr_mode = base::Subprocess::kInherit;
+// p.args.input = "stdin contents";
+// p.Call();
+// (or equivalently:
+// p.Start();
+// p.Wait();
+// )
+// EXPECT_EQ(p.status(), base::Subprocess::kTerminated);
+// EXPECT_EQ(p.returncode(), 0);
+class Subprocess {
+ public:
+ enum Status {
+ kNotStarted = 0, // Before calling Start() or Call().
+ kRunning, // After calling Start(), before Wait().
+ kTerminated, // The subprocess terminated, either successfully or not.
+ // This includes crashes or other signals on UNIX.
+ };
+
+ enum class OutputMode {
+ kInherit = 0, // Inherit's the caller process stdout/stderr.
+ kDevNull, // dup() onto /dev/null.
+ kBuffer, // dup() onto a pipe and move it into the output() buffer.
+ kFd, // dup() onto the passed args.fd.
+ };
+
+ enum class InputMode {
+ kBuffer = 0, // dup() onto a pipe and write args.input on it.
+ kDevNull, // dup() onto /dev/null.
+ };
+
+ // Input arguments for configuring the subprocess behavior.
+ struct Args {
+ Args(std::initializer_list<std::string> _cmd = {}) : exec_cmd(_cmd) {}
+ Args(Args&&) noexcept;
+ Args& operator=(Args&&);
+ // If non-empty this will cause an exec() when Start()/Call() are called.
+ std::vector<std::string> exec_cmd;
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ // If non-empty, it changes the argv[0] argument passed to exec. If
+ // unset, argv[0] == exec_cmd[0]. This is to handle cases like:
+ // exec_cmd = {"/proc/self/exec"}, argv0: "my_custom_test_override".
+ std::string posix_argv0_override_for_testing;
+
+ // If non-empty this will be invoked on the fork()-ed child process, after
+ // stdin/out/err has been redirected and all other file descriptor are
+ // closed. It is valid to specify both |exec_cmd| AND
+ // |posix_entrypoint_for_testing|. In this case the latter will be invoked
+ // just before the exec() call, but after having closed all fds % stdin/o/e.
+ // This is for synchronization barriers in tests.
+ std::function<void()> posix_entrypoint_for_testing;
+
+ // When set, will will move the process to the given process group. If set
+ // and zero, it will create a new process group. Effectively this calls
+ // setpgid(0 /*self_pid*/, posix_proc_group_id).
+ // This can be used to avoid that subprocesses receive CTRL-C from the
+ // terminal, while still living in the same session.
+ std::optional<pid_t> posix_proc_group_id{};
+#endif
+
+ // If non-empty, replaces the environment passed to exec().
+ std::vector<std::string> env;
+
+ // The file descriptors in this list will not be closed.
+ std::vector<int> preserve_fds;
+
+ // The data to push in the child process stdin, if input_mode ==
+ // InputMode::kBuffer.
+ std::string input;
+
+ InputMode stdin_mode = InputMode::kBuffer;
+ OutputMode stdout_mode = OutputMode::kInherit;
+ OutputMode stderr_mode = OutputMode::kInherit;
+
+ base::ScopedPlatformHandle out_fd;
+
+ // Returns " ".join(exec_cmd), quoting arguments.
+ std::string GetCmdString() const;
+ };
+
+ struct ResourceUsage {
+ uint32_t cpu_utime_ms = 0;
+ uint32_t cpu_stime_ms = 0;
+ uint32_t max_rss_kb = 0;
+ uint32_t min_page_faults = 0;
+ uint32_t maj_page_faults = 0;
+ uint32_t vol_ctx_switch = 0;
+ uint32_t invol_ctx_switch = 0;
+
+ uint32_t cpu_time_ms() const { return cpu_utime_ms + cpu_stime_ms; }
+ };
+
+ explicit Subprocess(std::initializer_list<std::string> exec_cmd = {});
+ Subprocess(Subprocess&&) noexcept;
+ Subprocess& operator=(Subprocess&&);
+ ~Subprocess(); // It will KillAndWaitForTermination() if still alive.
+
+ // Starts the subprocess but doesn't wait for its termination. The caller
+ // is expected to either call Wait() or Poll() after this call.
+ void Start();
+
+ // Wait for process termination. Can be called more than once.
+ // Args:
+ // |timeout_ms| = 0: wait indefinitely.
+ // |timeout_ms| > 0: wait for at most |timeout_ms|.
+ // Returns:
+ // True: The process terminated. See status() and returncode().
+ // False: Timeout reached, the process is still running. In this case the
+ // process will be left in the kRunning state.
+ bool Wait(int timeout_ms = 0);
+
+ // Equivalent of Start() + Wait();
+ // Returns true if the process exited cleanly with return code 0. False in
+ // any othe case.
+ bool Call(int timeout_ms = 0);
+
+ Status Poll();
+
+ // Sends a signal (SIGKILL if not specified) and wait for process termination.
+ void KillAndWaitForTermination(int sig_num = 0);
+
+ PlatformProcessId pid() const { return s_->pid; }
+
+ // The accessors below are updated only after a call to Poll(), Wait() or
+ // KillAndWaitForTermination().
+ // In most cases you want to call Poll() rather than these accessors.
+
+ Status status() const { return s_->status; }
+ int returncode() const { return s_->returncode; }
+ bool timed_out() const { return s_->timed_out; }
+
+ // This contains both stdout and stderr (if the corresponding _mode ==
+ // OutputMode::kBuffer). It's non-const so the caller can std::move() it.
+ std::string& output() { return s_->output; }
+ const std::string& output() const { return s_->output; }
+
+ const ResourceUsage& posix_rusage() const { return *s_->rusage; }
+
+ Args args;
+
+ private:
+ // The signal/exit code used when killing the process in case of a timeout.
+ static const int kTimeoutSignal;
+
+ Subprocess(const Subprocess&) = delete;
+ Subprocess& operator=(const Subprocess&) = delete;
+
+ // This is to deal robustly with the move operators, without having to
+ // manually maintain member-wise move instructions.
+ struct MovableState {
+ base::Pipe stdin_pipe;
+ base::Pipe stdouterr_pipe;
+ PlatformProcessId pid;
+ Status status = kNotStarted;
+ int returncode = -1;
+ std::string output; // Stdin+stderr. Only when OutputMode::kBuffer.
+ std::unique_ptr<ResourceUsage> rusage{new ResourceUsage()};
+ bool timed_out = false;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ std::thread stdouterr_thread;
+ std::thread stdin_thread;
+ ScopedPlatformHandle win_proc_handle;
+ ScopedPlatformHandle win_thread_handle;
+
+ base::EventFd stdouterr_done_event;
+ std::mutex mutex; // Protects locked_outerr_buf and the two pipes.
+ std::string locked_outerr_buf;
+#else
+ base::Pipe exit_status_pipe;
+ size_t input_written = 0;
+ std::thread waitpid_thread;
+#endif
+ };
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ static void StdinThread(MovableState*, std::string input);
+ static void StdoutErrThread(MovableState*);
+#else
+ void TryPushStdin();
+ void TryReadStdoutAndErr();
+ void TryReadExitStatus();
+ bool PollInternal(int poll_timeout_ms);
+#endif
+
+ std::unique_ptr<MovableState> s_;
+};
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/subprocess.h"
+
+#include <tuple>
+
+// This file contains only the common bits (ctors / dtors / move operators).
+// The rest lives in subprocess_posix.cc and subprocess_windows.cc.
+
+namespace perfetto {
+namespace base {
+
+Subprocess::Args::Args(Args&&) noexcept = default;
+Subprocess::Args& Subprocess::Args::operator=(Args&&) = default;
+
+Subprocess::Subprocess(std::initializer_list<std::string> a)
+ : args(a), s_(new MovableState()) {}
+
+Subprocess::Subprocess(Subprocess&& other) noexcept {
+ static_assert(sizeof(Subprocess) ==
+ sizeof(std::tuple<std::unique_ptr<MovableState>, Args>),
+ "base::Subprocess' move ctor needs updating");
+ s_ = std::move(other.s_);
+ args = std::move(other.args);
+
+ // Reset the state of the moved-from object.
+ other.s_.reset(new MovableState());
+ other.~Subprocess();
+ new (&other) Subprocess();
+}
+
+Subprocess& Subprocess::operator=(Subprocess&& other) {
+ this->~Subprocess();
+ new (this) Subprocess(std::move(other));
+ return *this;
+}
+
+Subprocess::~Subprocess() {
+ if (s_->status == kRunning)
+ KillAndWaitForTermination();
+}
+
+bool Subprocess::Call(int timeout_ms) {
+ PERFETTO_CHECK(s_->status == kNotStarted);
+ Start();
+
+ if (!Wait(timeout_ms)) {
+ s_->timed_out = true;
+ KillAndWaitForTermination(kTimeoutSignal);
+ }
+ PERFETTO_DCHECK(s_->status != kRunning);
+ return s_->status == kTerminated && s_->returncode == 0;
+}
+
+std::string Subprocess::Args::GetCmdString() const {
+ std::string str;
+ for (size_t i = 0; i < exec_cmd.size(); i++) {
+ str += i > 0 ? " \"" : "";
+ str += exec_cmd[i];
+ str += i > 0 ? "\"" : "";
+ }
+ return str;
+}
+
+} // namespace base
+} // namespace perfetto
+// gen_amalgamated begin source: src/base/subprocess_posix.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/subprocess.h"
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <thread>
+#include <tuple>
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/prctl.h>
+#endif
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+
+// In MacOS this is not defined in any header.
+extern "C" char** environ;
+
+namespace perfetto {
+namespace base {
+
+namespace {
+
+struct ChildProcessArgs {
+ Subprocess::Args* create_args;
+ const char* exec_cmd = nullptr;
+ std::vector<char*> argv;
+ std::vector<char*> env;
+ int stdin_pipe_rd = -1;
+ int stdouterr_pipe_wr = -1;
+};
+
+// Don't add any dynamic allocation in this function. This will be invoked
+// under a fork(), potentially in a state where the allocator lock is held.
+void __attribute__((noreturn)) ChildProcess(ChildProcessArgs* args) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ // In no case we want a child process to outlive its parent process. This is
+ // relevant for tests, so that a test failure/crash doesn't leave child
+ // processes around that get reparented to init.
+ prctl(PR_SET_PDEATHSIG, SIGKILL);
+#endif
+
+ auto die = [args](const char* err) __attribute__((noreturn)) {
+ base::ignore_result(write(args->stdouterr_pipe_wr, err, strlen(err)));
+ base::ignore_result(write(args->stdouterr_pipe_wr, "\n", 1));
+ // From https://www.gnu.org/software/libc/manual/html_node/Exit-Status.html
+ // "In particular, the value 128 is used to indicate failure to execute
+ // another program in a subprocess. This convention is not universally
+ // obeyed, but it is a good idea to follow it in your programs."
+ _exit(128);
+ };
+
+ if (args->create_args->posix_proc_group_id.has_value()) {
+ if (setpgid(0 /*self*/, args->create_args->posix_proc_group_id.value())) {
+ die("setpgid() failed");
+ }
+ }
+
+ auto set_fd_close_on_exec = [&die](int fd, bool close_on_exec) {
+ int flags = fcntl(fd, F_GETFD, 0);
+ if (flags < 0)
+ die("fcntl(F_GETFD) failed");
+ flags = close_on_exec ? (flags | FD_CLOEXEC) : (flags & ~FD_CLOEXEC);
+ if (fcntl(fd, F_SETFD, flags) < 0)
+ die("fcntl(F_SETFD) failed");
+ };
+
+ if (getppid() == 1)
+ die("terminating because parent process died");
+
+ switch (args->create_args->stdin_mode) {
+ case Subprocess::InputMode::kBuffer:
+ if (dup2(args->stdin_pipe_rd, STDIN_FILENO) == -1)
+ die("Failed to dup2(STDIN)");
+ close(args->stdin_pipe_rd);
+ break;
+ case Subprocess::InputMode::kDevNull:
+ if (dup2(open("/dev/null", O_RDONLY), STDIN_FILENO) == -1)
+ die("Failed to dup2(STDOUT)");
+ break;
+ }
+
+ switch (args->create_args->stdout_mode) {
+ case Subprocess::OutputMode::kInherit:
+ break;
+ case Subprocess::OutputMode::kDevNull: {
+ if (dup2(open("/dev/null", O_RDWR), STDOUT_FILENO) == -1)
+ die("Failed to dup2(STDOUT)");
+ break;
+ }
+ case Subprocess::OutputMode::kBuffer:
+ if (dup2(args->stdouterr_pipe_wr, STDOUT_FILENO) == -1)
+ die("Failed to dup2(STDOUT)");
+ break;
+ case Subprocess::OutputMode::kFd:
+ if (dup2(*args->create_args->out_fd, STDOUT_FILENO) == -1)
+ die("Failed to dup2(STDOUT)");
+ break;
+ }
+
+ switch (args->create_args->stderr_mode) {
+ case Subprocess::OutputMode::kInherit:
+ break;
+ case Subprocess::OutputMode::kDevNull: {
+ if (dup2(open("/dev/null", O_RDWR), STDERR_FILENO) == -1)
+ die("Failed to dup2(STDERR)");
+ break;
+ }
+ case Subprocess::OutputMode::kBuffer:
+ if (dup2(args->stdouterr_pipe_wr, STDERR_FILENO) == -1)
+ die("Failed to dup2(STDERR)");
+ break;
+ case Subprocess::OutputMode::kFd:
+ if (dup2(*args->create_args->out_fd, STDERR_FILENO) == -1)
+ die("Failed to dup2(STDERR)");
+ break;
+ }
+
+ // Close all FDs % stdin/out/err and the ones that the client explicitly
+ // asked to retain. The reason for this is twofold:
+ // 1. For exec-only (i.e. entrypoint == empty) cases: it avoids leaking FDs
+ // that didn't get marked as O_CLOEXEC by accident.
+ // 2. In fork() mode (entrypoint not empty) avoids retaining a dup of eventfds
+ // that would prevent the parent process to receive EOFs (tests usually use
+ // pipes as a synchronization mechanism between subprocesses).
+ const auto& preserve_fds = args->create_args->preserve_fds;
+ for (int i = 0; i < 512; i++) {
+ if (i != STDIN_FILENO && i != STDERR_FILENO && i != STDOUT_FILENO &&
+ i != args->stdouterr_pipe_wr &&
+ !std::count(preserve_fds.begin(), preserve_fds.end(), i)) {
+ close(i);
+ }
+ }
+
+ // Clears O_CLOEXEC from stdin/out/err and the |preserve_fds| list. These are
+ // the only FDs that we want to be preserved after the exec().
+ set_fd_close_on_exec(STDIN_FILENO, false);
+ set_fd_close_on_exec(STDOUT_FILENO, false);
+ set_fd_close_on_exec(STDERR_FILENO, false);
+
+ for (auto fd : preserve_fds)
+ set_fd_close_on_exec(fd, false);
+
+ // If the caller specified a std::function entrypoint, run that first.
+ if (args->create_args->posix_entrypoint_for_testing)
+ args->create_args->posix_entrypoint_for_testing();
+
+ // If the caller specified only an entrypoint, without any args, exit now.
+ // Otherwise proceed with the exec() below.
+ if (!args->exec_cmd)
+ _exit(0);
+
+ // If |args[0]| is a path use execv() (which takes a path), othewise use
+ // exevp(), which uses the shell and follows PATH.
+ if (strchr(args->exec_cmd, '/')) {
+ char** env = args->env.empty() ? environ : args->env.data();
+ execve(args->exec_cmd, args->argv.data(), env);
+ } else {
+ // There is no execvpe() on Mac.
+ if (!args->env.empty())
+ die("A full path is required for |exec_cmd| when setting |env|");
+ execvp(args->exec_cmd, args->argv.data());
+ }
+
+ // Reached only if execv fails.
+ die("execve() failed");
+}
+
+} // namespace
+
+// static
+const int Subprocess::kTimeoutSignal = SIGKILL;
+
+void Subprocess::Start() {
+ ChildProcessArgs proc_args;
+ proc_args.create_args = &args;
+
+ // Setup argv.
+ if (!args.exec_cmd.empty()) {
+ proc_args.exec_cmd = args.exec_cmd[0].c_str();
+ for (const std::string& arg : args.exec_cmd)
+ proc_args.argv.push_back(const_cast<char*>(arg.c_str()));
+ proc_args.argv.push_back(nullptr);
+
+ if (!args.posix_argv0_override_for_testing.empty()) {
+ proc_args.argv[0] =
+ const_cast<char*>(args.posix_argv0_override_for_testing.c_str());
+ }
+ }
+
+ // Setup env.
+ if (!args.env.empty()) {
+ for (const std::string& str : args.env)
+ proc_args.env.push_back(const_cast<char*>(str.c_str()));
+ proc_args.env.push_back(nullptr);
+ }
+
+ // Setup the pipes for stdin/err redirection.
+ if (args.stdin_mode == InputMode::kBuffer) {
+ s_->stdin_pipe = base::Pipe::Create(base::Pipe::kWrNonBlock);
+ proc_args.stdin_pipe_rd = *s_->stdin_pipe.rd;
+ }
+ s_->stdouterr_pipe = base::Pipe::Create(base::Pipe::kRdNonBlock);
+ proc_args.stdouterr_pipe_wr = *s_->stdouterr_pipe.wr;
+
+ // Spawn the child process that will exec().
+ s_->pid = fork();
+ PERFETTO_CHECK(s_->pid >= 0);
+ if (s_->pid == 0) {
+ // Close the parent-ends of the pipes.
+ s_->stdin_pipe.wr.reset();
+ s_->stdouterr_pipe.rd.reset();
+ ChildProcess(&proc_args);
+ // ChildProcess() doesn't return, not even in case of failures.
+ PERFETTO_FATAL("not reached");
+ }
+
+ s_->status = kRunning;
+
+ // Close the child-end of the pipes.
+ // Deliberately NOT closing the s_->stdin_pipe.rd. This is to avoid crashing
+ // with a SIGPIPE if the process exits without consuming its stdin, while
+ // the parent tries to write() on the other end of the stdin pipe.
+ s_->stdouterr_pipe.wr.reset();
+ proc_args.create_args->out_fd.reset();
+
+ // Spawn a thread that is blocked on waitpid() and writes the termination
+ // status onto a pipe. The problem here is that waipid() doesn't have a
+ // timeout option and can't be passed to poll(). The alternative would be
+ // using a SIGCHLD handler, but anecdotally signal handlers introduce more
+ // problems than what they solve.
+ s_->exit_status_pipe = base::Pipe::Create(base::Pipe::kRdNonBlock);
+
+ // Both ends of the pipe are closed after the thread.join().
+ int pid = s_->pid;
+ int exit_status_pipe_wr = s_->exit_status_pipe.wr.release();
+ auto* rusage = s_->rusage.get();
+ s_->waitpid_thread = std::thread([pid, exit_status_pipe_wr, rusage] {
+ int pid_stat = -1;
+ struct rusage usg {};
+ int wait_res = PERFETTO_EINTR(wait4(pid, &pid_stat, 0, &usg));
+ PERFETTO_CHECK(wait_res == pid);
+
+ auto tv_to_ms = [](const struct timeval& tv) {
+ return static_cast<uint32_t>(tv.tv_sec * 1000 + tv.tv_usec / 1000);
+ };
+ rusage->cpu_utime_ms = tv_to_ms(usg.ru_utime);
+ rusage->cpu_stime_ms = tv_to_ms(usg.ru_stime);
+ rusage->max_rss_kb = static_cast<uint32_t>(usg.ru_maxrss) / 1000;
+ rusage->min_page_faults = static_cast<uint32_t>(usg.ru_minflt);
+ rusage->maj_page_faults = static_cast<uint32_t>(usg.ru_majflt);
+ rusage->vol_ctx_switch = static_cast<uint32_t>(usg.ru_nvcsw);
+ rusage->invol_ctx_switch = static_cast<uint32_t>(usg.ru_nivcsw);
+
+ base::ignore_result(PERFETTO_EINTR(
+ write(exit_status_pipe_wr, &pid_stat, sizeof(pid_stat))));
+ PERFETTO_CHECK(close(exit_status_pipe_wr) == 0 || errno == EINTR);
+ });
+}
+
+Subprocess::Status Subprocess::Poll() {
+ if (s_->status != kRunning)
+ return s_->status; // Nothing to poll.
+ while (PollInternal(0 /* don't block*/)) {
+ }
+ return s_->status;
+}
+
+// |timeout_ms| semantic:
+// -1: Block indefinitely.
+// 0: Don't block, return immediately.
+// >0: Block for at most X ms.
+// Returns:
+// True: Read at least one fd (so there might be more queued).
+// False: if all fds reached quiescent (no data to read/write).
+bool Subprocess::PollInternal(int poll_timeout_ms) {
+ struct pollfd fds[3]{};
+ size_t num_fds = 0;
+ if (s_->exit_status_pipe.rd) {
+ fds[num_fds].fd = *s_->exit_status_pipe.rd;
+ fds[num_fds].events = POLLIN;
+ num_fds++;
+ }
+ if (s_->stdouterr_pipe.rd) {
+ fds[num_fds].fd = *s_->stdouterr_pipe.rd;
+ fds[num_fds].events = POLLIN;
+ num_fds++;
+ }
+ if (s_->stdin_pipe.wr) {
+ fds[num_fds].fd = *s_->stdin_pipe.wr;
+ fds[num_fds].events = POLLOUT;
+ num_fds++;
+ }
+
+ if (num_fds == 0)
+ return false;
+
+ auto nfds = static_cast<nfds_t>(num_fds);
+ int poll_res = PERFETTO_EINTR(poll(fds, nfds, poll_timeout_ms));
+ PERFETTO_CHECK(poll_res >= 0);
+
+ TryReadStdoutAndErr();
+ TryPushStdin();
+ TryReadExitStatus();
+
+ return poll_res > 0;
+}
+
+bool Subprocess::Wait(int timeout_ms) {
+ PERFETTO_CHECK(s_->status != kNotStarted);
+
+ // Break out of the loop only after both conditions are satisfied:
+ // - All stdout/stderr data has been read (if kBuffer).
+ // - The process exited.
+ // Note that the two events can happen arbitrary order. After the process
+ // exits, there might be still data in the pipe buffer, which we want to
+ // read fully.
+ //
+ // Instead, don't wait on the stdin to be fully written. The child process
+ // might exit prematurely (or crash). If that happens, we can end up in a
+ // state where the write(stdin_pipe_.wr) will never unblock.
+
+ const int64_t t_start = base::GetWallTimeMs().count();
+ while (s_->exit_status_pipe.rd || s_->stdouterr_pipe.rd) {
+ int poll_timeout_ms = -1; // Block until a FD is ready.
+ if (timeout_ms > 0) {
+ const int64_t now = GetWallTimeMs().count();
+ poll_timeout_ms = timeout_ms - static_cast<int>(now - t_start);
+ if (poll_timeout_ms <= 0)
+ return false;
+ }
+ PollInternal(poll_timeout_ms);
+ } // while(...)
+ return true;
+}
+
+void Subprocess::TryReadExitStatus() {
+ if (!s_->exit_status_pipe.rd)
+ return;
+
+ int pid_stat = -1;
+ int64_t rsize = PERFETTO_EINTR(
+ read(*s_->exit_status_pipe.rd, &pid_stat, sizeof(pid_stat)));
+ if (rsize < 0 && errno == EAGAIN)
+ return;
+
+ if (rsize > 0) {
+ PERFETTO_CHECK(rsize == sizeof(pid_stat));
+ } else if (rsize < 0) {
+ PERFETTO_PLOG("Subprocess read(s_->exit_status_pipe) failed");
+ }
+ s_->waitpid_thread.join();
+ s_->exit_status_pipe.rd.reset();
+
+ s_->status = kTerminated;
+ if (WIFEXITED(pid_stat)) {
+ s_->returncode = WEXITSTATUS(pid_stat);
+ } else if (WIFSIGNALED(pid_stat)) {
+ s_->returncode = 128 + WTERMSIG(pid_stat); // Follow bash convention.
+ } else {
+ PERFETTO_FATAL("waitpid() returned an unexpected value (%d)", pid_stat);
+ }
+}
+
+// If the stidn pipe is still open, push input data and close it at the end.
+void Subprocess::TryPushStdin() {
+ if (!s_->stdin_pipe.wr)
+ return;
+
+ PERFETTO_DCHECK(args.input.empty() || s_->input_written < args.input.size());
+ if (!args.input.empty()) {
+ int64_t wsize =
+ PERFETTO_EINTR(write(*s_->stdin_pipe.wr, &args.input[s_->input_written],
+ args.input.size() - s_->input_written));
+ if (wsize < 0 && errno == EAGAIN)
+ return;
+
+ if (wsize >= 0) {
+ // Whether write() can return 0 is one of the greatest mysteries of UNIX.
+ // Just ignore it.
+ s_->input_written += static_cast<size_t>(wsize);
+ } else {
+ PERFETTO_PLOG("Subprocess write(stdin) failed");
+ s_->stdin_pipe.wr.reset();
+ }
+ }
+ PERFETTO_DCHECK(s_->input_written <= args.input.size());
+ if (s_->input_written == args.input.size())
+ s_->stdin_pipe.wr.reset(); // Close stdin.
+}
+
+void Subprocess::TryReadStdoutAndErr() {
+ if (!s_->stdouterr_pipe.rd)
+ return;
+ char buf[4096];
+ int64_t rsize =
+ PERFETTO_EINTR(read(*s_->stdouterr_pipe.rd, buf, sizeof(buf)));
+ if (rsize < 0 && errno == EAGAIN)
+ return;
+
+ if (rsize > 0) {
+ s_->output.append(buf, static_cast<size_t>(rsize));
+ } else if (rsize == 0 /* EOF */) {
+ s_->stdouterr_pipe.rd.reset();
+ } else {
+ PERFETTO_PLOG("Subprocess read(stdout/err) failed");
+ s_->stdouterr_pipe.rd.reset();
+ }
+}
+
+void Subprocess::KillAndWaitForTermination(int sig_num) {
+ kill(s_->pid, sig_num ? sig_num : SIGKILL);
+ Wait();
+ // TryReadExitStatus must have joined the thread.
+ PERFETTO_DCHECK(!s_->waitpid_thread.joinable());
+}
+
+} // namespace base
+} // namespace perfetto
+
+#endif // PERFETTO_OS_LINUX || PERFETTO_OS_ANDROID || PERFETTO_OS_APPLE
+// gen_amalgamated begin source: src/base/subprocess_windows.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/subprocess.h"
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+#include <stdio.h>
+
+#include <algorithm>
+#include <mutex>
+#include <tuple>
+
+#include <Windows.h>
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/pipe.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+
+namespace perfetto {
+namespace base {
+
+// static
+const int Subprocess::kTimeoutSignal = static_cast<int>(STATUS_TIMEOUT);
+
+void Subprocess::Start() {
+ if (args.exec_cmd.empty()) {
+ PERFETTO_ELOG("Subprocess.exec_cmd cannot be empty on Windows");
+ return;
+ }
+
+ // Quote arguments but only when ambiguous. When quoting, CreateProcess()
+ // assumes that the command is an absolute path and does not search in the
+ // %PATH%. If non quoted, instead, CreateProcess() tries both. This is to
+ // allow Subprocess("cmd.exe", "/c", "shell command").
+ std::string cmd;
+ for (const auto& part : args.exec_cmd) {
+ if (part.find(" ") != std::string::npos) {
+ cmd += "\"" + part + "\" ";
+ } else {
+ cmd += part + " ";
+ }
+ }
+ // Remove trailing space.
+ if (!cmd.empty())
+ cmd.resize(cmd.size() - 1);
+
+ if (args.stdin_mode == InputMode::kBuffer) {
+ s_->stdin_pipe = Pipe::Create();
+ // Allow the child process to inherit the other end of the pipe.
+ PERFETTO_CHECK(
+ ::SetHandleInformation(*s_->stdin_pipe.rd, HANDLE_FLAG_INHERIT, 1));
+ }
+
+ if (args.stderr_mode == OutputMode::kBuffer ||
+ args.stdout_mode == OutputMode::kBuffer) {
+ s_->stdouterr_pipe = Pipe::Create();
+ PERFETTO_CHECK(
+ ::SetHandleInformation(*s_->stdouterr_pipe.wr, HANDLE_FLAG_INHERIT, 1));
+ }
+
+ ScopedPlatformHandle nul_handle;
+ if (args.stderr_mode == OutputMode::kDevNull ||
+ args.stdout_mode == OutputMode::kDevNull) {
+ nul_handle.reset(::CreateFileA(
+ "NUL", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
+ PERFETTO_CHECK(::SetHandleInformation(*nul_handle, HANDLE_FLAG_INHERIT, 1));
+ }
+
+ PROCESS_INFORMATION proc_info{};
+ STARTUPINFOA start_info{};
+ start_info.cb = sizeof(STARTUPINFOA);
+
+ if (args.stderr_mode == OutputMode::kInherit) {
+ start_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
+ } else if (args.stderr_mode == OutputMode::kBuffer) {
+ start_info.hStdError = *s_->stdouterr_pipe.wr;
+ } else if (args.stderr_mode == OutputMode::kDevNull) {
+ start_info.hStdError = *nul_handle;
+ } else if (args.stderr_mode == OutputMode::kFd) {
+ PERFETTO_CHECK(
+ ::SetHandleInformation(*args.out_fd, HANDLE_FLAG_INHERIT, 1));
+ start_info.hStdError = *args.out_fd;
+ } else {
+ PERFETTO_CHECK(false);
+ }
+
+ if (args.stdout_mode == OutputMode::kInherit) {
+ start_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
+ } else if (args.stdout_mode == OutputMode::kBuffer) {
+ start_info.hStdOutput = *s_->stdouterr_pipe.wr;
+ } else if (args.stdout_mode == OutputMode::kDevNull) {
+ start_info.hStdOutput = *nul_handle;
+ } else if (args.stdout_mode == OutputMode::kFd) {
+ PERFETTO_CHECK(
+ ::SetHandleInformation(*args.out_fd, HANDLE_FLAG_INHERIT, 1));
+ start_info.hStdOutput = *args.out_fd;
+ } else {
+ PERFETTO_CHECK(false);
+ }
+
+ if (args.stdin_mode == InputMode::kBuffer) {
+ start_info.hStdInput = *s_->stdin_pipe.rd;
+ } else if (args.stdin_mode == InputMode::kDevNull) {
+ start_info.hStdInput = *nul_handle;
+ }
+
+ start_info.dwFlags |= STARTF_USESTDHANDLES;
+
+ // Create the child process.
+ bool success =
+ ::CreateProcessA(nullptr, // App name. Needs to be null to use PATH.
+ &cmd[0], // Command line.
+ nullptr, // Process security attributes.
+ nullptr, // Primary thread security attributes.
+ true, // Handles are inherited.
+ 0, // Flags.
+ nullptr, // Use parent's environment.
+ nullptr, // Use parent's current directory.
+ &start_info, // STARTUPINFO pointer.
+ &proc_info); // Receives PROCESS_INFORMATION.
+
+ // Close on our side the pipe ends that we passed to the child process.
+ s_->stdin_pipe.rd.reset();
+ s_->stdouterr_pipe.wr.reset();
+ args.out_fd.reset();
+
+ if (!success) {
+ s_->returncode = ERROR_FILE_NOT_FOUND;
+ s_->status = kTerminated;
+ s_->stdin_pipe.wr.reset();
+ s_->stdouterr_pipe.rd.reset();
+ PERFETTO_ELOG("CreateProcess failed: %lx, cmd: %s", GetLastError(),
+ &cmd[0]);
+ return;
+ }
+
+ s_->pid = proc_info.dwProcessId;
+ s_->win_proc_handle = ScopedPlatformHandle(proc_info.hProcess);
+ s_->win_thread_handle = ScopedPlatformHandle(proc_info.hThread);
+ s_->status = kRunning;
+
+ MovableState* s = s_.get();
+ if (args.stdin_mode == InputMode::kBuffer) {
+ s_->stdin_thread = std::thread(&Subprocess::StdinThread, s, args.input);
+ }
+
+ if (args.stderr_mode == OutputMode::kBuffer ||
+ args.stdout_mode == OutputMode::kBuffer) {
+ PERFETTO_DCHECK(s_->stdouterr_pipe.rd);
+ s_->stdouterr_thread = std::thread(&Subprocess::StdoutErrThread, s);
+ }
+}
+
+// static
+void Subprocess::StdinThread(MovableState* s, std::string input) {
+ size_t input_written = 0;
+ while (input_written < input.size()) {
+ DWORD wsize = 0;
+ if (::WriteFile(*s->stdin_pipe.wr, input.data() + input_written,
+ static_cast<DWORD>(input.size() - input_written), &wsize,
+ nullptr)) {
+ input_written += wsize;
+ } else {
+ // ERROR_BROKEN_PIPE is WAI when the child just closes stdin and stops
+ // accepting input.
+ auto err = ::GetLastError();
+ if (err != ERROR_BROKEN_PIPE)
+ PERFETTO_PLOG("Subprocess WriteFile(stdin) failed %lx", err);
+ break;
+ }
+ } // while(...)
+ std::unique_lock<std::mutex> lock(s->mutex);
+ s->stdin_pipe.wr.reset();
+}
+
+// static
+void Subprocess::StdoutErrThread(MovableState* s) {
+ char buf[4096];
+ for (;;) {
+ DWORD rsize = 0;
+ bool res =
+ ::ReadFile(*s->stdouterr_pipe.rd, buf, sizeof(buf), &rsize, nullptr);
+ if (!res) {
+ auto err = GetLastError();
+ if (err != ERROR_BROKEN_PIPE)
+ PERFETTO_PLOG("Subprocess ReadFile(stdouterr) failed %ld", err);
+ }
+
+ if (rsize > 0) {
+ std::unique_lock<std::mutex> lock(s->mutex);
+ s->locked_outerr_buf.append(buf, static_cast<size_t>(rsize));
+ } else { // EOF or some error.
+ break;
+ }
+ } // For(..)
+
+ // Close the stdouterr_pipe. The main loop looks at the pipe closure to
+ // determine whether the stdout/err thread has completed.
+ {
+ std::unique_lock<std::mutex> lock(s->mutex);
+ s->stdouterr_pipe.rd.reset();
+ }
+ s->stdouterr_done_event.Notify();
+}
+
+Subprocess::Status Subprocess::Poll() {
+ if (s_->status != kRunning)
+ return s_->status; // Nothing to poll.
+ Wait(1 /*ms*/);
+ return s_->status;
+}
+
+bool Subprocess::Wait(int timeout_ms) {
+ PERFETTO_CHECK(s_->status != kNotStarted);
+ const bool wait_forever = timeout_ms == 0;
+ const int64_t wait_start_ms = base::GetWallTimeMs().count();
+
+ // Break out of the loop only after both conditions are satisfied:
+ // - All stdout/stderr data has been read (if OutputMode::kBuffer).
+ // - The process exited.
+ // Note that the two events can happen arbitrary order. After the process
+ // exits, there might be still data in the pipe buffer, which we want to
+ // read fully.
+ // Note also that stdout/err might be "complete" before starting, if neither
+ // is operating in OutputMode::kBuffer mode. In that case we just want to wait
+ // for the process termination.
+ //
+ // Instead, don't wait on the stdin to be fully written. The child process
+ // might exit prematurely (or crash). If that happens, we can end up in a
+ // state where the write(stdin_pipe_.wr) will never unblock.
+ bool stdouterr_complete = false;
+ for (;;) {
+ HANDLE wait_handles[2]{};
+ DWORD num_handles = 0;
+
+ // Check if the process exited.
+ bool process_exited = !s_->win_proc_handle;
+ if (!process_exited) {
+ DWORD exit_code = STILL_ACTIVE;
+ PERFETTO_CHECK(::GetExitCodeProcess(*s_->win_proc_handle, &exit_code));
+ if (exit_code != STILL_ACTIVE) {
+ s_->returncode = static_cast<int>(exit_code);
+ s_->status = kTerminated;
+ s_->win_proc_handle.reset();
+ s_->win_thread_handle.reset();
+ process_exited = true;
+ }
+ } else {
+ PERFETTO_DCHECK(s_->status != kRunning);
+ }
+ if (!process_exited) {
+ wait_handles[num_handles++] = *s_->win_proc_handle;
+ }
+
+ // Check if there is more output and if the stdout/err pipe has been closed.
+ {
+ std::unique_lock<std::mutex> lock(s_->mutex);
+ // Move the output from the internal buffer shared with the
+ // stdouterr_thread to the final buffer exposed to the client.
+ if (!s_->locked_outerr_buf.empty()) {
+ s_->output.append(std::move(s_->locked_outerr_buf));
+ s_->locked_outerr_buf.clear();
+ }
+ stdouterr_complete = !s_->stdouterr_pipe.rd;
+ if (!stdouterr_complete) {
+ wait_handles[num_handles++] = s_->stdouterr_done_event.fd();
+ }
+ } // lock(s_->mutex)
+
+ if (num_handles == 0) {
+ PERFETTO_DCHECK(process_exited && stdouterr_complete);
+ break;
+ }
+
+ DWORD wait_ms; // Note: DWORD is unsigned.
+ if (wait_forever) {
+ wait_ms = INFINITE;
+ } else {
+ const int64_t now = GetWallTimeMs().count();
+ const int64_t wait_left_ms = timeout_ms - (now - wait_start_ms);
+ if (wait_left_ms <= 0)
+ return false; // Timed out
+ wait_ms = static_cast<DWORD>(wait_left_ms);
+ }
+
+ auto wait_res =
+ ::WaitForMultipleObjects(num_handles, wait_handles, false, wait_ms);
+ PERFETTO_CHECK(wait_res != WAIT_FAILED);
+ }
+
+ PERFETTO_DCHECK(!s_->win_proc_handle);
+ PERFETTO_DCHECK(!s_->win_thread_handle);
+
+ if (s_->stdin_thread.joinable()) // Might not exist if CreateProcess failed.
+ s_->stdin_thread.join();
+ if (s_->stdouterr_thread.joinable())
+ s_->stdouterr_thread.join();
+
+ // The stdin pipe is closed by the dedicated stdin thread. However if that is
+ // not started (e.g. because of no redirection) force close it now. Needs to
+ // happen after the join() to be thread safe.
+ s_->stdin_pipe.wr.reset();
+ s_->stdouterr_pipe.rd.reset();
+
+ return true;
+}
+
+void Subprocess::KillAndWaitForTermination(int exit_code) {
+ auto code = exit_code ? static_cast<DWORD>(exit_code) : STATUS_CONTROL_C_EXIT;
+ ::TerminateProcess(*s_->win_proc_handle, code);
+ Wait();
+ // TryReadExitStatus must have joined the threads.
+ PERFETTO_DCHECK(!s_->stdin_thread.joinable());
+ PERFETTO_DCHECK(!s_->stdouterr_thread.joinable());
+}
+
+} // namespace base
+} // namespace perfetto
+
+#endif // PERFETTO_OS_WIN
+// gen_amalgamated begin source: src/protozero/field.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
+
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+
+#if !PERFETTO_IS_LITTLE_ENDIAN()
+// The memcpy() for fixed32/64 below needs to be adjusted if we want to
+// support big endian CPUs. There doesn't seem to be a compelling need today.
+#error Unimplemented for big endian archs.
+#endif
+
+namespace protozero {
+
+template <typename Container>
+void Field::SerializeAndAppendToInternal(Container* dst) const {
+ namespace pu = proto_utils;
+ size_t initial_size = dst->size();
+ dst->resize(initial_size + pu::kMaxSimpleFieldEncodedSize + size_);
+ uint8_t* start = reinterpret_cast<uint8_t*>(&(*dst)[initial_size]);
+ uint8_t* wptr = start;
+ switch (type_) {
+ case static_cast<int>(pu::ProtoWireType::kVarInt): {
+ wptr = pu::WriteVarInt(pu::MakeTagVarInt(id_), wptr);
+ wptr = pu::WriteVarInt(int_value_, wptr);
+ break;
+ }
+ case static_cast<int>(pu::ProtoWireType::kFixed32): {
+ wptr = pu::WriteVarInt(pu::MakeTagFixed<uint32_t>(id_), wptr);
+ uint32_t value32 = static_cast<uint32_t>(int_value_);
+ memcpy(wptr, &value32, sizeof(value32));
+ wptr += sizeof(uint32_t);
+ break;
+ }
+ case static_cast<int>(pu::ProtoWireType::kFixed64): {
+ wptr = pu::WriteVarInt(pu::MakeTagFixed<uint64_t>(id_), wptr);
+ memcpy(wptr, &int_value_, sizeof(int_value_));
+ wptr += sizeof(uint64_t);
+ break;
+ }
+ case static_cast<int>(pu::ProtoWireType::kLengthDelimited): {
+ ConstBytes payload = as_bytes();
+ wptr = pu::WriteVarInt(pu::MakeTagLengthDelimited(id_), wptr);
+ wptr = pu::WriteVarInt(payload.size, wptr);
+ memcpy(wptr, payload.data, payload.size);
+ wptr += payload.size;
+ break;
+ }
+ default:
+ PERFETTO_FATAL("Unknown field type %d", type_);
+ }
+ size_t written_size = static_cast<size_t>(wptr - start);
+ PERFETTO_DCHECK(written_size > 0 && written_size < pu::kMaxMessageLength);
+ PERFETTO_DCHECK(initial_size + written_size <= dst->size());
+ dst->resize(initial_size + written_size);
+}
+
+void Field::SerializeAndAppendTo(std::string* dst) const {
+ SerializeAndAppendToInternal(dst);
+}
+
+void Field::SerializeAndAppendTo(std::vector<uint8_t>* dst) const {
+ SerializeAndAppendToInternal(dst);
+}
+
+} // namespace protozero
+// gen_amalgamated begin source: src/protozero/gen_field_helpers.cc
+/*
+ * Copyright (C) 2023 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/protozero/gen_field_helpers.h"
+
+namespace protozero {
+namespace internal {
+namespace gen_helpers {
+
+void DeserializeString(const protozero::Field& field, std::string* dst) {
+ field.get(dst);
+}
+
+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+ uint64_t>(const protozero::Field& field,
+ std::vector<uint64_t>* dst);
+
+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+ int64_t>(const protozero::Field& field,
+ std::vector<int64_t>* dst);
+
+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+ uint32_t>(const protozero::Field& field,
+ std::vector<uint32_t>* dst);
+
+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+ int32_t>(const protozero::Field& field,
+ std::vector<int32_t>* dst);
+
+void SerializeTinyVarInt(uint32_t field_id, bool value, Message* msg) {
+ msg->AppendTinyVarInt(field_id, value);
+}
+
+template void SerializeExtendedVarInt<uint64_t>(uint32_t field_id,
+ uint64_t value,
+ Message* msg);
+
+template void SerializeExtendedVarInt<uint32_t>(uint32_t field_id,
+ uint32_t value,
+ Message* msg);
+
+template void SerializeFixed<double>(uint32_t field_id,
+ double value,
+ Message* msg);
+
+template void SerializeFixed<float>(uint32_t field_id,
+ float value,
+ Message* msg);
+
+template void SerializeFixed<uint64_t>(uint32_t field_id,
+ uint64_t value,
+ Message* msg);
+
+template void SerializeFixed<int64_t>(uint32_t field_id,
+ int64_t value,
+ Message* msg);
+
+template void SerializeFixed<uint32_t>(uint32_t field_id,
+ uint32_t value,
+ Message* msg);
+
+template void SerializeFixed<int32_t>(uint32_t field_id,
+ int32_t value,
+ Message* msg);
+
+void SerializeString(uint32_t field_id,
+ const std::string& value,
+ Message* msg) {
+ msg->AppendString(field_id, value);
+}
+
+void SerializeUnknownFields(const std::string& unknown_fields, Message* msg) {
+ msg->AppendRawProtoBytes(unknown_fields.data(), unknown_fields.size());
+}
+
+MessageSerializer::MessageSerializer() = default;
+
+MessageSerializer::~MessageSerializer() = default;
+
+std::vector<uint8_t> MessageSerializer::SerializeAsArray() {
+ return msg_.SerializeAsArray();
+}
+
+std::string MessageSerializer::SerializeAsString() {
+ return msg_.SerializeAsString();
+}
+
+template bool EqualsField<std::string>(const std::string&, const std::string&);
+
+} // namespace gen_helpers
+} // namespace internal
+} // namespace protozero
+// gen_amalgamated begin source: src/protozero/message.cc
+/*
+ * Copyright (C) 2017 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/protozero/message.h"
+
+#include <atomic>
+#include <type_traits>
+
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message_arena.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message_handle.h"
+
+#if !PERFETTO_IS_LITTLE_ENDIAN()
+// The memcpy() for float and double below needs to be adjusted if we want to
+// support big endian CPUs. There doesn't seem to be a compelling need today.
+#error Unimplemented for big endian archs.
+#endif
+
+namespace protozero {
+
+namespace {
+
+constexpr int kBytesToCompact = proto_utils::kMessageLengthFieldSize - 1u;
+
+#if PERFETTO_DCHECK_IS_ON()
+std::atomic<uint32_t> g_generation;
+#endif
+
+} // namespace
+
+// Do NOT put any code in the constructor or use default initialization.
+// Use the Reset() method below instead.
+
+// This method is called to initialize both root and nested messages.
+void Message::Reset(ScatteredStreamWriter* stream_writer, MessageArena* arena) {
+// Older versions of libstdcxx don't have is_trivially_constructible.
+#if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20170516
+ static_assert(std::is_trivially_constructible<Message>::value,
+ "Message must be trivially constructible");
+#endif
+
+ static_assert(std::is_trivially_destructible<Message>::value,
+ "Message must be trivially destructible");
+ stream_writer_ = stream_writer;
+ arena_ = arena;
+ size_ = 0;
+ size_field_ = nullptr;
+ nested_message_ = nullptr;
+ message_state_ = MessageState::kNotFinalized;
+#if PERFETTO_DCHECK_IS_ON()
+ handle_ = nullptr;
+ generation_ = g_generation.fetch_add(1, std::memory_order_relaxed);
+#endif
+}
+
+void Message::AppendString(uint32_t field_id, const char* str) {
+ AppendBytes(field_id, str, strlen(str));
+}
+
+void Message::AppendBytes(uint32_t field_id, const void* src, size_t size) {
+ PERFETTO_DCHECK(field_id);
+ if (nested_message_)
+ EndNestedMessage();
+
+ PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
+ // Write the proto preamble (field id, type and length of the field).
+ uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
+ uint8_t* pos = buffer;
+ pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
+ pos);
+ pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
+ WriteToStream(buffer, pos);
+
+ const uint8_t* src_u8 = reinterpret_cast<const uint8_t*>(src);
+ WriteToStream(src_u8, src_u8 + size);
+}
+
+size_t Message::AppendScatteredBytes(uint32_t field_id,
+ ContiguousMemoryRange* ranges,
+ size_t num_ranges) {
+ PERFETTO_DCHECK(field_id);
+ if (nested_message_)
+ EndNestedMessage();
+
+ size_t size = 0;
+ for (size_t i = 0; i < num_ranges; ++i) {
+ size += ranges[i].size();
+ }
+
+ PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
+
+ uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
+ uint8_t* pos = buffer;
+ pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
+ pos);
+ pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
+ WriteToStream(buffer, pos);
+
+ for (size_t i = 0; i < num_ranges; ++i) {
+ auto& range = ranges[i];
+ WriteToStream(range.begin, range.end);
+ }
+
+ return size;
+}
+
+uint32_t Message::Finalize() {
+ if (is_finalized())
+ return size_;
+
+ if (nested_message_)
+ EndNestedMessage();
+
+ // Write the length of the nested message a posteriori, using a leading-zero
+ // redundant varint encoding. This can be nullptr for the root message, among
+ // many reasons, because the TraceWriterImpl delegate is keeping track of the
+ // root fragment size independently.
+ if (size_field_) {
+ PERFETTO_DCHECK(!is_finalized());
+ PERFETTO_DCHECK(size_ < proto_utils::kMaxMessageLength);
+ //
+ // Normally the size of a protozero message is written with 4 bytes just
+ // before the contents of the message itself:
+ //
+ // size message data
+ // [aa bb cc dd] [01 23 45 67 ...]
+ //
+ // We always reserve 4 bytes for the size, because the real size of the
+ // message isn't known until the call to Finalize(). This is possible
+ // because we can use leading zero redundant varint coding to expand any
+ // size smaller than 256 MiB to 4 bytes.
+ //
+ // However this is wasteful for short, frequently written messages, so the
+ // code below uses a 1 byte size field when possible. This is done by
+ // shifting the already-written data (which should still be in the cache)
+ // back by 3 bytes, resulting in this layout:
+ //
+ // size message data
+ // [aa] [01 23 45 67 ...]
+ //
+ // We can only do this optimization if the message is contained in a single
+ // chunk (since we can't modify previously committed chunks). We can check
+ // this by verifying that the size field is immediately before the message
+ // in memory and is fully contained by the current chunk.
+ //
+ if (PERFETTO_LIKELY(size_ <= proto_utils::kMaxOneByteMessageLength &&
+ size_field_ ==
+ stream_writer_->write_ptr() - size_ -
+ proto_utils::kMessageLengthFieldSize &&
+ size_field_ >= stream_writer_->cur_range().begin)) {
+ stream_writer_->Rewind(size_, kBytesToCompact);
+ PERFETTO_DCHECK(size_field_ == stream_writer_->write_ptr() - size_ - 1u);
+ *size_field_ = static_cast<uint8_t>(size_);
+ message_state_ = MessageState::kFinalizedWithCompaction;
+ } else {
+ proto_utils::WriteRedundantVarInt(size_, size_field_);
+ message_state_ = MessageState::kFinalized;
+ }
+ size_field_ = nullptr;
+ } else {
+ message_state_ = MessageState::kFinalized;
+ }
+
+#if PERFETTO_DCHECK_IS_ON()
+ if (handle_)
+ handle_->reset_message();
+#endif
+
+ return size_;
+}
+
+Message* Message::BeginNestedMessageInternal(uint32_t field_id) {
+ PERFETTO_DCHECK(field_id);
+ if (nested_message_)
+ EndNestedMessage();
+
+ // Write the proto preamble for the nested message.
+ uint8_t data[proto_utils::kMaxTagEncodedSize];
+ uint8_t* data_end = proto_utils::WriteVarInt(
+ proto_utils::MakeTagLengthDelimited(field_id), data);
+ WriteToStream(data, data_end);
+
+ Message* message = arena_->NewMessage();
+ message->Reset(stream_writer_, arena_);
+
+ // The length of the nested message cannot be known upfront. So right now
+ // just reserve the bytes to encode the size after the nested message is done.
+ message->set_size_field(
+ stream_writer_->ReserveBytes(proto_utils::kMessageLengthFieldSize));
+ size_ += proto_utils::kMessageLengthFieldSize;
+
+ nested_message_ = message;
+ return message;
+}
+
+void Message::EndNestedMessage() {
+ size_ += nested_message_->Finalize();
+ if (nested_message_->message_state_ ==
+ MessageState::kFinalizedWithCompaction) {
+ size_ -= kBytesToCompact;
+ }
+ arena_->DeleteLastMessage(nested_message_);
+ nested_message_ = nullptr;
+}
+
+} // namespace protozero
+// gen_amalgamated begin source: src/protozero/message_arena.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/protozero/message_arena.h"
+
+#include <atomic>
+#include <type_traits>
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message_handle.h"
+
+namespace protozero {
+
+MessageArena::MessageArena() {
+ // The code below assumes that there is always at least one block.
+ blocks_.emplace_front();
+ static_assert(
+ std::alignment_of<decltype(blocks_.front().storage[0])>::value >=
+ alignof(Message),
+ "MessageArea's storage is not properly aligned");
+}
+
+MessageArena::~MessageArena() = default;
+
+Message* MessageArena::NewMessage() {
+ PERFETTO_DCHECK(!blocks_.empty()); // Should never become empty.
+
+ Block* block = &blocks_.front();
+ if (PERFETTO_UNLIKELY(block->entries >= Block::kCapacity)) {
+ blocks_.emplace_front();
+ block = &blocks_.front();
+ }
+ const auto idx = block->entries++;
+ void* storage = &block->storage[idx];
+ PERFETTO_ASAN_UNPOISON(storage, sizeof(Message));
+ return new (storage) Message();
+}
+
+void MessageArena::DeleteLastMessageInternal() {
+ PERFETTO_DCHECK(!blocks_.empty()); // Should never be empty, see below.
+ Block* block = &blocks_.front();
+ PERFETTO_DCHECK(block->entries > 0);
+
+ // This is the reason why there is no ~Message() call here.
+ // MessageArea::Reset() (see header) also relies on dtor being trivial.
+ static_assert(std::is_trivially_destructible<Message>::value,
+ "Message must be trivially destructible");
+
+ --block->entries;
+ PERFETTO_ASAN_POISON(&block->storage[block->entries], sizeof(Message));
+
+ // Don't remove the first block to avoid malloc/free calls when the root
+ // message is reset. Hitting the allocator all the times is a waste of time.
+ if (block->entries == 0 && std::next(blocks_.cbegin()) != blocks_.cend()) {
+ blocks_.pop_front();
+ }
+}
+
+} // namespace protozero
+// gen_amalgamated begin source: src/protozero/packed_repeated_fields.cc
+/*
+ * Copyright (C) 2017 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/protozero/packed_repeated_fields.h"
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+
+namespace protozero {
+
+void PackedBufferBase::GrowSlowpath() {
+ size_t write_off = static_cast<size_t>(write_ptr_ - storage_begin_);
+ size_t old_size = static_cast<size_t>(storage_end_ - storage_begin_);
+ size_t new_size = old_size < 65536 ? (old_size * 2) : (old_size * 3 / 2);
+ new_size = perfetto::base::AlignUp<4096>(new_size);
+ std::unique_ptr<uint8_t[]> new_buf(new uint8_t[new_size]);
+ memcpy(new_buf.get(), storage_begin_, old_size);
+ heap_buf_ = std::move(new_buf);
+ storage_begin_ = heap_buf_.get();
+ storage_end_ = storage_begin_ + new_size;
+ write_ptr_ = storage_begin_ + write_off;
+}
+
+void PackedBufferBase::Reset() {
+ heap_buf_.reset();
+ storage_begin_ = reinterpret_cast<uint8_t*>(&stack_buf_[0]);
+ storage_end_ = reinterpret_cast<uint8_t*>(&stack_buf_[kOnStackStorageSize]);
+ write_ptr_ = storage_begin_;
+}
+
+} // namespace protozero
+// gen_amalgamated begin source: src/protozero/proto_decoder.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+
+#include <string.h>
+
+#include <cinttypes>
+#include <limits>
+#include <memory>
+
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+
+namespace protozero {
+
+using namespace proto_utils;
+
+#if !PERFETTO_IS_LITTLE_ENDIAN()
+#error Unimplemented for big endian archs.
+#endif
+
+namespace {
+
+struct ParseFieldResult {
+ enum ParseResult { kAbort, kSkip, kOk };
+ ParseResult parse_res;
+ const uint8_t* next;
+ Field field;
+};
+
+// Parses one field and returns the field itself and a pointer to the next
+// field to parse. If parsing fails, the returned |next| == |buffer|.
+ParseFieldResult ParseOneField(const uint8_t* const buffer,
+ const uint8_t* const end) {
+ ParseFieldResult res{ParseFieldResult::kAbort, buffer, Field{}};
+
+ // The first byte of a proto field is structured as follows:
+ // The least 3 significant bits determine the field type.
+ // The most 5 significant bits determine the field id. If MSB == 1, the
+ // field id continues on the next bytes following the VarInt encoding.
+ const uint8_t kFieldTypeNumBits = 3;
+ const uint64_t kFieldTypeMask = (1 << kFieldTypeNumBits) - 1; // 0000 0111;
+ const uint8_t* pos = buffer;
+
+ // If we've already hit the end, just return an invalid field.
+ if (PERFETTO_UNLIKELY(pos >= end))
+ return res;
+
+ uint64_t preamble = 0;
+ if (PERFETTO_LIKELY(*pos < 0x80)) { // Fastpath for fields with ID < 16.
+ preamble = *(pos++);
+ } else {
+ const uint8_t* next = ParseVarInt(pos, end, &preamble);
+ if (PERFETTO_UNLIKELY(pos == next))
+ return res;
+ pos = next;
+ }
+
+ uint32_t field_id = static_cast<uint32_t>(preamble >> kFieldTypeNumBits);
+ if (field_id == 0 || pos >= end)
+ return res;
+
+ auto field_type = static_cast<uint8_t>(preamble & kFieldTypeMask);
+ const uint8_t* new_pos = pos;
+ uint64_t int_value = 0;
+ uint64_t size = 0;
+
+ switch (field_type) {
+ case static_cast<uint8_t>(ProtoWireType::kVarInt): {
+ new_pos = ParseVarInt(pos, end, &int_value);
+
+ // new_pos not being greater than pos means ParseVarInt could not fully
+ // parse the number. This is because we are out of space in the buffer.
+ // Set the id to zero and return but don't update the offset so a future
+ // read can read this field.
+ if (PERFETTO_UNLIKELY(new_pos == pos))
+ return res;
+
+ break;
+ }
+
+ case static_cast<uint8_t>(ProtoWireType::kLengthDelimited): {
+ uint64_t payload_length;
+ new_pos = ParseVarInt(pos, end, &payload_length);
+ if (PERFETTO_UNLIKELY(new_pos == pos))
+ return res;
+
+ // ParseVarInt guarantees that |new_pos| <= |end| when it succeeds;
+ if (payload_length > static_cast<uint64_t>(end - new_pos))
+ return res;
+
+ const uintptr_t payload_start = reinterpret_cast<uintptr_t>(new_pos);
+ int_value = payload_start;
+ size = payload_length;
+ new_pos += payload_length;
+ break;
+ }
+
+ case static_cast<uint8_t>(ProtoWireType::kFixed64): {
+ new_pos = pos + sizeof(uint64_t);
+ if (PERFETTO_UNLIKELY(new_pos > end))
+ return res;
+ memcpy(&int_value, pos, sizeof(uint64_t));
+ break;
+ }
+
+ case static_cast<uint8_t>(ProtoWireType::kFixed32): {
+ new_pos = pos + sizeof(uint32_t);
+ if (PERFETTO_UNLIKELY(new_pos > end))
+ return res;
+ memcpy(&int_value, pos, sizeof(uint32_t));
+ break;
+ }
+
+ default:
+ PERFETTO_DLOG("Invalid proto field type: %u", field_type);
+ return res;
+ }
+
+ res.next = new_pos;
+
+ if (PERFETTO_UNLIKELY(field_id > Field::kMaxId)) {
+ PERFETTO_DLOG("Skipping field %" PRIu32 " because its id > %" PRIu32,
+ field_id, Field::kMaxId);
+ res.parse_res = ParseFieldResult::kSkip;
+ return res;
+ }
+
+ if (PERFETTO_UNLIKELY(size > proto_utils::kMaxMessageLength)) {
+ PERFETTO_DLOG("Skipping field %" PRIu32 " because it's too big (%" PRIu64
+ " KB)",
+ field_id, size / 1024);
+ res.parse_res = ParseFieldResult::kSkip;
+ return res;
+ }
+
+ res.parse_res = ParseFieldResult::kOk;
+ res.field.initialize(field_id, field_type, int_value,
+ static_cast<uint32_t>(size));
+ return res;
+}
+
+} // namespace
+
+Field ProtoDecoder::FindField(uint32_t field_id) {
+ Field res{};
+ auto old_position = read_ptr_;
+ read_ptr_ = begin_;
+ for (auto f = ReadField(); f.valid(); f = ReadField()) {
+ if (f.id() == field_id) {
+ res = f;
+ break;
+ }
+ }
+ read_ptr_ = old_position;
+ return res;
+}
+
+Field ProtoDecoder::ReadField() {
+ ParseFieldResult res;
+ do {
+ res = ParseOneField(read_ptr_, end_);
+ read_ptr_ = res.next;
+ } while (PERFETTO_UNLIKELY(res.parse_res == ParseFieldResult::kSkip));
+ return res.field;
+}
+
+void TypedProtoDecoderBase::ParseAllFields() {
+ const uint8_t* cur = begin_;
+ ParseFieldResult res;
+ for (;;) {
+ res = ParseOneField(cur, end_);
+ PERFETTO_DCHECK(res.parse_res != ParseFieldResult::kOk || res.next != cur);
+ cur = res.next;
+ if (PERFETTO_UNLIKELY(res.parse_res == ParseFieldResult::kSkip))
+ continue;
+ if (PERFETTO_UNLIKELY(res.parse_res == ParseFieldResult::kAbort))
+ break;
+
+ PERFETTO_DCHECK(res.parse_res == ParseFieldResult::kOk);
+ PERFETTO_DCHECK(res.field.valid());
+ auto field_id = res.field.id();
+ if (PERFETTO_UNLIKELY(field_id >= num_fields_))
+ continue;
+
+ // There are two reasons why we might want to expand the heap capacity:
+ // 1. We are writing a non-repeated field, which has an id >
+ // INITIAL_STACK_CAPACITY. In this case ExpandHeapStorage() ensures to
+ // allocate at least (num_fields_ + 1) slots.
+ // 2. We are writing a repeated field but ran out of capacity.
+ if (PERFETTO_UNLIKELY(field_id >= size_ || size_ >= capacity_))
+ ExpandHeapStorage();
+
+ PERFETTO_DCHECK(field_id < size_);
+ Field* fld = &fields_[field_id];
+ if (PERFETTO_LIKELY(!fld->valid())) {
+ // This is the first time we see this field.
+ *fld = std::move(res.field);
+ } else {
+ // Repeated field case.
+ // In this case we need to:
+ // 1. Append the last value of the field to end of the repeated field
+ // storage.
+ // 2. Replace the default instance at offset |field_id| with the current
+ // value. This is because in case of repeated field a call to Get(X) is
+ // supposed to return the last value of X, not the first one.
+ // This is so that the RepeatedFieldIterator will iterate in the right
+ // order, see comments on RepeatedFieldIterator.
+ if (num_fields_ > size_) {
+ ExpandHeapStorage();
+ fld = &fields_[field_id];
+ }
+
+ PERFETTO_DCHECK(size_ < capacity_);
+ fields_[size_++] = *fld;
+ *fld = std::move(res.field);
+ }
+ }
+ read_ptr_ = res.next;
+}
+
+void TypedProtoDecoderBase::ExpandHeapStorage() {
+ // When we expand the heap we must ensure that we have at very last capacity
+ // to deal with all known fields plus at least one repeated field. We go +2048
+ // here based on observations on a large 4GB android trace. This is to avoid
+ // trivial re-allocations when dealing with repeated fields of a message that
+ // has > INITIAL_STACK_CAPACITY fields.
+ const uint32_t min_capacity = num_fields_ + 2048; // Any num >= +1 will do.
+ const uint32_t new_capacity = std::max(capacity_ * 2, min_capacity);
+ PERFETTO_CHECK(new_capacity > size_ && new_capacity > num_fields_);
+ std::unique_ptr<Field[]> new_storage(new Field[new_capacity]);
+
+ static_assert(std::is_trivially_constructible<Field>::value,
+ "Field must be trivially constructible");
+ static_assert(std::is_trivially_copyable<Field>::value,
+ "Field must be trivially copyable");
+
+ // Zero-initialize the slots for known field IDs slots, as they can be
+ // randomly accessed. Instead, there is no need to initialize the repeated
+ // slots, because they are written linearly with no gaps and are always
+ // initialized before incrementing |size_|.
+ const uint32_t new_size = std::max(size_, num_fields_);
+ memset(&new_storage[size_], 0, sizeof(Field) * (new_size - size_));
+
+ memcpy(&new_storage[0], fields_, sizeof(Field) * size_);
+
+ heap_storage_ = std::move(new_storage);
+ fields_ = &heap_storage_[0];
+ capacity_ = new_capacity;
+ size_ = new_size;
+}
+
+} // namespace protozero
+// gen_amalgamated begin source: src/protozero/scattered_heap_buffer.cc
+/*
+ * Copyright (C) 2017 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/protozero/scattered_heap_buffer.h"
+
+#include <algorithm>
+
+namespace protozero {
+
+ScatteredHeapBuffer::Slice::Slice()
+ : buffer_(nullptr), size_(0u), unused_bytes_(0u) {}
+
+ScatteredHeapBuffer::Slice::Slice(size_t size)
+ : buffer_(std::unique_ptr<uint8_t[]>(new uint8_t[size])),
+ size_(size),
+ unused_bytes_(size) {
+ PERFETTO_DCHECK(size);
+ Clear();
+}
+
+ScatteredHeapBuffer::Slice::Slice(Slice&& slice) noexcept = default;
+
+ScatteredHeapBuffer::Slice::~Slice() = default;
+
+ScatteredHeapBuffer::Slice& ScatteredHeapBuffer::Slice::operator=(Slice&&) =
+ default;
+
+void ScatteredHeapBuffer::Slice::Clear() {
+ unused_bytes_ = size_;
+#if PERFETTO_DCHECK_IS_ON()
+ memset(start(), 0xff, size_);
+#endif // PERFETTO_DCHECK_IS_ON()
+}
+
+ScatteredHeapBuffer::ScatteredHeapBuffer(size_t initial_slice_size_bytes,
+ size_t maximum_slice_size_bytes)
+ : next_slice_size_(initial_slice_size_bytes),
+ maximum_slice_size_(maximum_slice_size_bytes) {
+ PERFETTO_DCHECK(next_slice_size_ && maximum_slice_size_);
+ PERFETTO_DCHECK(maximum_slice_size_ >= initial_slice_size_bytes);
+}
+
+ScatteredHeapBuffer::~ScatteredHeapBuffer() = default;
+
+protozero::ContiguousMemoryRange ScatteredHeapBuffer::GetNewBuffer() {
+ PERFETTO_CHECK(writer_);
+ AdjustUsedSizeOfCurrentSlice();
+
+ if (cached_slice_.start()) {
+ slices_.push_back(std::move(cached_slice_));
+ PERFETTO_DCHECK(!cached_slice_.start());
+ } else {
+ slices_.emplace_back(next_slice_size_);
+ }
+ next_slice_size_ = std::min(maximum_slice_size_, next_slice_size_ * 2);
+ return slices_.back().GetTotalRange();
+}
+
+const std::vector<ScatteredHeapBuffer::Slice>&
+ScatteredHeapBuffer::GetSlices() {
+ AdjustUsedSizeOfCurrentSlice();
+ return slices_;
+}
+
+std::vector<uint8_t> ScatteredHeapBuffer::StitchSlices() {
+ size_t stitched_size = 0u;
+ const auto& slices = GetSlices();
+ for (const auto& slice : slices)
+ stitched_size += slice.size() - slice.unused_bytes();
+
+ std::vector<uint8_t> buffer;
+ buffer.reserve(stitched_size);
+ for (const auto& slice : slices) {
+ auto used_range = slice.GetUsedRange();
+ buffer.insert(buffer.end(), used_range.begin, used_range.end);
+ }
+ return buffer;
+}
+
+std::vector<protozero::ContiguousMemoryRange> ScatteredHeapBuffer::GetRanges() {
+ std::vector<protozero::ContiguousMemoryRange> ranges;
+ for (const auto& slice : GetSlices())
+ ranges.push_back(slice.GetUsedRange());
+ return ranges;
+}
+
+void ScatteredHeapBuffer::AdjustUsedSizeOfCurrentSlice() {
+ if (!slices_.empty())
+ slices_.back().set_unused_bytes(writer_->bytes_available());
+}
+
+size_t ScatteredHeapBuffer::GetTotalSize() {
+ size_t total_size = 0;
+ for (auto& slice : slices_) {
+ total_size += slice.size();
+ }
+ return total_size;
+}
+
+void ScatteredHeapBuffer::Reset() {
+ if (slices_.empty())
+ return;
+ cached_slice_ = std::move(slices_.front());
+ cached_slice_.Clear();
+ slices_.clear();
+}
+
+} // namespace protozero
+// gen_amalgamated begin source: src/protozero/scattered_stream_null_delegate.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_stream_null_delegate.h"
+
+namespace protozero {
+
+// An implementation of ScatteredStreamWriter::Delegate which always returns
+// the same piece of memory.
+// This is used when we need to no-op the writers (e.g. during teardown or in
+// case of resource exhaustion), avoiding that the clients have to deal with
+// nullptr checks.
+ScatteredStreamWriterNullDelegate::ScatteredStreamWriterNullDelegate(
+ size_t chunk_size)
+ : chunk_size_(chunk_size),
+ chunk_(std::unique_ptr<uint8_t[]>(new uint8_t[chunk_size_])) {}
+
+ScatteredStreamWriterNullDelegate::~ScatteredStreamWriterNullDelegate() {}
+
+ContiguousMemoryRange ScatteredStreamWriterNullDelegate::GetNewBuffer() {
+ return {chunk_.get(), chunk_.get() + chunk_size_};
+}
+
+} // namespace protozero
+// gen_amalgamated begin source: src/protozero/scattered_stream_writer.cc
+/*
+ * Copyright (C) 2017 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/protozero/scattered_stream_writer.h"
+
+#include <algorithm>
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+
+namespace protozero {
+
+ScatteredStreamWriter::Delegate::~Delegate() {}
+
+uint8_t* ScatteredStreamWriter::Delegate::AnnotatePatch(uint8_t* patch_addr) {
+ // In most cases, a patch is transparent. The caller can write directly into
+ // `to_patch`, because its memory is not going away. TraceWriterImpl, however,
+ // requires a more complicated logic, because the chunks might be copied
+ // earlier.
+ return patch_addr;
+}
+
+ScatteredStreamWriter::ScatteredStreamWriter(Delegate* delegate)
+ : delegate_(delegate),
+ cur_range_({nullptr, nullptr}),
+ write_ptr_(nullptr) {}
+
+ScatteredStreamWriter::~ScatteredStreamWriter() {}
+
+void ScatteredStreamWriter::Reset(ContiguousMemoryRange range) {
+ written_previously_ += static_cast<uint64_t>(write_ptr_ - cur_range_.begin);
+ cur_range_ = range;
+ write_ptr_ = range.begin;
+ PERFETTO_DCHECK(!write_ptr_ || write_ptr_ < cur_range_.end);
+}
+
+void ScatteredStreamWriter::Extend() {
+ Reset(delegate_->GetNewBuffer());
+}
+
+void ScatteredStreamWriter::WriteBytesSlowPath(const uint8_t* src,
+ size_t size) {
+ size_t bytes_left = size;
+ while (bytes_left > 0) {
+ if (write_ptr_ >= cur_range_.end)
+ Extend();
+ const size_t burst_size = std::min(bytes_available(), bytes_left);
+ WriteBytesUnsafe(src, burst_size);
+ bytes_left -= burst_size;
+ src += burst_size;
+ }
+}
+
+// TODO(primiano): perf optimization: I suspect that at the end this will always
+// be called with |size| == 4, in which case we might just hardcode it.
+uint8_t* ScatteredStreamWriter::ReserveBytes(size_t size) {
+ if (write_ptr_ + size > cur_range_.end) {
+ // Assume the reservations are always < Delegate::GetNewBuffer().size(),
+ // so that one single call to Extend() will definitely give enough headroom.
+ Extend();
+ PERFETTO_DCHECK(write_ptr_ + size <= cur_range_.end);
+ }
+ uint8_t* begin = write_ptr_;
+ write_ptr_ += size;
+#if PERFETTO_DCHECK_IS_ON()
+ // In the past, the service had a matching DCHECK in
+ // TraceBuffer::TryPatchChunkContents, which was assuming that service and all
+ // producers are built with matching DCHECK levels. This turned out to be a
+ // source of problems and was removed in b/197340286. This memset is useless
+ // these days and is here only to maintain ABI compatibility between producers
+ // that use a v20+ SDK and older versions of the service that were built in
+ // debug mode. At some point around 2023 it should be safe to remove it.
+ // (running a debug version of traced in production seems a bad idea
+ // regardless).
+ memset(begin, 0, size);
+#endif
+ return begin;
+}
+
+} // namespace protozero
+// gen_amalgamated begin source: src/protozero/static_buffer.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/protozero/static_buffer.h"
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+
+namespace protozero {
+
+StaticBufferDelegate::~StaticBufferDelegate() = default;
+
+ContiguousMemoryRange StaticBufferDelegate::GetNewBuffer() {
+ if (get_new_buffer_called_once_) {
+ // This is the 2nd time GetNewBuffer is called. The estimate is wrong. We
+ // shouldn't try to grow the buffer after the initial call.
+ PERFETTO_FATAL("Static buffer too small");
+ }
+ get_new_buffer_called_once_ = true;
+ return range_;
+}
+
+} // namespace protozero
+// gen_amalgamated begin source: src/protozero/virtual_destructors.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/protozero/cpp_message_obj.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message_handle.h"
+
+namespace protozero {
+
+CppMessageObj::~CppMessageObj() = default;
+MessageFinalizationListener::~MessageFinalizationListener() = default;
+
+} // namespace protozero
+// gen_amalgamated begin source: gen/protos/perfetto/common/android_energy_consumer_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/android_energy_consumer_descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+AndroidEnergyConsumerDescriptor::AndroidEnergyConsumerDescriptor() = default;
+AndroidEnergyConsumerDescriptor::~AndroidEnergyConsumerDescriptor() = default;
+AndroidEnergyConsumerDescriptor::AndroidEnergyConsumerDescriptor(const AndroidEnergyConsumerDescriptor&) = default;
+AndroidEnergyConsumerDescriptor& AndroidEnergyConsumerDescriptor::operator=(const AndroidEnergyConsumerDescriptor&) = default;
+AndroidEnergyConsumerDescriptor::AndroidEnergyConsumerDescriptor(AndroidEnergyConsumerDescriptor&&) noexcept = default;
+AndroidEnergyConsumerDescriptor& AndroidEnergyConsumerDescriptor::operator=(AndroidEnergyConsumerDescriptor&&) = default;
+
+bool AndroidEnergyConsumerDescriptor::operator==(const AndroidEnergyConsumerDescriptor& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(energy_consumers_, other.energy_consumers_);
+}
+
+int AndroidEnergyConsumerDescriptor::energy_consumers_size() const { return static_cast<int>(energy_consumers_.size()); }
+void AndroidEnergyConsumerDescriptor::clear_energy_consumers() { energy_consumers_.clear(); }
+AndroidEnergyConsumer* AndroidEnergyConsumerDescriptor::add_energy_consumers() { energy_consumers_.emplace_back(); return &energy_consumers_.back(); }
+bool AndroidEnergyConsumerDescriptor::ParseFromArray(const void* raw, size_t size) {
+ energy_consumers_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* energy_consumers */:
+ energy_consumers_.emplace_back();
+ energy_consumers_.back().ParseFromArray(field.data(), field.size());
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string AndroidEnergyConsumerDescriptor::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> AndroidEnergyConsumerDescriptor::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void AndroidEnergyConsumerDescriptor::Serialize(::protozero::Message* msg) const {
+ // Field 1: energy_consumers
+ for (auto& it : energy_consumers_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+AndroidEnergyConsumer::AndroidEnergyConsumer() = default;
+AndroidEnergyConsumer::~AndroidEnergyConsumer() = default;
+AndroidEnergyConsumer::AndroidEnergyConsumer(const AndroidEnergyConsumer&) = default;
+AndroidEnergyConsumer& AndroidEnergyConsumer::operator=(const AndroidEnergyConsumer&) = default;
+AndroidEnergyConsumer::AndroidEnergyConsumer(AndroidEnergyConsumer&&) noexcept = default;
+AndroidEnergyConsumer& AndroidEnergyConsumer::operator=(AndroidEnergyConsumer&&) = default;
+
+bool AndroidEnergyConsumer::operator==(const AndroidEnergyConsumer& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(energy_consumer_id_, other.energy_consumer_id_)
+ && ::protozero::internal::gen_helpers::EqualsField(ordinal_, other.ordinal_)
+ && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+}
+
+bool AndroidEnergyConsumer::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* energy_consumer_id */:
+ field.get(&energy_consumer_id_);
+ break;
+ case 2 /* ordinal */:
+ field.get(&ordinal_);
+ break;
+ case 3 /* type */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &type_);
+ break;
+ case 4 /* name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string AndroidEnergyConsumer::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> AndroidEnergyConsumer::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void AndroidEnergyConsumer::Serialize(::protozero::Message* msg) const {
+ // Field 1: energy_consumer_id
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(1, energy_consumer_id_, msg);
+ }
+
+ // Field 2: ordinal
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(2, ordinal_, msg);
+ }
+
+ // Field 3: type
+ if (_has_field_[3]) {
+ ::protozero::internal::gen_helpers::SerializeString(3, type_, msg);
+ }
+
+ // Field 4: name
+ if (_has_field_[4]) {
+ ::protozero::internal::gen_helpers::SerializeString(4, name_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+} // namespace perfetto
+} // namespace protos
+} // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/android_log_constants.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/android_log_constants.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+} // namespace perfetto
+} // namespace protos
+} // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/builtin_clock.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/builtin_clock.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+} // namespace perfetto
+} // namespace protos
+} // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/commit_data_request.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/commit_data_request.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+CommitDataRequest::CommitDataRequest() = default;
+CommitDataRequest::~CommitDataRequest() = default;
+CommitDataRequest::CommitDataRequest(const CommitDataRequest&) = default;
+CommitDataRequest& CommitDataRequest::operator=(const CommitDataRequest&) = default;
+CommitDataRequest::CommitDataRequest(CommitDataRequest&&) noexcept = default;
+CommitDataRequest& CommitDataRequest::operator=(CommitDataRequest&&) = default;
+
+bool CommitDataRequest::operator==(const CommitDataRequest& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(chunks_to_move_, other.chunks_to_move_)
+ && ::protozero::internal::gen_helpers::EqualsField(chunks_to_patch_, other.chunks_to_patch_)
+ && ::protozero::internal::gen_helpers::EqualsField(flush_request_id_, other.flush_request_id_);
+}
+
+int CommitDataRequest::chunks_to_move_size() const { return static_cast<int>(chunks_to_move_.size()); }
+void CommitDataRequest::clear_chunks_to_move() { chunks_to_move_.clear(); }
+CommitDataRequest_ChunksToMove* CommitDataRequest::add_chunks_to_move() { chunks_to_move_.emplace_back(); return &chunks_to_move_.back(); }
+int CommitDataRequest::chunks_to_patch_size() const { return static_cast<int>(chunks_to_patch_.size()); }
+void CommitDataRequest::clear_chunks_to_patch() { chunks_to_patch_.clear(); }
+CommitDataRequest_ChunkToPatch* CommitDataRequest::add_chunks_to_patch() { chunks_to_patch_.emplace_back(); return &chunks_to_patch_.back(); }
+bool CommitDataRequest::ParseFromArray(const void* raw, size_t size) {
+ chunks_to_move_.clear();
+ chunks_to_patch_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* chunks_to_move */:
+ chunks_to_move_.emplace_back();
+ chunks_to_move_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 2 /* chunks_to_patch */:
+ chunks_to_patch_.emplace_back();
+ chunks_to_patch_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 3 /* flush_request_id */:
+ field.get(&flush_request_id_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string CommitDataRequest::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> CommitDataRequest::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void CommitDataRequest::Serialize(::protozero::Message* msg) const {
+ // Field 1: chunks_to_move
+ for (auto& it : chunks_to_move_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+ }
+
+ // Field 2: chunks_to_patch
+ for (auto& it : chunks_to_patch_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+ }
+
+ // Field 3: flush_request_id
+ if (_has_field_[3]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(3, flush_request_id_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+CommitDataRequest_ChunkToPatch::CommitDataRequest_ChunkToPatch() = default;
+CommitDataRequest_ChunkToPatch::~CommitDataRequest_ChunkToPatch() = default;
+CommitDataRequest_ChunkToPatch::CommitDataRequest_ChunkToPatch(const CommitDataRequest_ChunkToPatch&) = default;
+CommitDataRequest_ChunkToPatch& CommitDataRequest_ChunkToPatch::operator=(const CommitDataRequest_ChunkToPatch&) = default;
+CommitDataRequest_ChunkToPatch::CommitDataRequest_ChunkToPatch(CommitDataRequest_ChunkToPatch&&) noexcept = default;
+CommitDataRequest_ChunkToPatch& CommitDataRequest_ChunkToPatch::operator=(CommitDataRequest_ChunkToPatch&&) = default;
+
+bool CommitDataRequest_ChunkToPatch::operator==(const CommitDataRequest_ChunkToPatch& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(target_buffer_, other.target_buffer_)
+ && ::protozero::internal::gen_helpers::EqualsField(writer_id_, other.writer_id_)
+ && ::protozero::internal::gen_helpers::EqualsField(chunk_id_, other.chunk_id_)
+ && ::protozero::internal::gen_helpers::EqualsField(patches_, other.patches_)
+ && ::protozero::internal::gen_helpers::EqualsField(has_more_patches_, other.has_more_patches_);
+}
+
+int CommitDataRequest_ChunkToPatch::patches_size() const { return static_cast<int>(patches_.size()); }
+void CommitDataRequest_ChunkToPatch::clear_patches() { patches_.clear(); }
+CommitDataRequest_ChunkToPatch_Patch* CommitDataRequest_ChunkToPatch::add_patches() { patches_.emplace_back(); return &patches_.back(); }
+bool CommitDataRequest_ChunkToPatch::ParseFromArray(const void* raw, size_t size) {
+ patches_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* target_buffer */:
+ field.get(&target_buffer_);
+ break;
+ case 2 /* writer_id */:
+ field.get(&writer_id_);
+ break;
+ case 3 /* chunk_id */:
+ field.get(&chunk_id_);
+ break;
+ case 4 /* patches */:
+ patches_.emplace_back();
+ patches_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 5 /* has_more_patches */:
+ field.get(&has_more_patches_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string CommitDataRequest_ChunkToPatch::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> CommitDataRequest_ChunkToPatch::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void CommitDataRequest_ChunkToPatch::Serialize(::protozero::Message* msg) const {
+ // Field 1: target_buffer
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(1, target_buffer_, msg);
+ }
+
+ // Field 2: writer_id
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(2, writer_id_, msg);
+ }
+
+ // Field 3: chunk_id
+ if (_has_field_[3]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(3, chunk_id_, msg);
+ }
+
+ // Field 4: patches
+ for (auto& it : patches_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
+ }
+
+ // Field 5: has_more_patches
+ if (_has_field_[5]) {
+ ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, has_more_patches_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+CommitDataRequest_ChunkToPatch_Patch::CommitDataRequest_ChunkToPatch_Patch() = default;
+CommitDataRequest_ChunkToPatch_Patch::~CommitDataRequest_ChunkToPatch_Patch() = default;
+CommitDataRequest_ChunkToPatch_Patch::CommitDataRequest_ChunkToPatch_Patch(const CommitDataRequest_ChunkToPatch_Patch&) = default;
+CommitDataRequest_ChunkToPatch_Patch& CommitDataRequest_ChunkToPatch_Patch::operator=(const CommitDataRequest_ChunkToPatch_Patch&) = default;
+CommitDataRequest_ChunkToPatch_Patch::CommitDataRequest_ChunkToPatch_Patch(CommitDataRequest_ChunkToPatch_Patch&&) noexcept = default;
+CommitDataRequest_ChunkToPatch_Patch& CommitDataRequest_ChunkToPatch_Patch::operator=(CommitDataRequest_ChunkToPatch_Patch&&) = default;
+
+bool CommitDataRequest_ChunkToPatch_Patch::operator==(const CommitDataRequest_ChunkToPatch_Patch& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(offset_, other.offset_)
+ && ::protozero::internal::gen_helpers::EqualsField(data_, other.data_);
+}
+
+bool CommitDataRequest_ChunkToPatch_Patch::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* offset */:
+ field.get(&offset_);
+ break;
+ case 2 /* data */:
+ field.get(&data_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string CommitDataRequest_ChunkToPatch_Patch::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> CommitDataRequest_ChunkToPatch_Patch::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void CommitDataRequest_ChunkToPatch_Patch::Serialize(::protozero::Message* msg) const {
+ // Field 1: offset
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(1, offset_, msg);
+ }
+
+ // Field 2: data
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeString(2, data_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+CommitDataRequest_ChunksToMove::CommitDataRequest_ChunksToMove() = default;
+CommitDataRequest_ChunksToMove::~CommitDataRequest_ChunksToMove() = default;
+CommitDataRequest_ChunksToMove::CommitDataRequest_ChunksToMove(const CommitDataRequest_ChunksToMove&) = default;
+CommitDataRequest_ChunksToMove& CommitDataRequest_ChunksToMove::operator=(const CommitDataRequest_ChunksToMove&) = default;
+CommitDataRequest_ChunksToMove::CommitDataRequest_ChunksToMove(CommitDataRequest_ChunksToMove&&) noexcept = default;
+CommitDataRequest_ChunksToMove& CommitDataRequest_ChunksToMove::operator=(CommitDataRequest_ChunksToMove&&) = default;
+
+bool CommitDataRequest_ChunksToMove::operator==(const CommitDataRequest_ChunksToMove& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(page_, other.page_)
+ && ::protozero::internal::gen_helpers::EqualsField(chunk_, other.chunk_)
+ && ::protozero::internal::gen_helpers::EqualsField(target_buffer_, other.target_buffer_)
+ && ::protozero::internal::gen_helpers::EqualsField(data_, other.data_);
+}
+
+bool CommitDataRequest_ChunksToMove::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* page */:
+ field.get(&page_);
+ break;
+ case 2 /* chunk */:
+ field.get(&chunk_);
+ break;
+ case 3 /* target_buffer */:
+ field.get(&target_buffer_);
+ break;
+ case 4 /* data */:
+ field.get(&data_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string CommitDataRequest_ChunksToMove::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> CommitDataRequest_ChunksToMove::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void CommitDataRequest_ChunksToMove::Serialize(::protozero::Message* msg) const {
+ // Field 1: page
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(1, page_, msg);
+ }
+
+ // Field 2: chunk
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(2, chunk_, msg);
+ }
+
+ // Field 3: target_buffer
+ if (_has_field_[3]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(3, target_buffer_, msg);
+ }
+
+ // Field 4: data
+ if (_has_field_[4]) {
+ ::protozero::internal::gen_helpers::SerializeString(4, data_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+} // namespace perfetto
+} // namespace protos
+} // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/data_source_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/data_source_descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+DataSourceDescriptor::DataSourceDescriptor() = default;
+DataSourceDescriptor::~DataSourceDescriptor() = default;
+DataSourceDescriptor::DataSourceDescriptor(const DataSourceDescriptor&) = default;
+DataSourceDescriptor& DataSourceDescriptor::operator=(const DataSourceDescriptor&) = default;
+DataSourceDescriptor::DataSourceDescriptor(DataSourceDescriptor&&) noexcept = default;
+DataSourceDescriptor& DataSourceDescriptor::operator=(DataSourceDescriptor&&) = default;
+
+bool DataSourceDescriptor::operator==(const DataSourceDescriptor& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+ && ::protozero::internal::gen_helpers::EqualsField(id_, other.id_)
+ && ::protozero::internal::gen_helpers::EqualsField(will_notify_on_stop_, other.will_notify_on_stop_)
+ && ::protozero::internal::gen_helpers::EqualsField(will_notify_on_start_, other.will_notify_on_start_)
+ && ::protozero::internal::gen_helpers::EqualsField(handles_incremental_state_clear_, other.handles_incremental_state_clear_)
+ && ::protozero::internal::gen_helpers::EqualsField(no_flush_, other.no_flush_)
+ && ::protozero::internal::gen_helpers::EqualsField(gpu_counter_descriptor_, other.gpu_counter_descriptor_)
+ && ::protozero::internal::gen_helpers::EqualsField(track_event_descriptor_, other.track_event_descriptor_)
+ && ::protozero::internal::gen_helpers::EqualsField(ftrace_descriptor_, other.ftrace_descriptor_);
+}
+
+bool DataSourceDescriptor::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+ break;
+ case 7 /* id */:
+ field.get(&id_);
+ break;
+ case 2 /* will_notify_on_stop */:
+ field.get(&will_notify_on_stop_);
+ break;
+ case 3 /* will_notify_on_start */:
+ field.get(&will_notify_on_start_);
+ break;
+ case 4 /* handles_incremental_state_clear */:
+ field.get(&handles_incremental_state_clear_);
+ break;
+ case 9 /* no_flush */:
+ field.get(&no_flush_);
+ break;
+ case 5 /* gpu_counter_descriptor */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &gpu_counter_descriptor_);
+ break;
+ case 6 /* track_event_descriptor */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &track_event_descriptor_);
+ break;
+ case 8 /* ftrace_descriptor */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &ftrace_descriptor_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string DataSourceDescriptor::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> DataSourceDescriptor::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void DataSourceDescriptor::Serialize(::protozero::Message* msg) const {
+ // Field 1: name
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+ }
+
+ // Field 7: id
+ if (_has_field_[7]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(7, id_, msg);
+ }
+
+ // Field 2: will_notify_on_stop
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, will_notify_on_stop_, msg);
+ }
+
+ // Field 3: will_notify_on_start
+ if (_has_field_[3]) {
+ ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, will_notify_on_start_, msg);
+ }
+
+ // Field 4: handles_incremental_state_clear
+ if (_has_field_[4]) {
+ ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, handles_incremental_state_clear_, msg);
+ }
+
+ // Field 9: no_flush
+ if (_has_field_[9]) {
+ ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, no_flush_, msg);
+ }
+
+ // Field 5: gpu_counter_descriptor
+ if (_has_field_[5]) {
+ msg->AppendString(5, gpu_counter_descriptor_);
+ }
+
+ // Field 6: track_event_descriptor
+ if (_has_field_[6]) {
+ msg->AppendString(6, track_event_descriptor_);
+ }
+
+ // Field 8: ftrace_descriptor
+ if (_has_field_[8]) {
+ msg->AppendString(8, ftrace_descriptor_);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+} // namespace perfetto
+} // namespace protos
+} // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+OneofOptions::OneofOptions() = default;
+OneofOptions::~OneofOptions() = default;
+OneofOptions::OneofOptions(const OneofOptions&) = default;
+OneofOptions& OneofOptions::operator=(const OneofOptions&) = default;
+OneofOptions::OneofOptions(OneofOptions&&) noexcept = default;
+OneofOptions& OneofOptions::operator=(OneofOptions&&) = default;
+
+bool OneofOptions::operator==(const OneofOptions& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
+}
+
+bool OneofOptions::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string OneofOptions::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> OneofOptions::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void OneofOptions::Serialize(::protozero::Message* msg) const {
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+EnumValueDescriptorProto::EnumValueDescriptorProto() = default;
+EnumValueDescriptorProto::~EnumValueDescriptorProto() = default;
+EnumValueDescriptorProto::EnumValueDescriptorProto(const EnumValueDescriptorProto&) = default;
+EnumValueDescriptorProto& EnumValueDescriptorProto::operator=(const EnumValueDescriptorProto&) = default;
+EnumValueDescriptorProto::EnumValueDescriptorProto(EnumValueDescriptorProto&&) noexcept = default;
+EnumValueDescriptorProto& EnumValueDescriptorProto::operator=(EnumValueDescriptorProto&&) = default;
+
+bool EnumValueDescriptorProto::operator==(const EnumValueDescriptorProto& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+ && ::protozero::internal::gen_helpers::EqualsField(number_, other.number_);
+}
+
+bool EnumValueDescriptorProto::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+ break;
+ case 2 /* number */:
+ field.get(&number_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string EnumValueDescriptorProto::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> EnumValueDescriptorProto::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void EnumValueDescriptorProto::Serialize(::protozero::Message* msg) const {
+ // Field 1: name
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+ }
+
+ // Field 2: number
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(2, number_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+EnumDescriptorProto::EnumDescriptorProto() = default;
+EnumDescriptorProto::~EnumDescriptorProto() = default;
+EnumDescriptorProto::EnumDescriptorProto(const EnumDescriptorProto&) = default;
+EnumDescriptorProto& EnumDescriptorProto::operator=(const EnumDescriptorProto&) = default;
+EnumDescriptorProto::EnumDescriptorProto(EnumDescriptorProto&&) noexcept = default;
+EnumDescriptorProto& EnumDescriptorProto::operator=(EnumDescriptorProto&&) = default;
+
+bool EnumDescriptorProto::operator==(const EnumDescriptorProto& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+ && ::protozero::internal::gen_helpers::EqualsField(value_, other.value_)
+ && ::protozero::internal::gen_helpers::EqualsField(reserved_name_, other.reserved_name_);
+}
+
+int EnumDescriptorProto::value_size() const { return static_cast<int>(value_.size()); }
+void EnumDescriptorProto::clear_value() { value_.clear(); }
+EnumValueDescriptorProto* EnumDescriptorProto::add_value() { value_.emplace_back(); return &value_.back(); }
+bool EnumDescriptorProto::ParseFromArray(const void* raw, size_t size) {
+ value_.clear();
+ reserved_name_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+ break;
+ case 2 /* value */:
+ value_.emplace_back();
+ value_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 5 /* reserved_name */:
+ reserved_name_.emplace_back();
+ ::protozero::internal::gen_helpers::DeserializeString(field, &reserved_name_.back());
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string EnumDescriptorProto::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> EnumDescriptorProto::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void EnumDescriptorProto::Serialize(::protozero::Message* msg) const {
+ // Field 1: name
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+ }
+
+ // Field 2: value
+ for (auto& it : value_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+ }
+
+ // Field 5: reserved_name
+ for (auto& it : reserved_name_) {
+ ::protozero::internal::gen_helpers::SerializeString(5, it, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+OneofDescriptorProto::OneofDescriptorProto() = default;
+OneofDescriptorProto::~OneofDescriptorProto() = default;
+OneofDescriptorProto::OneofDescriptorProto(const OneofDescriptorProto&) = default;
+OneofDescriptorProto& OneofDescriptorProto::operator=(const OneofDescriptorProto&) = default;
+OneofDescriptorProto::OneofDescriptorProto(OneofDescriptorProto&&) noexcept = default;
+OneofDescriptorProto& OneofDescriptorProto::operator=(OneofDescriptorProto&&) = default;
+
+bool OneofDescriptorProto::operator==(const OneofDescriptorProto& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+ && ::protozero::internal::gen_helpers::EqualsField(options_, other.options_);
+}
+
+bool OneofDescriptorProto::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+ break;
+ case 2 /* options */:
+ (*options_).ParseFromArray(field.data(), field.size());
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string OneofDescriptorProto::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> OneofDescriptorProto::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void OneofDescriptorProto::Serialize(::protozero::Message* msg) const {
+ // Field 1: name
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+ }
+
+ // Field 2: options
+ if (_has_field_[2]) {
+ (*options_).Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+FieldDescriptorProto::FieldDescriptorProto() = default;
+FieldDescriptorProto::~FieldDescriptorProto() = default;
+FieldDescriptorProto::FieldDescriptorProto(const FieldDescriptorProto&) = default;
+FieldDescriptorProto& FieldDescriptorProto::operator=(const FieldDescriptorProto&) = default;
+FieldDescriptorProto::FieldDescriptorProto(FieldDescriptorProto&&) noexcept = default;
+FieldDescriptorProto& FieldDescriptorProto::operator=(FieldDescriptorProto&&) = default;
+
+bool FieldDescriptorProto::operator==(const FieldDescriptorProto& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+ && ::protozero::internal::gen_helpers::EqualsField(number_, other.number_)
+ && ::protozero::internal::gen_helpers::EqualsField(label_, other.label_)
+ && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
+ && ::protozero::internal::gen_helpers::EqualsField(type_name_, other.type_name_)
+ && ::protozero::internal::gen_helpers::EqualsField(extendee_, other.extendee_)
+ && ::protozero::internal::gen_helpers::EqualsField(default_value_, other.default_value_)
+ && ::protozero::internal::gen_helpers::EqualsField(options_, other.options_)
+ && ::protozero::internal::gen_helpers::EqualsField(oneof_index_, other.oneof_index_);
+}
+
+bool FieldDescriptorProto::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+ break;
+ case 3 /* number */:
+ field.get(&number_);
+ break;
+ case 4 /* label */:
+ field.get(&label_);
+ break;
+ case 5 /* type */:
+ field.get(&type_);
+ break;
+ case 6 /* type_name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &type_name_);
+ break;
+ case 2 /* extendee */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &extendee_);
+ break;
+ case 7 /* default_value */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &default_value_);
+ break;
+ case 8 /* options */:
+ (*options_).ParseFromArray(field.data(), field.size());
+ break;
+ case 9 /* oneof_index */:
+ field.get(&oneof_index_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string FieldDescriptorProto::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> FieldDescriptorProto::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void FieldDescriptorProto::Serialize(::protozero::Message* msg) const {
+ // Field 1: name
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+ }
+
+ // Field 3: number
+ if (_has_field_[3]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(3, number_, msg);
+ }
+
+ // Field 4: label
+ if (_has_field_[4]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(4, label_, msg);
+ }
+
+ // Field 5: type
+ if (_has_field_[5]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(5, type_, msg);
+ }
+
+ // Field 6: type_name
+ if (_has_field_[6]) {
+ ::protozero::internal::gen_helpers::SerializeString(6, type_name_, msg);
+ }
+
+ // Field 2: extendee
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeString(2, extendee_, msg);
+ }
+
+ // Field 7: default_value
+ if (_has_field_[7]) {
+ ::protozero::internal::gen_helpers::SerializeString(7, default_value_, msg);
+ }
+
+ // Field 8: options
+ if (_has_field_[8]) {
+ (*options_).Serialize(msg->BeginNestedMessage<::protozero::Message>(8));
+ }
+
+ // Field 9: oneof_index
+ if (_has_field_[9]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(9, oneof_index_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+FieldOptions::FieldOptions() = default;
+FieldOptions::~FieldOptions() = default;
+FieldOptions::FieldOptions(const FieldOptions&) = default;
+FieldOptions& FieldOptions::operator=(const FieldOptions&) = default;
+FieldOptions::FieldOptions(FieldOptions&&) noexcept = default;
+FieldOptions& FieldOptions::operator=(FieldOptions&&) = default;
+
+bool FieldOptions::operator==(const FieldOptions& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(packed_, other.packed_)
+ && ::protozero::internal::gen_helpers::EqualsField(uninterpreted_option_, other.uninterpreted_option_);
+}
+
+int FieldOptions::uninterpreted_option_size() const { return static_cast<int>(uninterpreted_option_.size()); }
+void FieldOptions::clear_uninterpreted_option() { uninterpreted_option_.clear(); }
+UninterpretedOption* FieldOptions::add_uninterpreted_option() { uninterpreted_option_.emplace_back(); return &uninterpreted_option_.back(); }
+bool FieldOptions::ParseFromArray(const void* raw, size_t size) {
+ uninterpreted_option_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 2 /* packed */:
+ field.get(&packed_);
+ break;
+ case 999 /* uninterpreted_option */:
+ uninterpreted_option_.emplace_back();
+ uninterpreted_option_.back().ParseFromArray(field.data(), field.size());
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string FieldOptions::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> FieldOptions::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void FieldOptions::Serialize(::protozero::Message* msg) const {
+ // Field 2: packed
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, packed_, msg);
+ }
+
+ // Field 999: uninterpreted_option
+ for (auto& it : uninterpreted_option_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(999));
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+UninterpretedOption::UninterpretedOption() = default;
+UninterpretedOption::~UninterpretedOption() = default;
+UninterpretedOption::UninterpretedOption(const UninterpretedOption&) = default;
+UninterpretedOption& UninterpretedOption::operator=(const UninterpretedOption&) = default;
+UninterpretedOption::UninterpretedOption(UninterpretedOption&&) noexcept = default;
+UninterpretedOption& UninterpretedOption::operator=(UninterpretedOption&&) = default;
+
+bool UninterpretedOption::operator==(const UninterpretedOption& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+ && ::protozero::internal::gen_helpers::EqualsField(identifier_value_, other.identifier_value_)
+ && ::protozero::internal::gen_helpers::EqualsField(positive_int_value_, other.positive_int_value_)
+ && ::protozero::internal::gen_helpers::EqualsField(negative_int_value_, other.negative_int_value_)
+ && ::protozero::internal::gen_helpers::EqualsField(double_value_, other.double_value_)
+ && ::protozero::internal::gen_helpers::EqualsField(string_value_, other.string_value_)
+ && ::protozero::internal::gen_helpers::EqualsField(aggregate_value_, other.aggregate_value_);
+}
+
+int UninterpretedOption::name_size() const { return static_cast<int>(name_.size()); }
+void UninterpretedOption::clear_name() { name_.clear(); }
+UninterpretedOption_NamePart* UninterpretedOption::add_name() { name_.emplace_back(); return &name_.back(); }
+bool UninterpretedOption::ParseFromArray(const void* raw, size_t size) {
+ name_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 2 /* name */:
+ name_.emplace_back();
+ name_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 3 /* identifier_value */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &identifier_value_);
+ break;
+ case 4 /* positive_int_value */:
+ field.get(&positive_int_value_);
+ break;
+ case 5 /* negative_int_value */:
+ field.get(&negative_int_value_);
+ break;
+ case 6 /* double_value */:
+ field.get(&double_value_);
+ break;
+ case 7 /* string_value */:
+ field.get(&string_value_);
+ break;
+ case 8 /* aggregate_value */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &aggregate_value_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string UninterpretedOption::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> UninterpretedOption::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void UninterpretedOption::Serialize(::protozero::Message* msg) const {
+ // Field 2: name
+ for (auto& it : name_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+ }
+
+ // Field 3: identifier_value
+ if (_has_field_[3]) {
+ ::protozero::internal::gen_helpers::SerializeString(3, identifier_value_, msg);
+ }
+
+ // Field 4: positive_int_value
+ if (_has_field_[4]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(4, positive_int_value_, msg);
+ }
+
+ // Field 5: negative_int_value
+ if (_has_field_[5]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(5, negative_int_value_, msg);
+ }
+
+ // Field 6: double_value
+ if (_has_field_[6]) {
+ ::protozero::internal::gen_helpers::SerializeFixed(6, double_value_, msg);
+ }
+
+ // Field 7: string_value
+ if (_has_field_[7]) {
+ ::protozero::internal::gen_helpers::SerializeString(7, string_value_, msg);
+ }
+
+ // Field 8: aggregate_value
+ if (_has_field_[8]) {
+ ::protozero::internal::gen_helpers::SerializeString(8, aggregate_value_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+UninterpretedOption_NamePart::UninterpretedOption_NamePart() = default;
+UninterpretedOption_NamePart::~UninterpretedOption_NamePart() = default;
+UninterpretedOption_NamePart::UninterpretedOption_NamePart(const UninterpretedOption_NamePart&) = default;
+UninterpretedOption_NamePart& UninterpretedOption_NamePart::operator=(const UninterpretedOption_NamePart&) = default;
+UninterpretedOption_NamePart::UninterpretedOption_NamePart(UninterpretedOption_NamePart&&) noexcept = default;
+UninterpretedOption_NamePart& UninterpretedOption_NamePart::operator=(UninterpretedOption_NamePart&&) = default;
+
+bool UninterpretedOption_NamePart::operator==(const UninterpretedOption_NamePart& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_part_, other.name_part_)
+ && ::protozero::internal::gen_helpers::EqualsField(is_extension_, other.is_extension_);
+}
+
+bool UninterpretedOption_NamePart::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* name_part */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_part_);
+ break;
+ case 2 /* is_extension */:
+ field.get(&is_extension_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string UninterpretedOption_NamePart::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> UninterpretedOption_NamePart::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void UninterpretedOption_NamePart::Serialize(::protozero::Message* msg) const {
+ // Field 1: name_part
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeString(1, name_part_, msg);
+ }
+
+ // Field 2: is_extension
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, is_extension_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+DescriptorProto::DescriptorProto() = default;
+DescriptorProto::~DescriptorProto() = default;
+DescriptorProto::DescriptorProto(const DescriptorProto&) = default;
+DescriptorProto& DescriptorProto::operator=(const DescriptorProto&) = default;
+DescriptorProto::DescriptorProto(DescriptorProto&&) noexcept = default;
+DescriptorProto& DescriptorProto::operator=(DescriptorProto&&) = default;
+
+bool DescriptorProto::operator==(const DescriptorProto& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+ && ::protozero::internal::gen_helpers::EqualsField(field_, other.field_)
+ && ::protozero::internal::gen_helpers::EqualsField(extension_, other.extension_)
+ && ::protozero::internal::gen_helpers::EqualsField(nested_type_, other.nested_type_)
+ && ::protozero::internal::gen_helpers::EqualsField(enum_type_, other.enum_type_)
+ && ::protozero::internal::gen_helpers::EqualsField(oneof_decl_, other.oneof_decl_)
+ && ::protozero::internal::gen_helpers::EqualsField(reserved_range_, other.reserved_range_)
+ && ::protozero::internal::gen_helpers::EqualsField(reserved_name_, other.reserved_name_);
+}
+
+int DescriptorProto::field_size() const { return static_cast<int>(field_.size()); }
+void DescriptorProto::clear_field() { field_.clear(); }
+FieldDescriptorProto* DescriptorProto::add_field() { field_.emplace_back(); return &field_.back(); }
+int DescriptorProto::extension_size() const { return static_cast<int>(extension_.size()); }
+void DescriptorProto::clear_extension() { extension_.clear(); }
+FieldDescriptorProto* DescriptorProto::add_extension() { extension_.emplace_back(); return &extension_.back(); }
+int DescriptorProto::nested_type_size() const { return static_cast<int>(nested_type_.size()); }
+void DescriptorProto::clear_nested_type() { nested_type_.clear(); }
+DescriptorProto* DescriptorProto::add_nested_type() { nested_type_.emplace_back(); return &nested_type_.back(); }
+int DescriptorProto::enum_type_size() const { return static_cast<int>(enum_type_.size()); }
+void DescriptorProto::clear_enum_type() { enum_type_.clear(); }
+EnumDescriptorProto* DescriptorProto::add_enum_type() { enum_type_.emplace_back(); return &enum_type_.back(); }
+int DescriptorProto::oneof_decl_size() const { return static_cast<int>(oneof_decl_.size()); }
+void DescriptorProto::clear_oneof_decl() { oneof_decl_.clear(); }
+OneofDescriptorProto* DescriptorProto::add_oneof_decl() { oneof_decl_.emplace_back(); return &oneof_decl_.back(); }
+int DescriptorProto::reserved_range_size() const { return static_cast<int>(reserved_range_.size()); }
+void DescriptorProto::clear_reserved_range() { reserved_range_.clear(); }
+DescriptorProto_ReservedRange* DescriptorProto::add_reserved_range() { reserved_range_.emplace_back(); return &reserved_range_.back(); }
+bool DescriptorProto::ParseFromArray(const void* raw, size_t size) {
+ field_.clear();
+ extension_.clear();
+ nested_type_.clear();
+ enum_type_.clear();
+ oneof_decl_.clear();
+ reserved_range_.clear();
+ reserved_name_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+ break;
+ case 2 /* field */:
+ field_.emplace_back();
+ field_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 6 /* extension */:
+ extension_.emplace_back();
+ extension_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 3 /* nested_type */:
+ nested_type_.emplace_back();
+ nested_type_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 4 /* enum_type */:
+ enum_type_.emplace_back();
+ enum_type_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 8 /* oneof_decl */:
+ oneof_decl_.emplace_back();
+ oneof_decl_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 9 /* reserved_range */:
+ reserved_range_.emplace_back();
+ reserved_range_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 10 /* reserved_name */:
+ reserved_name_.emplace_back();
+ ::protozero::internal::gen_helpers::DeserializeString(field, &reserved_name_.back());
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string DescriptorProto::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> DescriptorProto::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void DescriptorProto::Serialize(::protozero::Message* msg) const {
+ // Field 1: name
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+ }
+
+ // Field 2: field
+ for (auto& it : field_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+ }
+
+ // Field 6: extension
+ for (auto& it : extension_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
+ }
+
+ // Field 3: nested_type
+ for (auto& it : nested_type_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
+ }
+
+ // Field 4: enum_type
+ for (auto& it : enum_type_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
+ }
+
+ // Field 8: oneof_decl
+ for (auto& it : oneof_decl_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(8));
+ }
+
+ // Field 9: reserved_range
+ for (auto& it : reserved_range_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(9));
+ }
+
+ // Field 10: reserved_name
+ for (auto& it : reserved_name_) {
+ ::protozero::internal::gen_helpers::SerializeString(10, it, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+DescriptorProto_ReservedRange::DescriptorProto_ReservedRange() = default;
+DescriptorProto_ReservedRange::~DescriptorProto_ReservedRange() = default;
+DescriptorProto_ReservedRange::DescriptorProto_ReservedRange(const DescriptorProto_ReservedRange&) = default;
+DescriptorProto_ReservedRange& DescriptorProto_ReservedRange::operator=(const DescriptorProto_ReservedRange&) = default;
+DescriptorProto_ReservedRange::DescriptorProto_ReservedRange(DescriptorProto_ReservedRange&&) noexcept = default;
+DescriptorProto_ReservedRange& DescriptorProto_ReservedRange::operator=(DescriptorProto_ReservedRange&&) = default;
+
+bool DescriptorProto_ReservedRange::operator==(const DescriptorProto_ReservedRange& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(start_, other.start_)
+ && ::protozero::internal::gen_helpers::EqualsField(end_, other.end_);
+}
+
+bool DescriptorProto_ReservedRange::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* start */:
+ field.get(&start_);
+ break;
+ case 2 /* end */:
+ field.get(&end_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string DescriptorProto_ReservedRange::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> DescriptorProto_ReservedRange::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void DescriptorProto_ReservedRange::Serialize(::protozero::Message* msg) const {
+ // Field 1: start
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(1, start_, msg);
+ }
+
+ // Field 2: end
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(2, end_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+FileDescriptorProto::FileDescriptorProto() = default;
+FileDescriptorProto::~FileDescriptorProto() = default;
+FileDescriptorProto::FileDescriptorProto(const FileDescriptorProto&) = default;
+FileDescriptorProto& FileDescriptorProto::operator=(const FileDescriptorProto&) = default;
+FileDescriptorProto::FileDescriptorProto(FileDescriptorProto&&) noexcept = default;
+FileDescriptorProto& FileDescriptorProto::operator=(FileDescriptorProto&&) = default;
+
+bool FileDescriptorProto::operator==(const FileDescriptorProto& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+ && ::protozero::internal::gen_helpers::EqualsField(package_, other.package_)
+ && ::protozero::internal::gen_helpers::EqualsField(dependency_, other.dependency_)
+ && ::protozero::internal::gen_helpers::EqualsField(public_dependency_, other.public_dependency_)
+ && ::protozero::internal::gen_helpers::EqualsField(weak_dependency_, other.weak_dependency_)
+ && ::protozero::internal::gen_helpers::EqualsField(message_type_, other.message_type_)
+ && ::protozero::internal::gen_helpers::EqualsField(enum_type_, other.enum_type_)
+ && ::protozero::internal::gen_helpers::EqualsField(extension_, other.extension_);
+}
+
+int FileDescriptorProto::message_type_size() const { return static_cast<int>(message_type_.size()); }
+void FileDescriptorProto::clear_message_type() { message_type_.clear(); }
+DescriptorProto* FileDescriptorProto::add_message_type() { message_type_.emplace_back(); return &message_type_.back(); }
+int FileDescriptorProto::enum_type_size() const { return static_cast<int>(enum_type_.size()); }
+void FileDescriptorProto::clear_enum_type() { enum_type_.clear(); }
+EnumDescriptorProto* FileDescriptorProto::add_enum_type() { enum_type_.emplace_back(); return &enum_type_.back(); }
+int FileDescriptorProto::extension_size() const { return static_cast<int>(extension_.size()); }
+void FileDescriptorProto::clear_extension() { extension_.clear(); }
+FieldDescriptorProto* FileDescriptorProto::add_extension() { extension_.emplace_back(); return &extension_.back(); }
+bool FileDescriptorProto::ParseFromArray(const void* raw, size_t size) {
+ dependency_.clear();
+ public_dependency_.clear();
+ weak_dependency_.clear();
+ message_type_.clear();
+ enum_type_.clear();
+ extension_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+ break;
+ case 2 /* package */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &package_);
+ break;
+ case 3 /* dependency */:
+ dependency_.emplace_back();
+ ::protozero::internal::gen_helpers::DeserializeString(field, &dependency_.back());
+ break;
+ case 10 /* public_dependency */:
+ public_dependency_.emplace_back();
+ field.get(&public_dependency_.back());
+ break;
+ case 11 /* weak_dependency */:
+ weak_dependency_.emplace_back();
+ field.get(&weak_dependency_.back());
+ break;
+ case 4 /* message_type */:
+ message_type_.emplace_back();
+ message_type_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 5 /* enum_type */:
+ enum_type_.emplace_back();
+ enum_type_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 7 /* extension */:
+ extension_.emplace_back();
+ extension_.back().ParseFromArray(field.data(), field.size());
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string FileDescriptorProto::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> FileDescriptorProto::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void FileDescriptorProto::Serialize(::protozero::Message* msg) const {
+ // Field 1: name
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+ }
+
+ // Field 2: package
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeString(2, package_, msg);
+ }
+
+ // Field 3: dependency
+ for (auto& it : dependency_) {
+ ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
+ }
+
+ // Field 10: public_dependency
+ for (auto& it : public_dependency_) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(10, it, msg);
+ }
+
+ // Field 11: weak_dependency
+ for (auto& it : weak_dependency_) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(11, it, msg);
+ }
+
+ // Field 4: message_type
+ for (auto& it : message_type_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
+ }
+
+ // Field 5: enum_type
+ for (auto& it : enum_type_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
+ }
+
+ // Field 7: extension
+ for (auto& it : extension_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(7));
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+FileDescriptorSet::FileDescriptorSet() = default;
+FileDescriptorSet::~FileDescriptorSet() = default;
+FileDescriptorSet::FileDescriptorSet(const FileDescriptorSet&) = default;
+FileDescriptorSet& FileDescriptorSet::operator=(const FileDescriptorSet&) = default;
+FileDescriptorSet::FileDescriptorSet(FileDescriptorSet&&) noexcept = default;
+FileDescriptorSet& FileDescriptorSet::operator=(FileDescriptorSet&&) = default;
+
+bool FileDescriptorSet::operator==(const FileDescriptorSet& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(file_, other.file_);
+}
+
+int FileDescriptorSet::file_size() const { return static_cast<int>(file_.size()); }
+void FileDescriptorSet::clear_file() { file_.clear(); }
+FileDescriptorProto* FileDescriptorSet::add_file() { file_.emplace_back(); return &file_.back(); }
+bool FileDescriptorSet::ParseFromArray(const void* raw, size_t size) {
+ file_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* file */:
+ file_.emplace_back();
+ file_.back().ParseFromArray(field.data(), field.size());
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string FileDescriptorSet::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> FileDescriptorSet::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void FileDescriptorSet::Serialize(::protozero::Message* msg) const {
+ // Field 1: file
+ for (auto& it : file_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+} // namespace perfetto
+} // namespace protos
+} // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/ftrace_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/ftrace_descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+FtraceDescriptor::FtraceDescriptor() = default;
+FtraceDescriptor::~FtraceDescriptor() = default;
+FtraceDescriptor::FtraceDescriptor(const FtraceDescriptor&) = default;
+FtraceDescriptor& FtraceDescriptor::operator=(const FtraceDescriptor&) = default;
+FtraceDescriptor::FtraceDescriptor(FtraceDescriptor&&) noexcept = default;
+FtraceDescriptor& FtraceDescriptor::operator=(FtraceDescriptor&&) = default;
+
+bool FtraceDescriptor::operator==(const FtraceDescriptor& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(atrace_categories_, other.atrace_categories_);
+}
+
+int FtraceDescriptor::atrace_categories_size() const { return static_cast<int>(atrace_categories_.size()); }
+void FtraceDescriptor::clear_atrace_categories() { atrace_categories_.clear(); }
+FtraceDescriptor_AtraceCategory* FtraceDescriptor::add_atrace_categories() { atrace_categories_.emplace_back(); return &atrace_categories_.back(); }
+bool FtraceDescriptor::ParseFromArray(const void* raw, size_t size) {
+ atrace_categories_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* atrace_categories */:
+ atrace_categories_.emplace_back();
+ atrace_categories_.back().ParseFromArray(field.data(), field.size());
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string FtraceDescriptor::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> FtraceDescriptor::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void FtraceDescriptor::Serialize(::protozero::Message* msg) const {
+ // Field 1: atrace_categories
+ for (auto& it : atrace_categories_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+FtraceDescriptor_AtraceCategory::FtraceDescriptor_AtraceCategory() = default;
+FtraceDescriptor_AtraceCategory::~FtraceDescriptor_AtraceCategory() = default;
+FtraceDescriptor_AtraceCategory::FtraceDescriptor_AtraceCategory(const FtraceDescriptor_AtraceCategory&) = default;
+FtraceDescriptor_AtraceCategory& FtraceDescriptor_AtraceCategory::operator=(const FtraceDescriptor_AtraceCategory&) = default;
+FtraceDescriptor_AtraceCategory::FtraceDescriptor_AtraceCategory(FtraceDescriptor_AtraceCategory&&) noexcept = default;
+FtraceDescriptor_AtraceCategory& FtraceDescriptor_AtraceCategory::operator=(FtraceDescriptor_AtraceCategory&&) = default;
+
+bool FtraceDescriptor_AtraceCategory::operator==(const FtraceDescriptor_AtraceCategory& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+ && ::protozero::internal::gen_helpers::EqualsField(description_, other.description_);
+}
+
+bool FtraceDescriptor_AtraceCategory::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+ break;
+ case 2 /* description */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &description_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string FtraceDescriptor_AtraceCategory::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> FtraceDescriptor_AtraceCategory::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void FtraceDescriptor_AtraceCategory::Serialize(::protozero::Message* msg) const {
+ // Field 1: name
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+ }
+
+ // Field 2: description
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeString(2, description_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+} // namespace perfetto
+} // namespace protos
+} // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/gpu_counter_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/gpu_counter_descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+GpuCounterDescriptor::GpuCounterDescriptor() = default;
+GpuCounterDescriptor::~GpuCounterDescriptor() = default;
+GpuCounterDescriptor::GpuCounterDescriptor(const GpuCounterDescriptor&) = default;
+GpuCounterDescriptor& GpuCounterDescriptor::operator=(const GpuCounterDescriptor&) = default;
+GpuCounterDescriptor::GpuCounterDescriptor(GpuCounterDescriptor&&) noexcept = default;
+GpuCounterDescriptor& GpuCounterDescriptor::operator=(GpuCounterDescriptor&&) = default;
+
+bool GpuCounterDescriptor::operator==(const GpuCounterDescriptor& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(specs_, other.specs_)
+ && ::protozero::internal::gen_helpers::EqualsField(blocks_, other.blocks_)
+ && ::protozero::internal::gen_helpers::EqualsField(min_sampling_period_ns_, other.min_sampling_period_ns_)
+ && ::protozero::internal::gen_helpers::EqualsField(max_sampling_period_ns_, other.max_sampling_period_ns_)
+ && ::protozero::internal::gen_helpers::EqualsField(supports_instrumented_sampling_, other.supports_instrumented_sampling_);
+}
+
+int GpuCounterDescriptor::specs_size() const { return static_cast<int>(specs_.size()); }
+void GpuCounterDescriptor::clear_specs() { specs_.clear(); }
+GpuCounterDescriptor_GpuCounterSpec* GpuCounterDescriptor::add_specs() { specs_.emplace_back(); return &specs_.back(); }
+int GpuCounterDescriptor::blocks_size() const { return static_cast<int>(blocks_.size()); }
+void GpuCounterDescriptor::clear_blocks() { blocks_.clear(); }
+GpuCounterDescriptor_GpuCounterBlock* GpuCounterDescriptor::add_blocks() { blocks_.emplace_back(); return &blocks_.back(); }
+bool GpuCounterDescriptor::ParseFromArray(const void* raw, size_t size) {
+ specs_.clear();
+ blocks_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* specs */:
+ specs_.emplace_back();
+ specs_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 2 /* blocks */:
+ blocks_.emplace_back();
+ blocks_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 3 /* min_sampling_period_ns */:
+ field.get(&min_sampling_period_ns_);
+ break;
+ case 4 /* max_sampling_period_ns */:
+ field.get(&max_sampling_period_ns_);
+ break;
+ case 5 /* supports_instrumented_sampling */:
+ field.get(&supports_instrumented_sampling_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string GpuCounterDescriptor::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> GpuCounterDescriptor::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void GpuCounterDescriptor::Serialize(::protozero::Message* msg) const {
+ // Field 1: specs
+ for (auto& it : specs_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+ }
+
+ // Field 2: blocks
+ for (auto& it : blocks_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+ }
+
+ // Field 3: min_sampling_period_ns
+ if (_has_field_[3]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(3, min_sampling_period_ns_, msg);
+ }
+
+ // Field 4: max_sampling_period_ns
+ if (_has_field_[4]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(4, max_sampling_period_ns_, msg);
+ }
+
+ // Field 5: supports_instrumented_sampling
+ if (_has_field_[5]) {
+ ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, supports_instrumented_sampling_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+GpuCounterDescriptor_GpuCounterBlock::GpuCounterDescriptor_GpuCounterBlock() = default;
+GpuCounterDescriptor_GpuCounterBlock::~GpuCounterDescriptor_GpuCounterBlock() = default;
+GpuCounterDescriptor_GpuCounterBlock::GpuCounterDescriptor_GpuCounterBlock(const GpuCounterDescriptor_GpuCounterBlock&) = default;
+GpuCounterDescriptor_GpuCounterBlock& GpuCounterDescriptor_GpuCounterBlock::operator=(const GpuCounterDescriptor_GpuCounterBlock&) = default;
+GpuCounterDescriptor_GpuCounterBlock::GpuCounterDescriptor_GpuCounterBlock(GpuCounterDescriptor_GpuCounterBlock&&) noexcept = default;
+GpuCounterDescriptor_GpuCounterBlock& GpuCounterDescriptor_GpuCounterBlock::operator=(GpuCounterDescriptor_GpuCounterBlock&&) = default;
+
+bool GpuCounterDescriptor_GpuCounterBlock::operator==(const GpuCounterDescriptor_GpuCounterBlock& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(block_id_, other.block_id_)
+ && ::protozero::internal::gen_helpers::EqualsField(block_capacity_, other.block_capacity_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+ && ::protozero::internal::gen_helpers::EqualsField(description_, other.description_)
+ && ::protozero::internal::gen_helpers::EqualsField(counter_ids_, other.counter_ids_);
+}
+
+bool GpuCounterDescriptor_GpuCounterBlock::ParseFromArray(const void* raw, size_t size) {
+ counter_ids_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* block_id */:
+ field.get(&block_id_);
+ break;
+ case 2 /* block_capacity */:
+ field.get(&block_capacity_);
+ break;
+ case 3 /* name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+ break;
+ case 4 /* description */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &description_);
+ break;
+ case 5 /* counter_ids */:
+ counter_ids_.emplace_back();
+ field.get(&counter_ids_.back());
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string GpuCounterDescriptor_GpuCounterBlock::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> GpuCounterDescriptor_GpuCounterBlock::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void GpuCounterDescriptor_GpuCounterBlock::Serialize(::protozero::Message* msg) const {
+ // Field 1: block_id
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(1, block_id_, msg);
+ }
+
+ // Field 2: block_capacity
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(2, block_capacity_, msg);
+ }
+
+ // Field 3: name
+ if (_has_field_[3]) {
+ ::protozero::internal::gen_helpers::SerializeString(3, name_, msg);
+ }
+
+ // Field 4: description
+ if (_has_field_[4]) {
+ ::protozero::internal::gen_helpers::SerializeString(4, description_, msg);
+ }
+
+ // Field 5: counter_ids
+ for (auto& it : counter_ids_) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(5, it, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+GpuCounterDescriptor_GpuCounterSpec::GpuCounterDescriptor_GpuCounterSpec() = default;
+GpuCounterDescriptor_GpuCounterSpec::~GpuCounterDescriptor_GpuCounterSpec() = default;
+GpuCounterDescriptor_GpuCounterSpec::GpuCounterDescriptor_GpuCounterSpec(const GpuCounterDescriptor_GpuCounterSpec&) = default;
+GpuCounterDescriptor_GpuCounterSpec& GpuCounterDescriptor_GpuCounterSpec::operator=(const GpuCounterDescriptor_GpuCounterSpec&) = default;
+GpuCounterDescriptor_GpuCounterSpec::GpuCounterDescriptor_GpuCounterSpec(GpuCounterDescriptor_GpuCounterSpec&&) noexcept = default;
+GpuCounterDescriptor_GpuCounterSpec& GpuCounterDescriptor_GpuCounterSpec::operator=(GpuCounterDescriptor_GpuCounterSpec&&) = default;
+
+bool GpuCounterDescriptor_GpuCounterSpec::operator==(const GpuCounterDescriptor_GpuCounterSpec& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(counter_id_, other.counter_id_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+ && ::protozero::internal::gen_helpers::EqualsField(description_, other.description_)
+ && ::protozero::internal::gen_helpers::EqualsField(int_peak_value_, other.int_peak_value_)
+ && ::protozero::internal::gen_helpers::EqualsField(double_peak_value_, other.double_peak_value_)
+ && ::protozero::internal::gen_helpers::EqualsField(numerator_units_, other.numerator_units_)
+ && ::protozero::internal::gen_helpers::EqualsField(denominator_units_, other.denominator_units_)
+ && ::protozero::internal::gen_helpers::EqualsField(select_by_default_, other.select_by_default_)
+ && ::protozero::internal::gen_helpers::EqualsField(groups_, other.groups_);
+}
+
+bool GpuCounterDescriptor_GpuCounterSpec::ParseFromArray(const void* raw, size_t size) {
+ numerator_units_.clear();
+ denominator_units_.clear();
+ groups_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* counter_id */:
+ field.get(&counter_id_);
+ break;
+ case 2 /* name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+ break;
+ case 3 /* description */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &description_);
+ break;
+ case 5 /* int_peak_value */:
+ field.get(&int_peak_value_);
+ break;
+ case 6 /* double_peak_value */:
+ field.get(&double_peak_value_);
+ break;
+ case 7 /* numerator_units */:
+ numerator_units_.emplace_back();
+ field.get(&numerator_units_.back());
+ break;
+ case 8 /* denominator_units */:
+ denominator_units_.emplace_back();
+ field.get(&denominator_units_.back());
+ break;
+ case 9 /* select_by_default */:
+ field.get(&select_by_default_);
+ break;
+ case 10 /* groups */:
+ groups_.emplace_back();
+ field.get(&groups_.back());
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string GpuCounterDescriptor_GpuCounterSpec::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> GpuCounterDescriptor_GpuCounterSpec::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void GpuCounterDescriptor_GpuCounterSpec::Serialize(::protozero::Message* msg) const {
+ // Field 1: counter_id
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(1, counter_id_, msg);
+ }
+
+ // Field 2: name
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
+ }
+
+ // Field 3: description
+ if (_has_field_[3]) {
+ ::protozero::internal::gen_helpers::SerializeString(3, description_, msg);
+ }
+
+ // Field 5: int_peak_value
+ if (_has_field_[5]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(5, int_peak_value_, msg);
+ }
+
+ // Field 6: double_peak_value
+ if (_has_field_[6]) {
+ ::protozero::internal::gen_helpers::SerializeFixed(6, double_peak_value_, msg);
+ }
+
+ // Field 7: numerator_units
+ for (auto& it : numerator_units_) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(7, it, msg);
+ }
+
+ // Field 8: denominator_units
+ for (auto& it : denominator_units_) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(8, it, msg);
+ }
+
+ // Field 9: select_by_default
+ if (_has_field_[9]) {
+ ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, select_by_default_, msg);
+ }
+
+ // Field 10: groups
+ for (auto& it : groups_) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(10, it, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+} // namespace perfetto
+} // namespace protos
+} // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/interceptor_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/interceptor_descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+InterceptorDescriptor::InterceptorDescriptor() = default;
+InterceptorDescriptor::~InterceptorDescriptor() = default;
+InterceptorDescriptor::InterceptorDescriptor(const InterceptorDescriptor&) = default;
+InterceptorDescriptor& InterceptorDescriptor::operator=(const InterceptorDescriptor&) = default;
+InterceptorDescriptor::InterceptorDescriptor(InterceptorDescriptor&&) noexcept = default;
+InterceptorDescriptor& InterceptorDescriptor::operator=(InterceptorDescriptor&&) = default;
+
+bool InterceptorDescriptor::operator==(const InterceptorDescriptor& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+}
+
+bool InterceptorDescriptor::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string InterceptorDescriptor::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> InterceptorDescriptor::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void InterceptorDescriptor::Serialize(::protozero::Message* msg) const {
+ // Field 1: name
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+} // namespace perfetto
+} // namespace protos
+} // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/observable_events.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/observable_events.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+ObservableEvents::ObservableEvents() = default;
+ObservableEvents::~ObservableEvents() = default;
+ObservableEvents::ObservableEvents(const ObservableEvents&) = default;
+ObservableEvents& ObservableEvents::operator=(const ObservableEvents&) = default;
+ObservableEvents::ObservableEvents(ObservableEvents&&) noexcept = default;
+ObservableEvents& ObservableEvents::operator=(ObservableEvents&&) = default;
+
+bool ObservableEvents::operator==(const ObservableEvents& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(instance_state_changes_, other.instance_state_changes_)
+ && ::protozero::internal::gen_helpers::EqualsField(all_data_sources_started_, other.all_data_sources_started_)
+ && ::protozero::internal::gen_helpers::EqualsField(clone_trigger_hit_, other.clone_trigger_hit_);
+}
+
+int ObservableEvents::instance_state_changes_size() const { return static_cast<int>(instance_state_changes_.size()); }
+void ObservableEvents::clear_instance_state_changes() { instance_state_changes_.clear(); }
+ObservableEvents_DataSourceInstanceStateChange* ObservableEvents::add_instance_state_changes() { instance_state_changes_.emplace_back(); return &instance_state_changes_.back(); }
+bool ObservableEvents::ParseFromArray(const void* raw, size_t size) {
+ instance_state_changes_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* instance_state_changes */:
+ instance_state_changes_.emplace_back();
+ instance_state_changes_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 2 /* all_data_sources_started */:
+ field.get(&all_data_sources_started_);
+ break;
+ case 3 /* clone_trigger_hit */:
+ (*clone_trigger_hit_).ParseFromArray(field.data(), field.size());
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string ObservableEvents::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ObservableEvents::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void ObservableEvents::Serialize(::protozero::Message* msg) const {
+ // Field 1: instance_state_changes
+ for (auto& it : instance_state_changes_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+ }
+
+ // Field 2: all_data_sources_started
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, all_data_sources_started_, msg);
+ }
+
+ // Field 3: clone_trigger_hit
+ if (_has_field_[3]) {
+ (*clone_trigger_hit_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+ObservableEvents_CloneTriggerHit::ObservableEvents_CloneTriggerHit() = default;
+ObservableEvents_CloneTriggerHit::~ObservableEvents_CloneTriggerHit() = default;
+ObservableEvents_CloneTriggerHit::ObservableEvents_CloneTriggerHit(const ObservableEvents_CloneTriggerHit&) = default;
+ObservableEvents_CloneTriggerHit& ObservableEvents_CloneTriggerHit::operator=(const ObservableEvents_CloneTriggerHit&) = default;
+ObservableEvents_CloneTriggerHit::ObservableEvents_CloneTriggerHit(ObservableEvents_CloneTriggerHit&&) noexcept = default;
+ObservableEvents_CloneTriggerHit& ObservableEvents_CloneTriggerHit::operator=(ObservableEvents_CloneTriggerHit&&) = default;
+
+bool ObservableEvents_CloneTriggerHit::operator==(const ObservableEvents_CloneTriggerHit& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(tracing_session_id_, other.tracing_session_id_)
+ && ::protozero::internal::gen_helpers::EqualsField(trigger_name_, other.trigger_name_)
+ && ::protozero::internal::gen_helpers::EqualsField(producer_name_, other.producer_name_)
+ && ::protozero::internal::gen_helpers::EqualsField(producer_uid_, other.producer_uid_)
+ && ::protozero::internal::gen_helpers::EqualsField(boot_time_ns_, other.boot_time_ns_);
+}
+
+bool ObservableEvents_CloneTriggerHit::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* tracing_session_id */:
+ field.get(&tracing_session_id_);
+ break;
+ case 2 /* trigger_name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &trigger_name_);
+ break;
+ case 3 /* producer_name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &producer_name_);
+ break;
+ case 4 /* producer_uid */:
+ field.get(&producer_uid_);
+ break;
+ case 5 /* boot_time_ns */:
+ field.get(&boot_time_ns_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string ObservableEvents_CloneTriggerHit::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ObservableEvents_CloneTriggerHit::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void ObservableEvents_CloneTriggerHit::Serialize(::protozero::Message* msg) const {
+ // Field 1: tracing_session_id
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(1, tracing_session_id_, msg);
+ }
+
+ // Field 2: trigger_name
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeString(2, trigger_name_, msg);
+ }
+
+ // Field 3: producer_name
+ if (_has_field_[3]) {
+ ::protozero::internal::gen_helpers::SerializeString(3, producer_name_, msg);
+ }
+
+ // Field 4: producer_uid
+ if (_has_field_[4]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(4, producer_uid_, msg);
+ }
+
+ // Field 5: boot_time_ns
+ if (_has_field_[5]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(5, boot_time_ns_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+ObservableEvents_DataSourceInstanceStateChange::ObservableEvents_DataSourceInstanceStateChange() = default;
+ObservableEvents_DataSourceInstanceStateChange::~ObservableEvents_DataSourceInstanceStateChange() = default;
+ObservableEvents_DataSourceInstanceStateChange::ObservableEvents_DataSourceInstanceStateChange(const ObservableEvents_DataSourceInstanceStateChange&) = default;
+ObservableEvents_DataSourceInstanceStateChange& ObservableEvents_DataSourceInstanceStateChange::operator=(const ObservableEvents_DataSourceInstanceStateChange&) = default;
+ObservableEvents_DataSourceInstanceStateChange::ObservableEvents_DataSourceInstanceStateChange(ObservableEvents_DataSourceInstanceStateChange&&) noexcept = default;
+ObservableEvents_DataSourceInstanceStateChange& ObservableEvents_DataSourceInstanceStateChange::operator=(ObservableEvents_DataSourceInstanceStateChange&&) = default;
+
+bool ObservableEvents_DataSourceInstanceStateChange::operator==(const ObservableEvents_DataSourceInstanceStateChange& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(producer_name_, other.producer_name_)
+ && ::protozero::internal::gen_helpers::EqualsField(data_source_name_, other.data_source_name_)
+ && ::protozero::internal::gen_helpers::EqualsField(state_, other.state_);
+}
+
+bool ObservableEvents_DataSourceInstanceStateChange::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* producer_name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &producer_name_);
+ break;
+ case 2 /* data_source_name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &data_source_name_);
+ break;
+ case 3 /* state */:
+ field.get(&state_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string ObservableEvents_DataSourceInstanceStateChange::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ObservableEvents_DataSourceInstanceStateChange::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void ObservableEvents_DataSourceInstanceStateChange::Serialize(::protozero::Message* msg) const {
+ // Field 1: producer_name
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeString(1, producer_name_, msg);
+ }
+
+ // Field 2: data_source_name
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeString(2, data_source_name_, msg);
+ }
+
+ // Field 3: state
+ if (_has_field_[3]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(3, state_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+} // namespace perfetto
+} // namespace protos
+} // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/perf_events.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/perf_events.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+FollowerEvent::FollowerEvent() = default;
+FollowerEvent::~FollowerEvent() = default;
+FollowerEvent::FollowerEvent(const FollowerEvent&) = default;
+FollowerEvent& FollowerEvent::operator=(const FollowerEvent&) = default;
+FollowerEvent::FollowerEvent(FollowerEvent&&) noexcept = default;
+FollowerEvent& FollowerEvent::operator=(FollowerEvent&&) = default;
+
+bool FollowerEvent::operator==(const FollowerEvent& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(counter_, other.counter_)
+ && ::protozero::internal::gen_helpers::EqualsField(tracepoint_, other.tracepoint_)
+ && ::protozero::internal::gen_helpers::EqualsField(raw_event_, other.raw_event_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+}
+
+bool FollowerEvent::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* counter */:
+ field.get(&counter_);
+ break;
+ case 2 /* tracepoint */:
+ (*tracepoint_).ParseFromArray(field.data(), field.size());
+ break;
+ case 3 /* raw_event */:
+ (*raw_event_).ParseFromArray(field.data(), field.size());
+ break;
+ case 4 /* name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string FollowerEvent::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> FollowerEvent::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void FollowerEvent::Serialize(::protozero::Message* msg) const {
+ // Field 1: counter
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(1, counter_, msg);
+ }
+
+ // Field 2: tracepoint
+ if (_has_field_[2]) {
+ (*tracepoint_).Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+ }
+
+ // Field 3: raw_event
+ if (_has_field_[3]) {
+ (*raw_event_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
+ }
+
+ // Field 4: name
+ if (_has_field_[4]) {
+ ::protozero::internal::gen_helpers::SerializeString(4, name_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+PerfEvents_RawEvent::PerfEvents_RawEvent() = default;
+PerfEvents_RawEvent::~PerfEvents_RawEvent() = default;
+PerfEvents_RawEvent::PerfEvents_RawEvent(const PerfEvents_RawEvent&) = default;
+PerfEvents_RawEvent& PerfEvents_RawEvent::operator=(const PerfEvents_RawEvent&) = default;
+PerfEvents_RawEvent::PerfEvents_RawEvent(PerfEvents_RawEvent&&) noexcept = default;
+PerfEvents_RawEvent& PerfEvents_RawEvent::operator=(PerfEvents_RawEvent&&) = default;
+
+bool PerfEvents_RawEvent::operator==(const PerfEvents_RawEvent& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
+ && ::protozero::internal::gen_helpers::EqualsField(config_, other.config_)
+ && ::protozero::internal::gen_helpers::EqualsField(config1_, other.config1_)
+ && ::protozero::internal::gen_helpers::EqualsField(config2_, other.config2_);
+}
+
+bool PerfEvents_RawEvent::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* type */:
+ field.get(&type_);
+ break;
+ case 2 /* config */:
+ field.get(&config_);
+ break;
+ case 3 /* config1 */:
+ field.get(&config1_);
+ break;
+ case 4 /* config2 */:
+ field.get(&config2_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string PerfEvents_RawEvent::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> PerfEvents_RawEvent::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void PerfEvents_RawEvent::Serialize(::protozero::Message* msg) const {
+ // Field 1: type
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(1, type_, msg);
+ }
+
+ // Field 2: config
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(2, config_, msg);
+ }
+
+ // Field 3: config1
+ if (_has_field_[3]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(3, config1_, msg);
+ }
+
+ // Field 4: config2
+ if (_has_field_[4]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(4, config2_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+PerfEvents_Tracepoint::PerfEvents_Tracepoint() = default;
+PerfEvents_Tracepoint::~PerfEvents_Tracepoint() = default;
+PerfEvents_Tracepoint::PerfEvents_Tracepoint(const PerfEvents_Tracepoint&) = default;
+PerfEvents_Tracepoint& PerfEvents_Tracepoint::operator=(const PerfEvents_Tracepoint&) = default;
+PerfEvents_Tracepoint::PerfEvents_Tracepoint(PerfEvents_Tracepoint&&) noexcept = default;
+PerfEvents_Tracepoint& PerfEvents_Tracepoint::operator=(PerfEvents_Tracepoint&&) = default;
+
+bool PerfEvents_Tracepoint::operator==(const PerfEvents_Tracepoint& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+ && ::protozero::internal::gen_helpers::EqualsField(filter_, other.filter_);
+}
+
+bool PerfEvents_Tracepoint::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+ break;
+ case 2 /* filter */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &filter_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string PerfEvents_Tracepoint::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> PerfEvents_Tracepoint::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void PerfEvents_Tracepoint::Serialize(::protozero::Message* msg) const {
+ // Field 1: name
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+ }
+
+ // Field 2: filter
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeString(2, filter_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+PerfEvents::PerfEvents() = default;
+PerfEvents::~PerfEvents() = default;
+PerfEvents::PerfEvents(const PerfEvents&) = default;
+PerfEvents& PerfEvents::operator=(const PerfEvents&) = default;
+PerfEvents::PerfEvents(PerfEvents&&) noexcept = default;
+PerfEvents& PerfEvents::operator=(PerfEvents&&) = default;
+
+bool PerfEvents::operator==(const PerfEvents& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
+}
+
+bool PerfEvents::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string PerfEvents::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> PerfEvents::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void PerfEvents::Serialize(::protozero::Message* msg) const {
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+PerfEvents_Timebase::PerfEvents_Timebase() = default;
+PerfEvents_Timebase::~PerfEvents_Timebase() = default;
+PerfEvents_Timebase::PerfEvents_Timebase(const PerfEvents_Timebase&) = default;
+PerfEvents_Timebase& PerfEvents_Timebase::operator=(const PerfEvents_Timebase&) = default;
+PerfEvents_Timebase::PerfEvents_Timebase(PerfEvents_Timebase&&) noexcept = default;
+PerfEvents_Timebase& PerfEvents_Timebase::operator=(PerfEvents_Timebase&&) = default;
+
+bool PerfEvents_Timebase::operator==(const PerfEvents_Timebase& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(frequency_, other.frequency_)
+ && ::protozero::internal::gen_helpers::EqualsField(period_, other.period_)
+ && ::protozero::internal::gen_helpers::EqualsField(counter_, other.counter_)
+ && ::protozero::internal::gen_helpers::EqualsField(tracepoint_, other.tracepoint_)
+ && ::protozero::internal::gen_helpers::EqualsField(raw_event_, other.raw_event_)
+ && ::protozero::internal::gen_helpers::EqualsField(timestamp_clock_, other.timestamp_clock_)
+ && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+}
+
+bool PerfEvents_Timebase::ParseFromArray(const void* raw, size_t size) {
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 2 /* frequency */:
+ field.get(&frequency_);
+ break;
+ case 1 /* period */:
+ field.get(&period_);
+ break;
+ case 4 /* counter */:
+ field.get(&counter_);
+ break;
+ case 3 /* tracepoint */:
+ (*tracepoint_).ParseFromArray(field.data(), field.size());
+ break;
+ case 5 /* raw_event */:
+ (*raw_event_).ParseFromArray(field.data(), field.size());
+ break;
+ case 11 /* timestamp_clock */:
+ field.get(×tamp_clock_);
+ break;
+ case 10 /* name */:
+ ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string PerfEvents_Timebase::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> PerfEvents_Timebase::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void PerfEvents_Timebase::Serialize(::protozero::Message* msg) const {
+ // Field 2: frequency
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(2, frequency_, msg);
+ }
+
+ // Field 1: period
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(1, period_, msg);
+ }
+
+ // Field 4: counter
+ if (_has_field_[4]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(4, counter_, msg);
+ }
+
+ // Field 3: tracepoint
+ if (_has_field_[3]) {
+ (*tracepoint_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
+ }
+
+ // Field 5: raw_event
+ if (_has_field_[5]) {
+ (*raw_event_).Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
+ }
+
+ // Field 11: timestamp_clock
+ if (_has_field_[11]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(11, timestamp_clock_, msg);
+ }
+
+ // Field 10: name
+ if (_has_field_[10]) {
+ ::protozero::internal::gen_helpers::SerializeString(10, name_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+} // namespace perfetto
+} // namespace protos
+} // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/protolog_common.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/protolog_common.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+} // namespace perfetto
+} // namespace protos
+} // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/sys_stats_counters.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/sys_stats_counters.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+} // namespace perfetto
+} // namespace protos
+} // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/trace_stats.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/trace_stats.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+TraceStats::TraceStats() = default;
+TraceStats::~TraceStats() = default;
+TraceStats::TraceStats(const TraceStats&) = default;
+TraceStats& TraceStats::operator=(const TraceStats&) = default;
+TraceStats::TraceStats(TraceStats&&) noexcept = default;
+TraceStats& TraceStats::operator=(TraceStats&&) = default;
+
+bool TraceStats::operator==(const TraceStats& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(buffer_stats_, other.buffer_stats_)
+ && ::protozero::internal::gen_helpers::EqualsField(chunk_payload_histogram_def_, other.chunk_payload_histogram_def_)
+ && ::protozero::internal::gen_helpers::EqualsField(writer_stats_, other.writer_stats_)
+ && ::protozero::internal::gen_helpers::EqualsField(producers_connected_, other.producers_connected_)
+ && ::protozero::internal::gen_helpers::EqualsField(producers_seen_, other.producers_seen_)
+ && ::protozero::internal::gen_helpers::EqualsField(data_sources_registered_, other.data_sources_registered_)
+ && ::protozero::internal::gen_helpers::EqualsField(data_sources_seen_, other.data_sources_seen_)
+ && ::protozero::internal::gen_helpers::EqualsField(tracing_sessions_, other.tracing_sessions_)
+ && ::protozero::internal::gen_helpers::EqualsField(total_buffers_, other.total_buffers_)
+ && ::protozero::internal::gen_helpers::EqualsField(chunks_discarded_, other.chunks_discarded_)
+ && ::protozero::internal::gen_helpers::EqualsField(patches_discarded_, other.patches_discarded_)
+ && ::protozero::internal::gen_helpers::EqualsField(invalid_packets_, other.invalid_packets_)
+ && ::protozero::internal::gen_helpers::EqualsField(filter_stats_, other.filter_stats_)
+ && ::protozero::internal::gen_helpers::EqualsField(flushes_requested_, other.flushes_requested_)
+ && ::protozero::internal::gen_helpers::EqualsField(flushes_succeeded_, other.flushes_succeeded_)
+ && ::protozero::internal::gen_helpers::EqualsField(flushes_failed_, other.flushes_failed_)
+ && ::protozero::internal::gen_helpers::EqualsField(final_flush_outcome_, other.final_flush_outcome_);
+}
+
+int TraceStats::buffer_stats_size() const { return static_cast<int>(buffer_stats_.size()); }
+void TraceStats::clear_buffer_stats() { buffer_stats_.clear(); }
+TraceStats_BufferStats* TraceStats::add_buffer_stats() { buffer_stats_.emplace_back(); return &buffer_stats_.back(); }
+int TraceStats::writer_stats_size() const { return static_cast<int>(writer_stats_.size()); }
+void TraceStats::clear_writer_stats() { writer_stats_.clear(); }
+TraceStats_WriterStats* TraceStats::add_writer_stats() { writer_stats_.emplace_back(); return &writer_stats_.back(); }
+bool TraceStats::ParseFromArray(const void* raw, size_t size) {
+ buffer_stats_.clear();
+ chunk_payload_histogram_def_.clear();
+ writer_stats_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* buffer_stats */:
+ buffer_stats_.emplace_back();
+ buffer_stats_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 17 /* chunk_payload_histogram_def */:
+ chunk_payload_histogram_def_.emplace_back();
+ field.get(&chunk_payload_histogram_def_.back());
+ break;
+ case 18 /* writer_stats */:
+ writer_stats_.emplace_back();
+ writer_stats_.back().ParseFromArray(field.data(), field.size());
+ break;
+ case 2 /* producers_connected */:
+ field.get(&producers_connected_);
+ break;
+ case 3 /* producers_seen */:
+ field.get(&producers_seen_);
+ break;
+ case 4 /* data_sources_registered */:
+ field.get(&data_sources_registered_);
+ break;
+ case 5 /* data_sources_seen */:
+ field.get(&data_sources_seen_);
+ break;
+ case 6 /* tracing_sessions */:
+ field.get(&tracing_sessions_);
+ break;
+ case 7 /* total_buffers */:
+ field.get(&total_buffers_);
+ break;
+ case 8 /* chunks_discarded */:
+ field.get(&chunks_discarded_);
+ break;
+ case 9 /* patches_discarded */:
+ field.get(&patches_discarded_);
+ break;
+ case 10 /* invalid_packets */:
+ field.get(&invalid_packets_);
+ break;
+ case 11 /* filter_stats */:
+ (*filter_stats_).ParseFromArray(field.data(), field.size());
+ break;
+ case 12 /* flushes_requested */:
+ field.get(&flushes_requested_);
+ break;
+ case 13 /* flushes_succeeded */:
+ field.get(&flushes_succeeded_);
+ break;
+ case 14 /* flushes_failed */:
+ field.get(&flushes_failed_);
+ break;
+ case 15 /* final_flush_outcome */:
+ field.get(&final_flush_outcome_);
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string TraceStats::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> TraceStats::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void TraceStats::Serialize(::protozero::Message* msg) const {
+ // Field 1: buffer_stats
+ for (auto& it : buffer_stats_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+ }
+
+ // Field 17: chunk_payload_histogram_def
+ for (auto& it : chunk_payload_histogram_def_) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(17, it, msg);
+ }
+
+ // Field 18: writer_stats
+ for (auto& it : writer_stats_) {
+ it.Serialize(msg->BeginNestedMessage<::protozero::Message>(18));
+ }
+
+ // Field 2: producers_connected
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(2, producers_connected_, msg);
+ }
+
+ // Field 3: producers_seen
+ if (_has_field_[3]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(3, producers_seen_, msg);
+ }
+
+ // Field 4: data_sources_registered
+ if (_has_field_[4]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(4, data_sources_registered_, msg);
+ }
+
+ // Field 5: data_sources_seen
+ if (_has_field_[5]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(5, data_sources_seen_, msg);
+ }
+
+ // Field 6: tracing_sessions
+ if (_has_field_[6]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(6, tracing_sessions_, msg);
+ }
+
+ // Field 7: total_buffers
+ if (_has_field_[7]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(7, total_buffers_, msg);
+ }
+
+ // Field 8: chunks_discarded
+ if (_has_field_[8]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(8, chunks_discarded_, msg);
+ }
+
+ // Field 9: patches_discarded
+ if (_has_field_[9]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(9, patches_discarded_, msg);
+ }
+
+ // Field 10: invalid_packets
+ if (_has_field_[10]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(10, invalid_packets_, msg);
+ }
+
+ // Field 11: filter_stats
+ if (_has_field_[11]) {
+ (*filter_stats_).Serialize(msg->BeginNestedMessage<::protozero::Message>(11));
+ }
+
+ // Field 12: flushes_requested
+ if (_has_field_[12]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(12, flushes_requested_, msg);
+ }
+
+ // Field 13: flushes_succeeded
+ if (_has_field_[13]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(13, flushes_succeeded_, msg);
+ }
+
+ // Field 14: flushes_failed
+ if (_has_field_[14]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(14, flushes_failed_, msg);
+ }
+
+ // Field 15: final_flush_outcome
+ if (_has_field_[15]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(15, final_flush_outcome_, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+TraceStats_FilterStats::TraceStats_FilterStats() = default;
+TraceStats_FilterStats::~TraceStats_FilterStats() = default;
+TraceStats_FilterStats::TraceStats_FilterStats(const TraceStats_FilterStats&) = default;
+TraceStats_FilterStats& TraceStats_FilterStats::operator=(const TraceStats_FilterStats&) = default;
+TraceStats_FilterStats::TraceStats_FilterStats(TraceStats_FilterStats&&) noexcept = default;
+TraceStats_FilterStats& TraceStats_FilterStats::operator=(TraceStats_FilterStats&&) = default;
+
+bool TraceStats_FilterStats::operator==(const TraceStats_FilterStats& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(input_packets_, other.input_packets_)
+ && ::protozero::internal::gen_helpers::EqualsField(input_bytes_, other.input_bytes_)
+ && ::protozero::internal::gen_helpers::EqualsField(output_bytes_, other.output_bytes_)
+ && ::protozero::internal::gen_helpers::EqualsField(errors_, other.errors_)
+ && ::protozero::internal::gen_helpers::EqualsField(time_taken_ns_, other.time_taken_ns_)
+ && ::protozero::internal::gen_helpers::EqualsField(bytes_discarded_per_buffer_, other.bytes_discarded_per_buffer_);
+}
+
+bool TraceStats_FilterStats::ParseFromArray(const void* raw, size_t size) {
+ bytes_discarded_per_buffer_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* input_packets */:
+ field.get(&input_packets_);
+ break;
+ case 2 /* input_bytes */:
+ field.get(&input_bytes_);
+ break;
+ case 3 /* output_bytes */:
+ field.get(&output_bytes_);
+ break;
+ case 4 /* errors */:
+ field.get(&errors_);
+ break;
+ case 5 /* time_taken_ns */:
+ field.get(&time_taken_ns_);
+ break;
+ case 20 /* bytes_discarded_per_buffer */:
+ bytes_discarded_per_buffer_.emplace_back();
+ field.get(&bytes_discarded_per_buffer_.back());
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string TraceStats_FilterStats::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> TraceStats_FilterStats::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void TraceStats_FilterStats::Serialize(::protozero::Message* msg) const {
+ // Field 1: input_packets
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(1, input_packets_, msg);
+ }
+
+ // Field 2: input_bytes
+ if (_has_field_[2]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(2, input_bytes_, msg);
+ }
+
+ // Field 3: output_bytes
+ if (_has_field_[3]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(3, output_bytes_, msg);
+ }
+
+ // Field 4: errors
+ if (_has_field_[4]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(4, errors_, msg);
+ }
+
+ // Field 5: time_taken_ns
+ if (_has_field_[5]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(5, time_taken_ns_, msg);
+ }
+
+ // Field 20: bytes_discarded_per_buffer
+ for (auto& it : bytes_discarded_per_buffer_) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(20, it, msg);
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+TraceStats_WriterStats::TraceStats_WriterStats() = default;
+TraceStats_WriterStats::~TraceStats_WriterStats() = default;
+TraceStats_WriterStats::TraceStats_WriterStats(const TraceStats_WriterStats&) = default;
+TraceStats_WriterStats& TraceStats_WriterStats::operator=(const TraceStats_WriterStats&) = default;
+TraceStats_WriterStats::TraceStats_WriterStats(TraceStats_WriterStats&&) noexcept = default;
+TraceStats_WriterStats& TraceStats_WriterStats::operator=(TraceStats_WriterStats&&) = default;
+
+bool TraceStats_WriterStats::operator==(const TraceStats_WriterStats& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(sequence_id_, other.sequence_id_)
+ && ::protozero::internal::gen_helpers::EqualsField(buffer_, other.buffer_)
+ && ::protozero::internal::gen_helpers::EqualsField(chunk_payload_histogram_counts_, other.chunk_payload_histogram_counts_)
+ && ::protozero::internal::gen_helpers::EqualsField(chunk_payload_histogram_sum_, other.chunk_payload_histogram_sum_);
+}
+
+bool TraceStats_WriterStats::ParseFromArray(const void* raw, size_t size) {
+ chunk_payload_histogram_counts_.clear();
+ chunk_payload_histogram_sum_.clear();
+ unknown_fields_.clear();
+ bool packed_error = false;
+
+ ::protozero::ProtoDecoder dec(raw, size);
+ for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+ if (field.id() < _has_field_.size()) {
+ _has_field_.set(field.id());
+ }
+ switch (field.id()) {
+ case 1 /* sequence_id */:
+ field.get(&sequence_id_);
+ break;
+ case 4 /* buffer */:
+ field.get(&buffer_);
+ break;
+ case 2 /* chunk_payload_histogram_counts */:
+ if (!::protozero::internal::gen_helpers::DeserializePackedRepeated<::protozero::proto_utils::ProtoWireType::kVarInt, uint64_t>(field, &chunk_payload_histogram_counts_)) {
+ packed_error = true;}
+ break;
+ case 3 /* chunk_payload_histogram_sum */:
+ if (!::protozero::internal::gen_helpers::DeserializePackedRepeated<::protozero::proto_utils::ProtoWireType::kVarInt, int64_t>(field, &chunk_payload_histogram_sum_)) {
+ packed_error = true;}
+ break;
+ default:
+ field.SerializeAndAppendTo(&unknown_fields_);
+ break;
+ }
+ }
+ return !packed_error && !dec.bytes_left();
+}
+
+std::string TraceStats_WriterStats::SerializeAsString() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> TraceStats_WriterStats::SerializeAsArray() const {
+ ::protozero::internal::gen_helpers::MessageSerializer msg;
+ Serialize(msg.get());
+ return msg.SerializeAsArray();
+}
+
+void TraceStats_WriterStats::Serialize(::protozero::Message* msg) const {
+ // Field 1: sequence_id
+ if (_has_field_[1]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(1, sequence_id_, msg);
+ }
+
+ // Field 4: buffer
+ if (_has_field_[4]) {
+ ::protozero::internal::gen_helpers::SerializeVarInt(4, buffer_, msg);
+ }
+
+ // Field 2: chunk_payload_histogram_counts
+ {
+ ::protozero::PackedVarInt pack;
+ for (auto& it : chunk_payload_histogram_counts_)
+ pack.Append(it);
+ msg->AppendBytes(2, pack.data(), pack.size());
+ }
+
+ // Field 3: chunk_payload_histogram_sum
+ {
+ ::protozero::PackedVarInt pack;
+ for (auto& it : chunk_payload_histogram_sum_)
+ pack.Append(it);
+ msg->AppendBytes(3, pack.data(), pack.size());
+ }
+
+ protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+TraceStats_BufferStats::TraceStats_BufferStats() = default;
+TraceStats_BufferStats::~TraceStats_BufferStats() = default;
+TraceStats_BufferStats::TraceStats_BufferStats(const TraceStats_BufferStats&) = default;
+TraceStats_BufferStats& TraceStats_BufferStats::operator=(const TraceStats_BufferStats&) = default;
+TraceStats_BufferStats::TraceStats_BufferStats(TraceStats_BufferStats&&) noexcept = default;
+TraceStats_BufferStats& TraceStats_BufferStats::operator=(TraceStats_BufferStats&&) = default;
+
+bool TraceStats_BufferStats::operator==(const TraceStats_BufferStats& other) const {
+ return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+ && ::protozero::internal::gen_helpers::EqualsField(buffer_size_, other.buffer_size_)
+ && ::protozero::internal::gen_helpers::EqualsField(bytes_written_, other.bytes_written_)
+ && ::protozero::internal::gen_helpers::EqualsField(bytes_overwritten_, other.bytes_overwritten_)
+ && ::protozero::internal::gen_helpers::EqualsField(bytes_read_, other.bytes_read_)
+ && ::protozero::internal::gen_helpers::EqualsField(padding_bytes_written_, other.padding_bytes_written_)
+ && ::protozero::internal::gen_helpers::EqualsField(padding_bytes_cleared_, other.padding_bytes_cleared_)
+ && ::protozero::internal::gen_helpers::EqualsField(chunks_written_, other.chunks_written_)
+ && ::protozero::internal::gen_helpers::EqualsField(chunks_rewritten_, other.chunks_rewritten_)
+ && ::protozero::internal::gen_helpers::EqualsField(chunks_overwritten_, other.chunks_overwritten_)
+ && ::protozero::internal::gen_helpers::EqualsField(chunks_discarded_, other.chunks_discarded_)