// 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 IORAP_SRC_PERFETTO_RX_PRODUCER_H_
|
#define IORAP_SRC_PERFETTO_RX_PRODUCER_H_
|
|
#include "perfetto/perfetto_consumer.h" // libiorap
|
|
#include <perfetto/config/trace_config.pb.h> // libperfetto
|
#include <rxcpp/rx.hpp>
|
|
#include <iosfwd>
|
#include <functional>
|
#include <optional>
|
#include <vector>
|
|
namespace iorap::perfetto {
|
|
struct PerfettoDependencies {
|
using Component =
|
fruit::Component<PerfettoConsumer, ::perfetto::protos::TraceConfig>;
|
using Injector =
|
fruit::Injector<PerfettoConsumer, ::perfetto::protos::TraceConfig>;
|
using NormalizedComponent =
|
fruit::NormalizedComponent<PerfettoConsumer, ::perfetto::protos::TraceConfig>;
|
|
// Create a 'live' component that will talk to perfetto via traced.
|
static Component CreateComponent(/*TODO: config params*/);
|
|
// Create perfetto.protos.TraceConfig , serialized as a (machine-readable) string.
|
//
|
// The following ftrace events are enabled:
|
// * mm_filemap_add_to_page_cache
|
// * mm_filemap_delete_from_page_cache
|
//
|
// If deferred starting is also enabled, no tracing will begin until
|
// ::perfetto::consumer::StartTracing is invoked.
|
static ::perfetto::protos::TraceConfig CreateConfig(uint32_t duration_ms,
|
bool deferred_start = true,
|
uint32_t buffer_size = 4096);
|
};
|
|
// This acts as a lightweight type marker so that we know what data has actually
|
// encoded under the hood.
|
template <typename T>
|
struct BinaryWireProtobuf {
|
std::vector<std::byte>& data() {
|
return data_;
|
}
|
|
const std::vector<std::byte>& data() const {
|
return data_;
|
}
|
|
size_t size() const {
|
return data_.size();
|
}
|
|
explicit BinaryWireProtobuf(char* data, size_t size)
|
: BinaryWireProtobuf(reinterpret_cast<std::byte*>(data), size) {
|
}
|
|
explicit BinaryWireProtobuf(std::byte* data, size_t size) {
|
data_.resize(size);
|
std::copy(data,
|
data + size,
|
data_.data());
|
}
|
|
// Important: Deserialization could fail, for example data is truncated or
|
// some minor disc corruption occurred.
|
template <typename U>
|
std::optional<U> MaybeUnserialize() {
|
U unencoded;
|
|
if (!unencoded.ParseFromArray(data_.data(), data_.size())) {
|
return std::nullopt;
|
}
|
|
return {std::move(unencoded)};
|
}
|
|
bool WriteFullyToFile(const std::string& path,
|
bool follow_symlinks = false) const;
|
|
private:
|
static bool CleanUpAfterFailedWrite(const std::string& path);
|
bool WriteStringToFd(int fd) const;
|
|
std::vector<std::byte> data_;
|
};
|
|
//using PerfettoTraceProto = BinaryWireProtobuf<::perfetto::protos::Trace>;
|
using PerfettoTraceProto = BinaryWireProtobuf<::google::protobuf::MessageLite>;
|
|
enum class PerfettoStreamCommand {
|
kStartTracing, // -> () | on_error
|
kStopTracing, // -> on_next(PerfettoTraceProto) | on_error
|
kShutdown, // -> on_completed | on_error
|
// XX: should shutdown be converted to use the rx suscriber#unsubscribe instead?
|
};
|
|
std::ostream& operator<<(std::ostream& os, PerfettoStreamCommand c);
|
|
struct RxProducerFactory {
|
// Passing anything by value leads to a lot of pain and headache.
|
// Pass in the injector by reference because nothing else seems to work.
|
explicit RxProducerFactory(PerfettoDependencies::Injector& injector);
|
|
// Create a one-shot perfetto observable that will begin
|
// asynchronously producing a PerfettoTraceProto after the 'kStartTracing'
|
// command is observed.
|
//
|
// libperfetto is immediately primed (i.e. connected in a deferred state)
|
// upon calling this function, to reduce the latency of 'kStartTracing'.
|
//
|
// To finish the trace, push 'kStopTracing'. To cancel or tear down at any
|
// time, push 'kShutdown'.
|
//
|
// The TraceProto may come out at any time after 'kStartTracing',
|
// this is controlled by duration_ms in the TraceConfig.
|
//
|
// TODO: libperfetto should actually stop tracing when we ask it to,
|
// instead of using a hardcoded time.
|
//
|
// The observable may go into #on_error at any time, if the underlying
|
// libperfetto states transition to a failing state.
|
// This usually means the OS is not configured correctly.
|
rxcpp::observable<PerfettoTraceProto> CreateTraceStream(
|
rxcpp::observable<PerfettoStreamCommand> commands);
|
|
// TODO: is this refactor-able into a subscriber factory that takes
|
// the commands-observable as a parameter?
|
|
// TODO: infinite perfetto stream.
|
|
private:
|
// XX: why doesn't this just let me pass in a regular Component?
|
PerfettoDependencies::Injector& injector_;
|
|
friend void CollectPerfettoTraceBufferImmediately(
|
RxProducerFactory& producer_factory,
|
const std::string& arg_output_proto);
|
};
|
|
// An rx Coordination, which will cause a new thread to spawn for each new Worker.
|
//
|
// Idle-class priority is set for the CPU and IO priorities on the new thread.
|
//
|
// TODO: move to separate file
|
rxcpp::observe_on_one_worker ObserveOnNewIoThread();
|
|
} // namespace iorap::perfetto
|
#endif // IORAP_SRC_PERFETTO_RX_PRODUCER_H_
|