1 // Copyright (C) 2019 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef IORAP_SRC_PERFETTO_RX_PRODUCER_H_ 16 #define IORAP_SRC_PERFETTO_RX_PRODUCER_H_ 17 18 #include "perfetto/perfetto_consumer.h" // libiorap 19 20 #include <perfetto/config/trace_config.pb.h> // libperfetto 21 #include <rxcpp/rx.hpp> 22 23 #include <iosfwd> 24 #include <functional> 25 #include <optional> 26 #include <vector> 27 28 namespace iorap::perfetto { 29 30 struct PerfettoDependencies { 31 using Component = 32 fruit::Component<PerfettoConsumer, ::perfetto::protos::TraceConfig>; 33 using Injector = 34 fruit::Injector<PerfettoConsumer, ::perfetto::protos::TraceConfig>; 35 using NormalizedComponent = 36 fruit::NormalizedComponent<PerfettoConsumer, ::perfetto::protos::TraceConfig>; 37 38 // Create a 'live' component that will talk to perfetto via traced. 39 static Component CreateComponent(/*TODO: config params*/); 40 41 // Create perfetto.protos.TraceConfig , serialized as a (machine-readable) string. 42 // 43 // The following ftrace events are enabled: 44 // * mm_filemap_add_to_page_cache 45 // * mm_filemap_delete_from_page_cache 46 // 47 // If deferred starting is also enabled, no tracing will begin until 48 // ::perfetto::consumer::StartTracing is invoked. 49 static ::perfetto::protos::TraceConfig CreateConfig(uint32_t duration_ms, 50 bool deferred_start = true, 51 uint32_t buffer_size = 4096); 52 }; 53 54 // This acts as a lightweight type marker so that we know what data has actually 55 // encoded under the hood. 56 template <typename T> 57 struct BinaryWireProtobuf { dataBinaryWireProtobuf58 std::vector<std::byte>& data() { 59 return data_; 60 } 61 dataBinaryWireProtobuf62 const std::vector<std::byte>& data() const { 63 return data_; 64 } 65 sizeBinaryWireProtobuf66 size_t size() const { 67 return data_.size(); 68 } 69 BinaryWireProtobufBinaryWireProtobuf70 explicit BinaryWireProtobuf(char* data, size_t size) 71 : BinaryWireProtobuf(reinterpret_cast<std::byte*>(data), size) { 72 } 73 BinaryWireProtobufBinaryWireProtobuf74 explicit BinaryWireProtobuf(std::byte* data, size_t size) { 75 data_.resize(size); 76 std::copy(data, 77 data + size, 78 data_.data()); 79 } 80 81 // Important: Deserialization could fail, for example data is truncated or 82 // some minor disc corruption occurred. 83 template <typename U> MaybeUnserializeBinaryWireProtobuf84 std::optional<U> MaybeUnserialize() { 85 U unencoded; 86 87 if (!unencoded.ParseFromArray(data_.data(), data_.size())) { 88 return std::nullopt; 89 } 90 91 return {std::move(unencoded)}; 92 } 93 94 bool WriteFullyToFile(const std::string& path, 95 bool follow_symlinks = false) const; 96 97 private: 98 static bool CleanUpAfterFailedWrite(const std::string& path); 99 bool WriteStringToFd(int fd) const; 100 101 std::vector<std::byte> data_; 102 }; 103 104 //using PerfettoTraceProto = BinaryWireProtobuf<::perfetto::protos::Trace>; 105 using PerfettoTraceProto = BinaryWireProtobuf<::google::protobuf::MessageLite>; 106 107 enum class PerfettoStreamCommand { 108 kStartTracing, // -> () | on_error 109 kStopTracing, // -> on_next(PerfettoTraceProto) | on_error 110 kShutdown, // -> on_completed | on_error 111 // XX: should shutdown be converted to use the rx suscriber#unsubscribe instead? 112 }; 113 114 std::ostream& operator<<(std::ostream& os, PerfettoStreamCommand c); 115 116 struct RxProducerFactory { 117 // Passing anything by value leads to a lot of pain and headache. 118 // Pass in the injector by reference because nothing else seems to work. 119 explicit RxProducerFactory(PerfettoDependencies::Injector& injector); 120 121 // Create a one-shot perfetto observable that will begin 122 // asynchronously producing a PerfettoTraceProto after the 'kStartTracing' 123 // command is observed. 124 // 125 // libperfetto is immediately primed (i.e. connected in a deferred state) 126 // upon calling this function, to reduce the latency of 'kStartTracing'. 127 // 128 // To finish the trace, push 'kStopTracing'. To cancel or tear down at any 129 // time, push 'kShutdown'. 130 // 131 // The TraceProto may come out at any time after 'kStartTracing', 132 // this is controlled by duration_ms in the TraceConfig. 133 // 134 // TODO: libperfetto should actually stop tracing when we ask it to, 135 // instead of using a hardcoded time. 136 // 137 // The observable may go into #on_error at any time, if the underlying 138 // libperfetto states transition to a failing state. 139 // This usually means the OS is not configured correctly. 140 rxcpp::observable<PerfettoTraceProto> CreateTraceStream( 141 rxcpp::observable<PerfettoStreamCommand> commands); 142 143 // TODO: is this refactor-able into a subscriber factory that takes 144 // the commands-observable as a parameter? 145 146 // TODO: infinite perfetto stream. 147 148 private: 149 // XX: why doesn't this just let me pass in a regular Component? 150 PerfettoDependencies::Injector& injector_; 151 152 friend void CollectPerfettoTraceBufferImmediately( 153 RxProducerFactory& producer_factory, 154 const std::string& arg_output_proto); 155 }; 156 157 // An rx Coordination, which will cause a new thread to spawn for each new Worker. 158 // 159 // Idle-class priority is set for the CPU and IO priorities on the new thread. 160 // 161 // TODO: move to separate file 162 rxcpp::observe_on_one_worker ObserveOnNewIoThread(); 163 164 } // namespace iorap::perfetto 165 #endif // IORAP_SRC_PERFETTO_RX_PRODUCER_H_ 166