xref: /aosp_15_r20/external/perfetto/src/ipc/buffered_frame_deserializer.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker /*
2*6dbdd20aSAndroid Build Coastguard Worker  * Copyright (C) 2017 The Android Open Source Project
3*6dbdd20aSAndroid Build Coastguard Worker  *
4*6dbdd20aSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*6dbdd20aSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*6dbdd20aSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*6dbdd20aSAndroid Build Coastguard Worker  *
8*6dbdd20aSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*6dbdd20aSAndroid Build Coastguard Worker  *
10*6dbdd20aSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*6dbdd20aSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*6dbdd20aSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6dbdd20aSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*6dbdd20aSAndroid Build Coastguard Worker  * limitations under the License.
15*6dbdd20aSAndroid Build Coastguard Worker  */
16*6dbdd20aSAndroid Build Coastguard Worker 
17*6dbdd20aSAndroid Build Coastguard Worker #include "src/ipc/buffered_frame_deserializer.h"
18*6dbdd20aSAndroid Build Coastguard Worker 
19*6dbdd20aSAndroid Build Coastguard Worker #include <algorithm>
20*6dbdd20aSAndroid Build Coastguard Worker #include <cinttypes>
21*6dbdd20aSAndroid Build Coastguard Worker #include <type_traits>
22*6dbdd20aSAndroid Build Coastguard Worker #include <utility>
23*6dbdd20aSAndroid Build Coastguard Worker 
24*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/base/logging.h"
25*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/utils.h"
26*6dbdd20aSAndroid Build Coastguard Worker 
27*6dbdd20aSAndroid Build Coastguard Worker #include "protos/perfetto/ipc/wire_protocol.gen.h"
28*6dbdd20aSAndroid Build Coastguard Worker 
29*6dbdd20aSAndroid Build Coastguard Worker namespace perfetto {
30*6dbdd20aSAndroid Build Coastguard Worker namespace ipc {
31*6dbdd20aSAndroid Build Coastguard Worker 
32*6dbdd20aSAndroid Build Coastguard Worker namespace {
33*6dbdd20aSAndroid Build Coastguard Worker 
34*6dbdd20aSAndroid Build Coastguard Worker // The header is just the number of bytes of the Frame protobuf message.
35*6dbdd20aSAndroid Build Coastguard Worker constexpr size_t kHeaderSize = sizeof(uint32_t);
36*6dbdd20aSAndroid Build Coastguard Worker }  // namespace
37*6dbdd20aSAndroid Build Coastguard Worker 
BufferedFrameDeserializer(size_t max_capacity)38*6dbdd20aSAndroid Build Coastguard Worker BufferedFrameDeserializer::BufferedFrameDeserializer(size_t max_capacity)
39*6dbdd20aSAndroid Build Coastguard Worker     : capacity_(max_capacity) {
40*6dbdd20aSAndroid Build Coastguard Worker   PERFETTO_CHECK(max_capacity % base::GetSysPageSize() == 0);
41*6dbdd20aSAndroid Build Coastguard Worker   PERFETTO_CHECK(max_capacity >= base::GetSysPageSize());
42*6dbdd20aSAndroid Build Coastguard Worker }
43*6dbdd20aSAndroid Build Coastguard Worker 
44*6dbdd20aSAndroid Build Coastguard Worker BufferedFrameDeserializer::~BufferedFrameDeserializer() = default;
45*6dbdd20aSAndroid Build Coastguard Worker 
46*6dbdd20aSAndroid Build Coastguard Worker BufferedFrameDeserializer::ReceiveBuffer
BeginReceive()47*6dbdd20aSAndroid Build Coastguard Worker BufferedFrameDeserializer::BeginReceive() {
48*6dbdd20aSAndroid Build Coastguard Worker   // Upon the first recv initialize the buffer to the max message size but
49*6dbdd20aSAndroid Build Coastguard Worker   // release the physical memory for all but the first page. The kernel will
50*6dbdd20aSAndroid Build Coastguard Worker   // automatically give us physical pages back as soon as we page-fault on them.
51*6dbdd20aSAndroid Build Coastguard Worker   if (!buf_.IsValid()) {
52*6dbdd20aSAndroid Build Coastguard Worker     PERFETTO_DCHECK(size_ == 0);
53*6dbdd20aSAndroid Build Coastguard Worker     // TODO(eseckler): Don't commit all of the buffer at once on Windows.
54*6dbdd20aSAndroid Build Coastguard Worker     buf_ = base::PagedMemory::Allocate(capacity_);
55*6dbdd20aSAndroid Build Coastguard Worker 
56*6dbdd20aSAndroid Build Coastguard Worker     // Surely we are going to use at least the first page, but we may not need
57*6dbdd20aSAndroid Build Coastguard Worker     // the rest for a bit.
58*6dbdd20aSAndroid Build Coastguard Worker     const auto page_size = base::GetSysPageSize();
59*6dbdd20aSAndroid Build Coastguard Worker     buf_.AdviseDontNeed(buf() + page_size, capacity_ - page_size);
60*6dbdd20aSAndroid Build Coastguard Worker   }
61*6dbdd20aSAndroid Build Coastguard Worker 
62*6dbdd20aSAndroid Build Coastguard Worker   PERFETTO_CHECK(capacity_ > size_);
63*6dbdd20aSAndroid Build Coastguard Worker   return ReceiveBuffer{buf() + size_, capacity_ - size_};
64*6dbdd20aSAndroid Build Coastguard Worker }
65*6dbdd20aSAndroid Build Coastguard Worker 
EndReceive(size_t recv_size)66*6dbdd20aSAndroid Build Coastguard Worker bool BufferedFrameDeserializer::EndReceive(size_t recv_size) {
67*6dbdd20aSAndroid Build Coastguard Worker   const auto page_size = base::GetSysPageSize();
68*6dbdd20aSAndroid Build Coastguard Worker   PERFETTO_CHECK(recv_size + size_ <= capacity_);
69*6dbdd20aSAndroid Build Coastguard Worker   size_ += recv_size;
70*6dbdd20aSAndroid Build Coastguard Worker 
71*6dbdd20aSAndroid Build Coastguard Worker   // At this point the contents buf_ can contain:
72*6dbdd20aSAndroid Build Coastguard Worker   // A) Only a fragment of the header (the size of the frame). E.g.,
73*6dbdd20aSAndroid Build Coastguard Worker   //    03 00 00 (the header is 4 bytes, one is missing).
74*6dbdd20aSAndroid Build Coastguard Worker   //
75*6dbdd20aSAndroid Build Coastguard Worker   // B) A header and a part of the frame. E.g.,
76*6dbdd20aSAndroid Build Coastguard Worker   //     05 00 00 00         11 22 33
77*6dbdd20aSAndroid Build Coastguard Worker   //    [ header, size=5 ]  [ Partial frame ]
78*6dbdd20aSAndroid Build Coastguard Worker   //
79*6dbdd20aSAndroid Build Coastguard Worker   // C) One or more complete header+frame. E.g.,
80*6dbdd20aSAndroid Build Coastguard Worker   //     05 00 00 00         11 22 33 44 55   03 00 00 00        AA BB CC
81*6dbdd20aSAndroid Build Coastguard Worker   //    [ header, size=5 ]  [ Whole frame ]  [ header, size=3 ] [ Whole frame ]
82*6dbdd20aSAndroid Build Coastguard Worker   //
83*6dbdd20aSAndroid Build Coastguard Worker   // D) Some complete header+frame(s) and a partial header or frame (C + A/B).
84*6dbdd20aSAndroid Build Coastguard Worker   //
85*6dbdd20aSAndroid Build Coastguard Worker   // C Is the more likely case and the one we are optimizing for. A, B, D can
86*6dbdd20aSAndroid Build Coastguard Worker   // happen because of the streaming nature of the socket.
87*6dbdd20aSAndroid Build Coastguard Worker   // The invariant of this function is that, when it returns, buf_ is either
88*6dbdd20aSAndroid Build Coastguard Worker   // empty (we drained all the complete frames) or starts with the header of the
89*6dbdd20aSAndroid Build Coastguard Worker   // next, still incomplete, frame.
90*6dbdd20aSAndroid Build Coastguard Worker 
91*6dbdd20aSAndroid Build Coastguard Worker   size_t consumed_size = 0;
92*6dbdd20aSAndroid Build Coastguard Worker   for (;;) {
93*6dbdd20aSAndroid Build Coastguard Worker     if (size_ < consumed_size + kHeaderSize)
94*6dbdd20aSAndroid Build Coastguard Worker       break;  // Case A, not enough data to read even the header.
95*6dbdd20aSAndroid Build Coastguard Worker 
96*6dbdd20aSAndroid Build Coastguard Worker     // Read the header into |payload_size|.
97*6dbdd20aSAndroid Build Coastguard Worker     uint32_t payload_size = 0;
98*6dbdd20aSAndroid Build Coastguard Worker     const char* rd_ptr = buf() + consumed_size;
99*6dbdd20aSAndroid Build Coastguard Worker     memcpy(base::AssumeLittleEndian(&payload_size), rd_ptr, kHeaderSize);
100*6dbdd20aSAndroid Build Coastguard Worker 
101*6dbdd20aSAndroid Build Coastguard Worker     // Saturate the |payload_size| to prevent overflows. The > capacity_ check
102*6dbdd20aSAndroid Build Coastguard Worker     // below will abort the parsing.
103*6dbdd20aSAndroid Build Coastguard Worker     size_t next_frame_size =
104*6dbdd20aSAndroid Build Coastguard Worker         std::min(static_cast<size_t>(payload_size), capacity_);
105*6dbdd20aSAndroid Build Coastguard Worker     next_frame_size += kHeaderSize;
106*6dbdd20aSAndroid Build Coastguard Worker     rd_ptr += kHeaderSize;
107*6dbdd20aSAndroid Build Coastguard Worker 
108*6dbdd20aSAndroid Build Coastguard Worker     if (size_ < consumed_size + next_frame_size) {
109*6dbdd20aSAndroid Build Coastguard Worker       // Case B. We got the header but not the whole frame.
110*6dbdd20aSAndroid Build Coastguard Worker       if (next_frame_size > capacity_) {
111*6dbdd20aSAndroid Build Coastguard Worker         // The caller is expected to shut down the socket and give up at this
112*6dbdd20aSAndroid Build Coastguard Worker         // point. If it doesn't do that and insists going on at some point it
113*6dbdd20aSAndroid Build Coastguard Worker         // will hit the capacity check in BeginReceive().
114*6dbdd20aSAndroid Build Coastguard Worker         PERFETTO_LOG("IPC Frame too large (size %zu)", next_frame_size);
115*6dbdd20aSAndroid Build Coastguard Worker         return false;
116*6dbdd20aSAndroid Build Coastguard Worker       }
117*6dbdd20aSAndroid Build Coastguard Worker       break;
118*6dbdd20aSAndroid Build Coastguard Worker     }
119*6dbdd20aSAndroid Build Coastguard Worker 
120*6dbdd20aSAndroid Build Coastguard Worker     // Case C. We got at least one header and whole frame.
121*6dbdd20aSAndroid Build Coastguard Worker     DecodeFrame(rd_ptr, payload_size);
122*6dbdd20aSAndroid Build Coastguard Worker     consumed_size += next_frame_size;
123*6dbdd20aSAndroid Build Coastguard Worker   }
124*6dbdd20aSAndroid Build Coastguard Worker 
125*6dbdd20aSAndroid Build Coastguard Worker   PERFETTO_DCHECK(consumed_size <= size_);
126*6dbdd20aSAndroid Build Coastguard Worker   if (consumed_size > 0) {
127*6dbdd20aSAndroid Build Coastguard Worker     // Shift out the consumed data from the buffer. In the typical case (C)
128*6dbdd20aSAndroid Build Coastguard Worker     // there is nothing to shift really, just setting size_ = 0 is enough.
129*6dbdd20aSAndroid Build Coastguard Worker     // Shifting is only for the (unlikely) case D.
130*6dbdd20aSAndroid Build Coastguard Worker     size_ -= consumed_size;
131*6dbdd20aSAndroid Build Coastguard Worker     if (size_ > 0) {
132*6dbdd20aSAndroid Build Coastguard Worker       // Case D. We consumed some frames but there is a leftover at the end of
133*6dbdd20aSAndroid Build Coastguard Worker       // the buffer. Shift out the consumed bytes, so that on the next round
134*6dbdd20aSAndroid Build Coastguard Worker       // |buf_| starts with the header of the next unconsumed frame.
135*6dbdd20aSAndroid Build Coastguard Worker       const char* move_begin = buf() + consumed_size;
136*6dbdd20aSAndroid Build Coastguard Worker       PERFETTO_CHECK(move_begin > buf());
137*6dbdd20aSAndroid Build Coastguard Worker       PERFETTO_CHECK(move_begin + size_ <= buf() + capacity_);
138*6dbdd20aSAndroid Build Coastguard Worker       memmove(buf(), move_begin, size_);
139*6dbdd20aSAndroid Build Coastguard Worker     }
140*6dbdd20aSAndroid Build Coastguard Worker     // If we just finished decoding a large frame that used more than one page,
141*6dbdd20aSAndroid Build Coastguard Worker     // release the extra memory in the buffer. Large frames should be quite
142*6dbdd20aSAndroid Build Coastguard Worker     // rare.
143*6dbdd20aSAndroid Build Coastguard Worker     if (consumed_size > page_size) {
144*6dbdd20aSAndroid Build Coastguard Worker       size_t size_rounded_up = (size_ / page_size + 1) * page_size;
145*6dbdd20aSAndroid Build Coastguard Worker       if (size_rounded_up < capacity_) {
146*6dbdd20aSAndroid Build Coastguard Worker         char* madvise_begin = buf() + size_rounded_up;
147*6dbdd20aSAndroid Build Coastguard Worker         const size_t madvise_size = capacity_ - size_rounded_up;
148*6dbdd20aSAndroid Build Coastguard Worker         PERFETTO_CHECK(madvise_begin > buf() + size_);
149*6dbdd20aSAndroid Build Coastguard Worker         PERFETTO_CHECK(madvise_begin + madvise_size <= buf() + capacity_);
150*6dbdd20aSAndroid Build Coastguard Worker         buf_.AdviseDontNeed(madvise_begin, madvise_size);
151*6dbdd20aSAndroid Build Coastguard Worker       }
152*6dbdd20aSAndroid Build Coastguard Worker     }
153*6dbdd20aSAndroid Build Coastguard Worker   }
154*6dbdd20aSAndroid Build Coastguard Worker   // At this point |size_| == 0 for case C, > 0 for cases A, B, D.
155*6dbdd20aSAndroid Build Coastguard Worker   return true;
156*6dbdd20aSAndroid Build Coastguard Worker }
157*6dbdd20aSAndroid Build Coastguard Worker 
PopNextFrame()158*6dbdd20aSAndroid Build Coastguard Worker std::unique_ptr<Frame> BufferedFrameDeserializer::PopNextFrame() {
159*6dbdd20aSAndroid Build Coastguard Worker   if (decoded_frames_.empty())
160*6dbdd20aSAndroid Build Coastguard Worker     return nullptr;
161*6dbdd20aSAndroid Build Coastguard Worker   std::unique_ptr<Frame> frame = std::move(decoded_frames_.front());
162*6dbdd20aSAndroid Build Coastguard Worker   decoded_frames_.pop_front();
163*6dbdd20aSAndroid Build Coastguard Worker   return frame;
164*6dbdd20aSAndroid Build Coastguard Worker }
165*6dbdd20aSAndroid Build Coastguard Worker 
DecodeFrame(const char * data,size_t size)166*6dbdd20aSAndroid Build Coastguard Worker void BufferedFrameDeserializer::DecodeFrame(const char* data, size_t size) {
167*6dbdd20aSAndroid Build Coastguard Worker   if (size == 0)
168*6dbdd20aSAndroid Build Coastguard Worker     return;
169*6dbdd20aSAndroid Build Coastguard Worker   std::unique_ptr<Frame> frame(new Frame);
170*6dbdd20aSAndroid Build Coastguard Worker   if (frame->ParseFromArray(data, size))
171*6dbdd20aSAndroid Build Coastguard Worker     decoded_frames_.push_back(std::move(frame));
172*6dbdd20aSAndroid Build Coastguard Worker }
173*6dbdd20aSAndroid Build Coastguard Worker 
174*6dbdd20aSAndroid Build Coastguard Worker // static
Serialize(const Frame & frame)175*6dbdd20aSAndroid Build Coastguard Worker std::string BufferedFrameDeserializer::Serialize(const Frame& frame) {
176*6dbdd20aSAndroid Build Coastguard Worker   std::vector<uint8_t> payload = frame.SerializeAsArray();
177*6dbdd20aSAndroid Build Coastguard Worker   const uint32_t payload_size = static_cast<uint32_t>(payload.size());
178*6dbdd20aSAndroid Build Coastguard Worker   std::string buf;
179*6dbdd20aSAndroid Build Coastguard Worker   buf.resize(kHeaderSize + payload_size);
180*6dbdd20aSAndroid Build Coastguard Worker   memcpy(&buf[0], base::AssumeLittleEndian(&payload_size), kHeaderSize);
181*6dbdd20aSAndroid Build Coastguard Worker   memcpy(&buf[kHeaderSize], payload.data(), payload.size());
182*6dbdd20aSAndroid Build Coastguard Worker   return buf;
183*6dbdd20aSAndroid Build Coastguard Worker }
184*6dbdd20aSAndroid Build Coastguard Worker 
185*6dbdd20aSAndroid Build Coastguard Worker }  // namespace ipc
186*6dbdd20aSAndroid Build Coastguard Worker }  // namespace perfetto
187