|
|
|
|
Quelle perfetto.cc
Sprache: C
|
|
// 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) con | | |