chromium/third_party/perfetto/include/perfetto/tracing/traced_value.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_TRACING_TRACED_VALUE_H_
#define INCLUDE_PERFETTO_TRACING_TRACED_VALUE_H_

#include "perfetto/base/compiler.h"
#include "perfetto/base/export.h"
#include "perfetto/base/template_util.h"
#include "perfetto/protozero/message.h"
#include "perfetto/protozero/proto_utils.h"
#include "perfetto/tracing/internal/checked_scope.h"
#include "perfetto/tracing/string_helpers.h"
#include "perfetto/tracing/traced_value_forward.h"

#include <memory>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>

namespace perfetto {

namespace protos {
namespace pbzero {
class DebugAnnotation;
}
}  // namespace protos

class DebugAnnotation;
class EventContext;

// These classes provide a JSON-inspired way to write structed data into traces.
//
// Each TracedValue can be consumed exactly once to write a value into a trace
// using one of the Write* methods.
//
// Write* methods fall into two categories:
// - Primitive types (int, string, bool, double, etc): they just write the
//   provided value, consuming the TracedValue in the process.
// - Complex types (arrays and dicts): they consume the TracedValue and
//   return a corresponding scoped object (TracedArray or TracedDictionary).
//   This scope then can be used to write multiple items into the container:
//   TracedArray::AppendItem and TracedDictionary::AddItem return a new
//   TracedValue which then can be used to write an element of the
//   dictionary or array.
//
// To define how a custom class should be written into the trace, users should
// define one of the two following functions:
// - Foo::WriteIntoTrace(TracedValue) const
//   (preferred for code which depends on perfetto directly)
// - perfetto::TraceFormatTraits<T>::WriteIntoTrace(
//       TracedValue, const T&);
//   (should be used if T is defined in a library which doesn't know anything
//   about tracing).
//
//
// After defining a conversion method, the object can be used directly as a
// TRACE_EVENT argument:
//
// Foo foo;
// TRACE_EVENT("cat", "Event", "arg", foo);
//
// Examples:
//
// TRACE_EVENT("cat", "event", "params", [&](perfetto::TracedValue context)
// {
//   auto dict = std::move(context).WriteDictionary();
//   dict->Add("param1", param1);
//   dict->Add("param2", param2);
//   ...
//   dict->Add("paramN", paramN);
//
//   {
//     auto inner_array = dict->AddArray("inner");
//     inner_array->Append(value1);
//     inner_array->Append(value2);
//   }
// });
//
// template <typename T>
// TraceFormatTraits<std::optional<T>>::WriteIntoTrace(
//    TracedValue context, const std::optional<T>& value) {
//  if (!value) {
//    std::move(context).WritePointer(nullptr);
//    return;
//  }
//  perfetto::WriteIntoTrace(std::move(context), *value);
// }
//
// template <typename T>
// TraceFormatTraits<std::vector<T>>::WriteIntoTrace(
//    TracedValue context, const std::array<T>& value) {
//  auto array = std::move(context).WriteArray();
//  for (const auto& item: value) {
//    array_scope.Append(item);
//  }
// }
//
// class Foo {
//   void WriteIntoTrace(TracedValue context) const {
//     auto dict = std::move(context).WriteDictionary();
//     dict->Set("key", 42);
//     dict->Set("foo", "bar");
//     dict->Set("member", member_);
//   }
// }
namespace internal {
// TODO(altimin): Currently EventContext can be null due the need to support
// TracedValue-based serialisation with the Chrome's TraceLog. After this is
// gone, the second parameter should be changed to EventContext&.
PERFETTO_EXPORT_COMPONENT TracedValue
CreateTracedValueFromProto(protos::pbzero::DebugAnnotation*,
                           EventContext* = nullptr);
}

class PERFETTO_EXPORT_COMPONENT TracedValue {};

template <typename MessageType>
TracedProto<MessageType> TracedValue::WriteProto() && {}

class PERFETTO_EXPORT_COMPONENT TracedArray {};

class PERFETTO_EXPORT_COMPONENT TracedDictionary {};

namespace internal {

// SFINAE helpers for finding a right overload to convert a given class to
// trace-friendly form, ordered from most to least preferred.

constexpr int kMaxWriteImplPriority =;

// If T has WriteIntoTracedValue member function, call it.
template <typename T>
decltype(std::declval<T>().WriteIntoTracedValue(std::declval<TracedValue>()),
         void())
WriteImpl(base::priority_tag<4>, TracedValue context, T&& value) {}

// If T has WriteIntoTrace member function, call it.
template <typename T>
decltype(std::declval<T>().WriteIntoTrace(std::declval<TracedValue>()), void())
WriteImpl(base::priority_tag<4>, TracedValue context, T&& value) {}

// If perfetto::TraceFormatTraits<T>::WriteIntoTracedValue(TracedValue, const
// T&) is available, use it.
template <typename T>
decltype(TraceFormatTraits<base::remove_cvref_t<T>>::WriteIntoTracedValue(
             std::declval<TracedValue>(),
             std::declval<T>()),
         void())
WriteImpl(base::priority_tag<3>, TracedValue context, T&& value) {}

// If perfetto::TraceFormatTraits<T>::WriteIntoTrace(TracedValue, const T&)
// is available, use it.
template <typename T>
decltype(TraceFormatTraits<base::remove_cvref_t<T>>::WriteIntoTrace(
             std::declval<TracedValue>(),
             std::declval<T>()),
         void())
WriteImpl(base::priority_tag<3>, TracedValue context, T&& value) {}

// If T has operator(), which takes TracedValue, use it.
// Very useful for lambda resolutions.
template <typename T>
decltype(std::declval<T>()(std::declval<TracedValue>()), void())
WriteImpl(base::priority_tag<2>, TracedValue context, T&& value) {}

// If T is a container and its elements have tracing support, use it.
//
// Note: a reference to T should be passed to std::begin, otherwise
// for non-reference types const T& will be passed to std::begin, losing
// support for non-const WriteIntoTracedValue methods.
template <typename T>
typename check_traced_value_support<
    decltype(*std::begin(std::declval<T&>()))>::type
WriteImpl(base::priority_tag<1>, TracedValue context, T&& value) {}

// std::underlying_type can't be used with non-enum types, so we need this
// indirection.
template <typename T, bool = std::is_enum<T>::value>
struct safe_underlying_type {};

safe_underlying_type<T, false>;

template <typename T>
struct is_incomplete_type {};

// sizeof is not available for const char[], but it's still not considered to be
// an incomplete type for our purposes as the size can be determined at runtime
// due to strings being null-terminated.
template <>
struct is_incomplete_type<const char[]> {};

}  // namespace internal

// Helper template to determine if a given type can be passed to
// perfetto::WriteIntoTracedValue. These templates will fail to resolve if the
// class does not have it support, so they are useful in SFINAE and in producing
// helpful compiler results.
check_traced_value_support_t;

// check_traced_value_support<T, V>::type is defined (and equal to V) iff T
// supports being passed to WriteIntoTracedValue. See the comment in
// traced_value_forward.h for more details.
check_traced_value_support<T, Result, check_traced_value_support_t<T, Result>>;

namespace internal {

// Helper class to check if a given type can be passed to
// perfetto::WriteIntoTracedValue. This template will always resolve (with
// |value| being set to either true or false depending on presence of the
// support, so this macro is useful in the situation when you want to e.g. OR
// the result with some other conditions.
//
// In this case, compiler will not give you the full deduction chain, so, for
// example, use check_traced_value_support for writing positive static_asserts
// and has_traced_value_support for writing negative.
template <typename T>
class has_traced_value_support {};

}  // namespace internal

template <typename T>
void WriteIntoTracedValue(TracedValue context, T&& value) {}

// Helpers to write a given value into TracedValue even if the given type
// doesn't support conversion (in which case the provided fallback should be
// used). Useful for automatically generating conversions for autogenerated
// code, but otherwise shouldn't be used as non-autogenerated code is expected
// to define WriteIntoTracedValue convertor.
// See WriteWithFallback test in traced_value_unittest.cc for a concrete
// example.
template <typename T>
typename std::enable_if<internal::has_traced_value_support<T>::value>::type
WriteIntoTracedValueWithFallback(TracedValue context,
                                 T&& value,
                                 const std::string&) {}

template <typename T>
typename std::enable_if<!internal::has_traced_value_support<T>::value>::type
WriteIntoTracedValueWithFallback(TracedValue context,
                                 T&&,
                                 const std::string& fallback) {}

// TraceFormatTraits implementations for primitive types.

// Specialisation for signed integer types (note: it excludes enums, which have
// their own explicit specialisation).
TraceFormatTraits<T, typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value && std::is_signed<T>::value>::type>;

// Specialisation for unsigned integer types (note: it excludes enums, which
// have their own explicit specialisation).
TraceFormatTraits<T, typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value && std::is_unsigned<T>::value>::type>;

// Specialisation for bools.
template <>
struct TraceFormatTraits<bool> {};

// Specialisation for floating point values.
TraceFormatTraits<T, typename std::enable_if<std::is_floating_point<T>::value>::type>;

// Specialisation for signed enums.
TraceFormatTraits<T, typename std::enable_if<std::is_enum<T>::value && std::is_signed<typename internal::safe_underlying_type<T>::type>::value>::type>;

// Specialisation for unsigned enums.
TraceFormatTraits<T, typename std::enable_if<std::is_enum<T>::value && std::is_unsigned<typename internal::safe_underlying_type<T>::type>::value>::type>;

// Specialisations for C-style strings.
template <>
struct TraceFormatTraits<const char*> {};

template <>
struct TraceFormatTraits<char[]> {};

TraceFormatTraits<char[N]>;

// Specialization for Perfetto strings.
template <>
struct TraceFormatTraits<perfetto::StaticString> {};

template <>
struct TraceFormatTraits<perfetto::DynamicString> {};

// Specialisation for C++ strings.
template <>
struct TraceFormatTraits<std::string> {};

// Specialisation for C++ string_views.
template <>
struct TraceFormatTraits<std::string_view> {};

// Specialisation for (const) void*, which writes the pointer value.
template <>
struct TraceFormatTraits<void*> {};

template <>
struct TraceFormatTraits<const void*> {};

// Specialisation for std::unique_ptr<>, which writes either nullptr or the
// object it points to.
TraceFormatTraits<std::unique_ptr<T>, check_traced_value_support_t<T>>;

// Specialisation for raw pointer, which writes either nullptr or the object it
// points to.
TraceFormatTraits<T *, check_traced_value_support_t<T>>;

// Specialisation for nullptr.
template <>
struct TraceFormatTraits<std::nullptr_t> {};

}  // namespace perfetto

#endif  // INCLUDE_PERFETTO_TRACING_TRACED_VALUE_H_