blob: 4e93f0d7f621875e92474acf832d961ab4000f6d [file] [log] [blame]
Igor Murashkinb250cd82019-01-09 11:33:52 -08001// 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
28namespace iorap::perfetto {
29
30struct 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
Igor Murashkin4caf56e2019-04-04 17:11:26 -070054namespace detail {
55 template <typename T>
56 struct concept_message_lite_base {
57 static_assert(std::is_base_of_v<::google::protobuf::MessageLite, T>,
58 "T must inherit from MessageLite");
59 using type = T;
60 };
61
62 template <typename T>
63 using concept_message_lite_base_t = typename concept_message_lite_base<T>::type;
64} // namespace detail
65
66/*
67 * In Android's version of libprotobuf, move-constructors are not generated.
68 * This results in a legitimate (~10sec per TracePacket being compiled) slowdown,
69 * so we need to avoid it everywhere.
70 *
71 * 1) Don't copy the protos, move them instead.
72 * 2) Use 'shared_ptr' because rxcpp won't compile with unique_ptr.
73 */
74template <typename T>
75using ProtobufPtr = std::shared_ptr<detail::concept_message_lite_base_t<const T>>;
76
77template <typename T>
78using ProtobufMutablePtr = std::shared_ptr<detail::concept_message_lite_base_t<T>>;
79
Igor Murashkinb250cd82019-01-09 11:33:52 -080080// This acts as a lightweight type marker so that we know what data has actually
81// encoded under the hood.
82template <typename T>
83struct BinaryWireProtobuf {
Igor Murashkina5dfa9c2019-03-28 16:12:09 -070084 static_assert(std::is_base_of_v<::google::protobuf::MessageLite, T>,
85 "T should be a base class of MessageLite");
86
Igor Murashkinb250cd82019-01-09 11:33:52 -080087 std::vector<std::byte>& data() {
88 return data_;
89 }
90
91 const std::vector<std::byte>& data() const {
92 return data_;
93 }
94
95 size_t size() const {
96 return data_.size();
97 }
98
99 explicit BinaryWireProtobuf(char* data, size_t size)
100 : BinaryWireProtobuf(reinterpret_cast<std::byte*>(data), size) {
101 }
102
103 explicit BinaryWireProtobuf(std::byte* data, size_t size) {
104 data_.resize(size);
105 std::copy(data,
106 data + size,
107 data_.data());
108 }
109
Igor Murashkinc86d0d02019-02-22 14:20:42 -0800110 explicit BinaryWireProtobuf(std::vector<std::byte> data) : data_{std::move(data)} {
111 }
112
Igor Murashkin4caf56e2019-04-04 17:11:26 -0700113 // You wouldn't want to accidentally copy a giant multi-megabyte chunk would you?
114 // BinaryWireProtobuf(const BinaryWireProtobuf& other) = delete; // FIXME: rx likes to copy.
115 BinaryWireProtobuf(const BinaryWireProtobuf& other) = default;
116 BinaryWireProtobuf(BinaryWireProtobuf&& other) = default;
117
Igor Murashkinb250cd82019-01-09 11:33:52 -0800118 // Important: Deserialization could fail, for example data is truncated or
119 // some minor disc corruption occurred.
120 template <typename U>
Igor Murashkin4caf56e2019-04-04 17:11:26 -0700121 std::optional<ProtobufPtr<U>> MaybeUnserialize() {
122 ProtobufMutablePtr<U> unencoded{new U{}};
Igor Murashkinb250cd82019-01-09 11:33:52 -0800123
Igor Murashkin4caf56e2019-04-04 17:11:26 -0700124 if (!unencoded->ParseFromArray(data_.data(), data_.size())) {
Igor Murashkinb250cd82019-01-09 11:33:52 -0800125 return std::nullopt;
126 }
127
128 return {std::move(unencoded)};
129 }
130
131 bool WriteFullyToFile(const std::string& path,
132 bool follow_symlinks = false) const;
133
Igor Murashkinc86d0d02019-02-22 14:20:42 -0800134 static std::optional<BinaryWireProtobuf<T>> ReadFullyFromFile(const std::string& path,
135 bool follow_symlinks = false);
136
Igor Murashkin5e216982019-10-17 14:11:44 -0700137 bool operator==(const BinaryWireProtobuf<T>& other) const;
138 bool operator!=(const BinaryWireProtobuf<T>& other) const {
139 return !(*this == other);
140 }
141
Igor Murashkinb250cd82019-01-09 11:33:52 -0800142 private:
143 static bool CleanUpAfterFailedWrite(const std::string& path);
144 bool WriteStringToFd(int fd) const;
145
Igor Murashkinc86d0d02019-02-22 14:20:42 -0800146 static bool ReadFdToString(int fd, /*out*/std::vector<std::byte>* data);
147
Igor Murashkinb250cd82019-01-09 11:33:52 -0800148 std::vector<std::byte> data_;
149};
150
151//using PerfettoTraceProto = BinaryWireProtobuf<::perfetto::protos::Trace>;
152using PerfettoTraceProto = BinaryWireProtobuf<::google::protobuf::MessageLite>;
153
154enum class PerfettoStreamCommand {
155 kStartTracing, // -> () | on_error
156 kStopTracing, // -> on_next(PerfettoTraceProto) | on_error
157 kShutdown, // -> on_completed | on_error
158 // XX: should shutdown be converted to use the rx suscriber#unsubscribe instead?
159};
160
161std::ostream& operator<<(std::ostream& os, PerfettoStreamCommand c);
162
163struct RxProducerFactory {
164 // Passing anything by value leads to a lot of pain and headache.
165 // Pass in the injector by reference because nothing else seems to work.
166 explicit RxProducerFactory(PerfettoDependencies::Injector& injector);
167
168 // Create a one-shot perfetto observable that will begin
169 // asynchronously producing a PerfettoTraceProto after the 'kStartTracing'
170 // command is observed.
171 //
172 // libperfetto is immediately primed (i.e. connected in a deferred state)
173 // upon calling this function, to reduce the latency of 'kStartTracing'.
174 //
175 // To finish the trace, push 'kStopTracing'. To cancel or tear down at any
176 // time, push 'kShutdown'.
177 //
178 // The TraceProto may come out at any time after 'kStartTracing',
179 // this is controlled by duration_ms in the TraceConfig.
180 //
181 // TODO: libperfetto should actually stop tracing when we ask it to,
182 // instead of using a hardcoded time.
183 //
184 // The observable may go into #on_error at any time, if the underlying
185 // libperfetto states transition to a failing state.
186 // This usually means the OS is not configured correctly.
187 rxcpp::observable<PerfettoTraceProto> CreateTraceStream(
188 rxcpp::observable<PerfettoStreamCommand> commands);
189
190 // TODO: is this refactor-able into a subscriber factory that takes
191 // the commands-observable as a parameter?
192
193 // TODO: infinite perfetto stream.
194
195 private:
196 // XX: why doesn't this just let me pass in a regular Component?
197 PerfettoDependencies::Injector& injector_;
198
199 friend void CollectPerfettoTraceBufferImmediately(
200 RxProducerFactory& producer_factory,
201 const std::string& arg_output_proto);
202};
203
204// An rx Coordination, which will cause a new thread to spawn for each new Worker.
205//
206// Idle-class priority is set for the CPU and IO priorities on the new thread.
207//
208// TODO: move to separate file
209rxcpp::observe_on_one_worker ObserveOnNewIoThread();
210
211} // namespace iorap::perfetto
212#endif // IORAP_SRC_PERFETTO_RX_PRODUCER_H_