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