Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 47839580 authored by Zimuzo Ezeozue's avatar Zimuzo Ezeozue Committed by Android (Google) Code Review
Browse files

Merge "Support a PerfettoTrace Java API" into main

parents d0b8504b 14d13809
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ cc_library_shared {
    srcs: [
        "tracing_perfetto.cpp",
        "tracing_perfetto_internal.cpp",
        "tracing_sdk.cpp",
    ],

    shared_libs: [
@@ -45,6 +46,10 @@ cc_library_shared {
        "libperfetto_c",
    ],

    export_shared_lib_headers: [
        "libperfetto_c",
    ],

    host_supported: true,
    // for vndbinder
    vendor_available: true,
+461 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#pragma once

#include <android-base/logging.h>
#include <stdint.h>

#include <optional>
#include <vector>

#include "perfetto/public/producer.h"
#include "perfetto/public/te_category_macros.h"
#include "perfetto/public/te_macros.h"
#include "perfetto/public/track_event.h"

/**
 * The objects declared here are intended to be managed by Java.
 * This means the Java Garbage Collector is responsible for freeing the
 * underlying native resources.
 *
 * The static methods prefixed with `delete_` are special. They are designed to be
 * invoked by Java through the `NativeAllocationRegistry` when the
 * corresponding Java object becomes unreachable.  These methods act as
 * callbacks to ensure proper deallocation of native resources.
 */
namespace tracing_perfetto {
/**
 * @brief Represents extra data associated with a trace event.
 * This class manages a collection of PerfettoTeHlExtra pointers.
 */
class Extra;

/**
 * @brief Emits a trace event.
 * @param type The type of the event.
 * @param cat The category of the event.
 * @param name The name of the event.
 * @param arg_ptr Pointer to Extra data.
 */
void trace_event(int type, const PerfettoTeCategory* cat, const char* name,
                 Extra* extra);

/**
 * @brief Gets the process track UUID.
 */
uint64_t get_process_track_uuid();

/**
 * @brief Gets the thread track UUID for a given PID.
 */
uint64_t get_thread_track_uuid(pid_t tid);

/**
 * @brief Holder for all the other classes in the file.
 */
class Extra {
 public:
  Extra();
  void push_extra(PerfettoTeHlExtra* extra);
  void pop_extra();
  void clear_extras();
  static void delete_extra(Extra* extra);

  PerfettoTeHlExtra* const* get() const;

 private:
  DISALLOW_COPY_AND_ASSIGN(Extra);

  // These PerfettoTeHlExtra pointers are really pointers to all the other
  // types of extras: Category, DebugArg, Counter etc. Those objects are
  // individually managed by Java.
  std::vector<PerfettoTeHlExtra*> extras_;
};

/**
 * @brief Represents a trace event category.
 */
class Category {
 public:
  Category(const std::string& name, const std::string& tag,
           const std::string& severity);

  ~Category();

  void register_category();

  void unregister_category();

  bool is_category_enabled();

  static void delete_category(Category* category);

  const PerfettoTeCategory* get() const;

 private:
  DISALLOW_COPY_AND_ASSIGN(Category);
  PerfettoTeCategory category_;
  const std::string name_;
  const std::string tag_;
  const std::string severity_;
};

/**
 * @brief Represents one end of a flow between two events.
 */
class Flow {
 public:
  Flow();

  void set_process_flow(uint64_t id);
  void set_process_terminating_flow(uint64_t id);
  static void delete_flow(Flow* flow);

  const PerfettoTeHlExtraFlow* get() const;

 private:
  DISALLOW_COPY_AND_ASSIGN(Flow);
  PerfettoTeHlExtraFlow flow_;
};

/**
 * @brief Represents a named track.
 */
class NamedTrack {
 public:
  NamedTrack(uint64_t id, uint64_t parent_uuid, const std::string& name);

  static void delete_track(NamedTrack* track);

  const PerfettoTeHlExtraNamedTrack* get() const;

 private:
  DISALLOW_COPY_AND_ASSIGN(NamedTrack);
  const std::string name_;
  PerfettoTeHlExtraNamedTrack track_;
};

/**
 * @brief Represents a registered track.
 */
class RegisteredTrack {
 public:
  RegisteredTrack(uint64_t id, uint64_t parent_uuid, const std::string& name,
                  bool is_counter);
  ~RegisteredTrack();

  void register_track();
  void unregister_track();
  static void delete_track(RegisteredTrack* track);

  const PerfettoTeHlExtraRegisteredTrack* get() const;

 private:
  DISALLOW_COPY_AND_ASSIGN(RegisteredTrack);
  PerfettoTeRegisteredTrack registered_track_;
  PerfettoTeHlExtraRegisteredTrack track_;
  const std::string name_;
  const uint64_t id_;
  const uint64_t parent_uuid_;
  const bool is_counter_;
};

/**
 * @brief Represents a counter track event.
 * @tparam T The data type of the counter (int64_t or double).
 */
template <typename T>
class Counter {
 public:
  template <typename>
  struct always_false : std::false_type {};

  struct TypeMap {
    using type = std::invoke_result_t<decltype([]() {
      if constexpr (std::is_same_v<T, int64_t>) {
        return std::type_identity<PerfettoTeHlExtraCounterInt64>{};
      } else if constexpr (std::is_same_v<T, double>) {
        return std::type_identity<PerfettoTeHlExtraCounterDouble>{};
      } else {
        return std::type_identity<void>{};
      }
    })>::type;

    static constexpr int enum_value = []() {
      if constexpr (std::is_same_v<T, int64_t>) {
        return PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_INT64;
      } else if constexpr (std::is_same_v<T, double>) {
        return PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_DOUBLE;
      } else {
        static_assert(always_false<T>::value, "Unsupported type");
        return 0;  // Never reached, just to satisfy return type
      }
    }();
  };

  Counter() {
    static_assert(!std::is_same_v<typename TypeMap::type, void>,
                  "Unsupported type for Counter");

    typename TypeMap::type counter;
    counter.header = {TypeMap::enum_value};
    counter_ = std::move(counter);
  }

  void set_value(T value) {
    if constexpr (std::is_same_v<T, int64_t>) {
      counter_.value = value;
    } else if constexpr (std::is_same_v<T, double>) {
      counter_.value = value;
    }
  }

  static void delete_counter(Counter* counter) {
    delete counter;
  }

  const TypeMap::type* get() const {
    return &counter_;
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(Counter);
  TypeMap::type counter_;
};

/**
 * @brief Represents a debug argument for a trace event.
 * @tparam T The data type of the argument (bool, int64_t, double, const char*).
 */
template <typename T>
class DebugArg {
 public:
  template <typename>
  struct always_false : std::false_type {};

  struct TypeMap {
    using type = std::invoke_result_t<decltype([]() {
      if constexpr (std::is_same_v<T, bool>) {
        return std::type_identity<PerfettoTeHlExtraDebugArgBool>{};
      } else if constexpr (std::is_same_v<T, int64_t>) {
        return std::type_identity<PerfettoTeHlExtraDebugArgInt64>{};
      } else if constexpr (std::is_same_v<T, double>) {
        return std::type_identity<PerfettoTeHlExtraDebugArgDouble>{};
      } else if constexpr (std::is_same_v<T, const char*>) {
        return std::type_identity<PerfettoTeHlExtraDebugArgString>{};
      } else {
        return std::type_identity<void>{};
      }
    })>::type;

    static constexpr int enum_value = []() {
      if constexpr (std::is_same_v<T, bool>) {
        return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_BOOL;
      } else if constexpr (std::is_same_v<T, int64_t>) {
        return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_INT64;
      } else if constexpr (std::is_same_v<T, double>) {
        return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_DOUBLE;
      } else if constexpr (std::is_same_v<T, const char*>) {
        return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_STRING;
      } else {
        static_assert(always_false<T>::value, "Unsupported type");
        return 0;  // Never reached, just to satisfy return type
      }
    }();
  };

  DebugArg(const std::string& name) : name_(name) {
    static_assert(!std::is_same_v<typename TypeMap::type, void>,
                  "Unsupported type for DebugArg");

    typename TypeMap::type arg;
    arg.header = {TypeMap::enum_value};
    arg.name = name_.c_str();
    arg_ = std::move(arg);
  }

  ~DebugArg() {
    free_string_value();
  }

  void set_value(T value) {
    if constexpr (std::is_same_v<T, const char*>) {
      free_string_value();
      arg_.value = value;
    } else if constexpr (std::is_same_v<T, int64_t>) {
      arg_.value = value;
    } else if constexpr (std::is_same_v<T, bool>) {
      arg_.value = value;
    } else if constexpr (std::is_same_v<T, double>) {
      arg_.value = value;
    }
  }

  static void delete_arg(DebugArg* arg) {
    delete arg;
  }

  const TypeMap::type* get() const {
    return &arg_;
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(DebugArg);
  TypeMap::type arg_;
  const std::string name_;

  constexpr void free_string_value() {
    if constexpr (std::is_same_v<typename TypeMap::type,
                                 PerfettoTeHlExtraDebugArgString>) {
      if (arg_.value) {
        free((void*)arg_.value);
        arg_.value = nullptr;
      }
    }
  }
};

template <typename T>
class ProtoField {
 public:
  template <typename>
  struct always_false : std::false_type {};

  struct TypeMap {
    using type = std::invoke_result_t<decltype([]() {
      if constexpr (std::is_same_v<T, int64_t>) {
        return std::type_identity<PerfettoTeHlProtoFieldVarInt>{};
      } else if constexpr (std::is_same_v<T, double>) {
        return std::type_identity<PerfettoTeHlProtoFieldDouble>{};
      } else if constexpr (std::is_same_v<T, const char*>) {
        return std::type_identity<PerfettoTeHlProtoFieldCstr>{};
      } else {
        return std::type_identity<void>{};
      }
    })>::type;

    static constexpr PerfettoTeHlProtoFieldType enum_value = []() {
      if constexpr (std::is_same_v<T, int64_t>) {
        return PERFETTO_TE_HL_PROTO_TYPE_VARINT;
      } else if constexpr (std::is_same_v<T, double>) {
        return PERFETTO_TE_HL_PROTO_TYPE_DOUBLE;
      } else if constexpr (std::is_same_v<T, const char*>) {
        return PERFETTO_TE_HL_PROTO_TYPE_CSTR;
      } else {
        static_assert(always_false<T>::value, "Unsupported type");
        return 0;  // Never reached, just to satisfy return type
      }
    }();
  };

  ProtoField() {
    static_assert(!std::is_same_v<typename TypeMap::type, void>,
                  "Unsupported type for ProtoField");

    typename TypeMap::type arg;
    arg.header.type = TypeMap::enum_value;
    arg_ = std::move(arg);
  }

  ~ProtoField() {
    free_string_value();
  }

  void set_value(uint32_t id, T value) {
    if constexpr (std::is_same_v<T, int64_t>) {
      arg_.header.id = id;
      arg_.value = value;
    } else if constexpr (std::is_same_v<T, double>) {
      arg_.header.id = id;
      arg_.value = value;
    } else if constexpr (std::is_same_v<T, const char*>) {
      free_string_value();
      arg_.header.id = id;
      arg_.str = value;
    }
  }

  static void delete_field(ProtoField* field) {
    delete field;
  }

  const TypeMap::type* get() const {
    return &arg_;
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(ProtoField);
  TypeMap::type arg_;

  constexpr void free_string_value() {
    if constexpr (std::is_same_v<typename TypeMap::type,
                                 PerfettoTeHlProtoFieldCstr>) {
      if (arg_.str) {
        free((void*)arg_.str);
        arg_.str = nullptr;
      }
    }
  }
};

class ProtoFieldNested {
 public:
  ProtoFieldNested();

  void add_field(PerfettoTeHlProtoField* field);
  void set_id(uint32_t id);
  static void delete_field(ProtoFieldNested* field);

  const PerfettoTeHlProtoFieldNested* get() const;

 private:
  DISALLOW_COPY_AND_ASSIGN(ProtoFieldNested);
  PerfettoTeHlProtoFieldNested field_;
  // These PerfettoTeHlProtoField pointers are really pointers to all the other
  // types of protos: PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldVarInt,
  // PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldNested. Those objects are
  // individually managed by Java.
  std::vector<PerfettoTeHlProtoField*> fields_;
};

class Proto {
 public:
  Proto();

  void add_field(PerfettoTeHlProtoField* field);
  void clear_fields();
  static void delete_proto(Proto* proto);

  const PerfettoTeHlExtraProtoFields* get() const;

 private:
  DISALLOW_COPY_AND_ASSIGN(Proto);
  PerfettoTeHlExtraProtoFields proto_;
  // These PerfettoTeHlProtoField pointers are really pointers to all the other
  // types of protos: PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldVarInt,
  // PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldNested. Those objects are
  // individually managed by Java.
  std::vector<PerfettoTeHlProtoField*> fields_;
};

/**
 * @brief Activates a trigger.
 * @param name The name of the trigger.
 * @param ttl_ms The time-to-live of the trigger in milliseconds.
 */
void activate_trigger(const char* name, uint32_t ttl_ms);
}  // namespace tracing_perfetto
+32 −1
Original line number Diff line number Diff line
@@ -21,12 +21,44 @@ package {
    default_applicable_licenses: ["frameworks_native_license"],
}

cc_library_static {
    name: "libtracing_perfetto_test_utils",
    export_include_dirs: [
        "include",
    ],
    static_libs: [
        "libflagtest",
        "libgmock",
        "perfetto_trace_protos",
    ],
    cflags: [
        "-Wall",
        "-Werror",
        "-Wno-enum-compare",
        "-Wno-unused-function",
    ],

    srcs: [
        "utils.cpp",
    ],

    shared_libs: [
        "libperfetto_c",
        "liblog",
        "libprotobuf-cpp-lite",
    ],
    export_shared_lib_headers: [
        "libperfetto_c",
    ],
}

cc_test {
    name: "libtracing_perfetto_tests",
    static_libs: [
        "libflagtest",
        "libgmock",
        "perfetto_trace_protos",
        "libtracing_perfetto_test_utils",
    ],
    cflags: [
        "-Wall",
@@ -42,7 +74,6 @@ cc_test {
    ],
    srcs: [
        "tracing_perfetto_test.cpp",
        "utils.cpp",
    ],
    test_suites: ["device-tests"],
}
+124 −0
Original line number Diff line number Diff line
/*
 * Copyright 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.
 */

// Copied from //external/perfetto/src/shared_lib/test/utils.h

#ifndef UTILS_H
#define UTILS_H

#include <cassert>
#include <condition_variable>
#include <cstdint>
#include <functional>
#include <iterator>
#include <memory>
#include <mutex>
#include <ostream>
#include <string>
#include <vector>

#include "perfetto/public/abi/pb_decoder_abi.h"
#include "perfetto/public/pb_utils.h"
#include "perfetto/public/tracing_session.h"

// Pretty printer for gtest
void PrintTo(const PerfettoPbDecoderField& field, std::ostream*);

namespace perfetto {
namespace shlib {
namespace test_utils {

class WaitableEvent {
 public:
  WaitableEvent() = default;
  void Notify() {
    std::unique_lock<std::mutex> lock(m_);
    notified_ = true;
    cv_.notify_one();
  }
  bool WaitForNotification() {
    std::unique_lock<std::mutex> lock(m_);
    cv_.wait(lock, [this] { return notified_; });
    return notified_;
  }
  bool IsNotified() {
    std::unique_lock<std::mutex> lock(m_);
    return notified_;
  }

 private:
  std::mutex m_;
  std::condition_variable cv_;
  bool notified_ = false;
};

class TracingSession {
 public:
  class Builder {
   public:
    Builder() = default;
    Builder& add_enabled_category(std::string category) {
      enabled_categories_.push_back(std::move(category));
      return *this;
    }
    Builder& add_disabled_category(std::string category) {
      disabled_categories_.push_back(std::move(category));
      return *this;
    }
    Builder& add_atrace_category(std::string category) {
      atrace_categories_.push_back(std::move(category));
      return *this;
    }
    Builder& add_atrace_category_prefer_sdk(std::string category) {
      atrace_categories_prefer_sdk_.push_back(std::move(category));
      return *this;
    }
    TracingSession Build();

   private:
    std::vector<std::string> enabled_categories_;
    std::vector<std::string> disabled_categories_;
    std::vector<std::string> atrace_categories_;
    std::vector<std::string> atrace_categories_prefer_sdk_;
  };

  static TracingSession Adopt(struct PerfettoTracingSessionImpl*);
  static TracingSession FromBytes(void *buf, size_t len);

  TracingSession(TracingSession&&) noexcept;

  ~TracingSession();

  struct PerfettoTracingSessionImpl* session() const {
    return session_;
  }

  bool FlushBlocking(uint32_t timeout_ms);
  void WaitForStopped();
  void StopBlocking();
  std::vector<uint8_t> ReadBlocking();

 private:
  TracingSession() = default;
  struct PerfettoTracingSessionImpl* session_;
  std::unique_ptr<WaitableEvent> stopped_;
};

}  // namespace test_utils
}  // namespace shlib
}  // namespace perfetto

#endif  // UTILS_H
+1 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <unistd.h>

#include "gtest/gtest.h"

#include "perfetto/public/abi/data_source_abi.h"
#include "perfetto/public/abi/heap_buffer.h"
#include "perfetto/public/abi/pb_decoder_abi.h"
Loading