Primiano Tucci | 9ebb882 | 2017-11-09 18:36:25 +0000 | [diff] [blame] | 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 "ipc/src/buffered_frame_deserializer.h" |
| 18 | |
| 19 | #include <algorithm> |
| 20 | #include <string> |
| 21 | |
| 22 | #include "base/logging.h" |
| 23 | #include "base/utils.h" |
| 24 | #include "gtest/gtest.h" |
| 25 | |
Primiano Tucci | 2ee254a | 2017-11-15 00:38:48 +0000 | [diff] [blame] | 26 | #include "ipc/src/wire_protocol.pb.h" |
Primiano Tucci | 9ebb882 | 2017-11-09 18:36:25 +0000 | [diff] [blame] | 27 | |
| 28 | namespace perfetto { |
| 29 | namespace ipc { |
| 30 | namespace { |
| 31 | |
| 32 | constexpr size_t kHeaderSize = sizeof(uint32_t); |
| 33 | |
| 34 | // Generates a parsable Frame of exactly |size| bytes (including header). |
| 35 | std::vector<char> GetSimpleFrame(size_t size) { |
| 36 | // A bit of reverse math of the proto encoding: a Frame which has only the |
| 37 | // |data_for_testing| fields, will require for each data_for_testing that is |
| 38 | // up to 127 bytes: |
| 39 | // - 1 byte to write the field preamble (field type and id). |
| 40 | // - 1 byte to write the field size, if 0 < size <= 127. |
| 41 | // - N bytes for the actual content (|padding| below). |
| 42 | // So below we split the payload into chunks of <= 127 bytes, keeping into |
| 43 | // account the extra 2 bytes for each chunk. |
| 44 | Frame frame; |
| 45 | std::vector<char> padding; |
| 46 | char padding_char = '0'; |
| 47 | const size_t payload_size = size - kHeaderSize; |
| 48 | for (size_t size_left = payload_size; size_left > 0;) { |
| 49 | PERFETTO_CHECK(size_left >= 2); // We cannot produce frames < 2 bytes. |
| 50 | size_t padding_size; |
| 51 | if (size_left <= 127) { |
| 52 | padding_size = size_left - 2; |
| 53 | size_left = 0; |
| 54 | } else { |
| 55 | padding_size = 124; |
| 56 | size_left -= padding_size + 2; |
| 57 | } |
| 58 | padding.resize(padding_size); |
| 59 | for (size_t i = 0; i < padding_size; i++) { |
| 60 | padding_char = padding_char == 'z' ? '0' : padding_char + 1; |
| 61 | padding[i] = padding_char; |
| 62 | } |
| 63 | frame.add_data_for_testing(padding.data(), padding_size); |
| 64 | } |
| 65 | PERFETTO_CHECK(frame.ByteSize() == payload_size); |
| 66 | std::vector<char> encoded_frame; |
| 67 | encoded_frame.resize(size); |
| 68 | char* enc_buf = encoded_frame.data(); |
| 69 | PERFETTO_CHECK(frame.SerializeToArray(enc_buf + kHeaderSize, payload_size)); |
| 70 | memcpy(enc_buf, base::AssumeLittleEndian(&payload_size), kHeaderSize); |
| 71 | PERFETTO_CHECK(encoded_frame.size() == size); |
| 72 | return encoded_frame; |
| 73 | } |
| 74 | |
| 75 | void CheckedMemcpy(BufferedFrameDeserializer::ReceiveBuffer rbuf, |
| 76 | const std::vector<char>& encoded_frame, |
| 77 | size_t offset = 0) { |
| 78 | ASSERT_GE(rbuf.size, encoded_frame.size() + offset); |
| 79 | memcpy(rbuf.data + offset, encoded_frame.data(), encoded_frame.size()); |
| 80 | } |
| 81 | |
| 82 | bool FrameEq(std::vector<char> expected_frame_with_header, const Frame& frame) { |
| 83 | std::string reserialized_frame = frame.SerializeAsString(); |
| 84 | |
| 85 | size_t expected_size = expected_frame_with_header.size() - kHeaderSize; |
| 86 | EXPECT_EQ(expected_size, reserialized_frame.size()); |
| 87 | if (expected_size != reserialized_frame.size()) |
| 88 | return false; |
| 89 | |
| 90 | return memcmp(reserialized_frame.data(), |
| 91 | expected_frame_with_header.data() + kHeaderSize, |
| 92 | reserialized_frame.size()) == 0; |
| 93 | } |
| 94 | |
| 95 | // Tests the simple case where each recv() just returns one whole header+frame. |
| 96 | TEST(BufferedFrameDeserializerTest, WholeMessages) { |
| 97 | BufferedFrameDeserializer bfd; |
| 98 | for (int i = 1; i <= 50; i++) { |
| 99 | const size_t size = i * 10; |
| 100 | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); |
| 101 | |
| 102 | ASSERT_NE(nullptr, rbuf.data); |
| 103 | std::vector<char> frame = GetSimpleFrame(size); |
| 104 | CheckedMemcpy(rbuf, frame); |
| 105 | ASSERT_TRUE(bfd.EndReceive(frame.size())); |
| 106 | |
| 107 | // Excactly one frame should be decoded, with no leftover buffer. |
| 108 | auto decoded_frame = bfd.PopNextFrame(); |
| 109 | ASSERT_TRUE(decoded_frame); |
| 110 | ASSERT_EQ(size - kHeaderSize, decoded_frame->ByteSize()); |
| 111 | ASSERT_FALSE(bfd.PopNextFrame()); |
| 112 | ASSERT_EQ(0u, bfd.size()); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | // Sends first a simple test frame. Then creates a realistic Frame fragmenting |
| 117 | // it in three chunks and tests that the decoded Frame matches the original one. |
| 118 | // The recv() sequence is as follows: |
| 119 | // 1. [ simple_frame ] [ frame_chunk1 ... ] |
| 120 | // 2. [ ... frame_chunk2 ... ] |
| 121 | // 3. [ ... frame_chunk3 ] |
| 122 | TEST(BufferedFrameDeserializerTest, FragmentedFrameIsCorrectlyDeserialized) { |
| 123 | BufferedFrameDeserializer bfd; |
| 124 | Frame frame; |
| 125 | frame.set_request_id(42); |
| 126 | auto* bind_reply = frame.mutable_msg_bind_service_reply(); |
| 127 | bind_reply->set_success(true); |
| 128 | bind_reply->set_service_id(0x4242); |
| 129 | auto* method = bind_reply->add_methods(); |
| 130 | method->set_id(0x424242); |
| 131 | method->set_name("foo"); |
| 132 | std::vector<char> serialized_frame; |
| 133 | uint32_t payload_size = frame.ByteSize(); |
| 134 | |
| 135 | serialized_frame.resize(kHeaderSize + payload_size); |
| 136 | ASSERT_TRUE(frame.SerializeToArray(serialized_frame.data() + kHeaderSize, |
| 137 | payload_size)); |
| 138 | memcpy(serialized_frame.data(), base::AssumeLittleEndian(&payload_size), |
| 139 | kHeaderSize); |
| 140 | |
| 141 | std::vector<char> simple_frame = GetSimpleFrame(32); |
| 142 | std::vector<char> frame_chunk1(serialized_frame.begin(), |
| 143 | serialized_frame.begin() + 5); |
| 144 | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); |
| 145 | CheckedMemcpy(rbuf, simple_frame); |
| 146 | CheckedMemcpy(rbuf, frame_chunk1, simple_frame.size()); |
| 147 | ASSERT_TRUE(bfd.EndReceive(simple_frame.size() + frame_chunk1.size())); |
| 148 | |
| 149 | std::vector<char> frame_chunk2(serialized_frame.begin() + 5, |
| 150 | serialized_frame.begin() + 10); |
| 151 | rbuf = bfd.BeginReceive(); |
| 152 | CheckedMemcpy(rbuf, frame_chunk2); |
| 153 | ASSERT_TRUE(bfd.EndReceive(frame_chunk2.size())); |
| 154 | |
| 155 | std::vector<char> frame_chunk3(serialized_frame.begin() + 10, |
| 156 | serialized_frame.end()); |
| 157 | rbuf = bfd.BeginReceive(); |
| 158 | CheckedMemcpy(rbuf, frame_chunk3); |
| 159 | ASSERT_TRUE(bfd.EndReceive(frame_chunk3.size())); |
| 160 | |
| 161 | // Validate the received frame2. |
| 162 | std::unique_ptr<Frame> decoded_simple_frame = bfd.PopNextFrame(); |
| 163 | ASSERT_TRUE(decoded_simple_frame); |
| 164 | ASSERT_EQ(simple_frame.size() - kHeaderSize, |
| 165 | decoded_simple_frame->ByteSize()); |
| 166 | |
| 167 | std::unique_ptr<Frame> decoded_frame = bfd.PopNextFrame(); |
| 168 | ASSERT_TRUE(decoded_frame); |
| 169 | ASSERT_TRUE(FrameEq(serialized_frame, *decoded_frame)); |
| 170 | } |
| 171 | |
| 172 | // Tests the case of a EndReceive(0) while receiving a valid frame in chunks. |
| 173 | TEST(BufferedFrameDeserializerTest, ZeroSizedReceive) { |
| 174 | BufferedFrameDeserializer bfd; |
| 175 | std::vector<char> frame = GetSimpleFrame(100); |
| 176 | std::vector<char> frame_chunk1(frame.begin(), frame.begin() + 50); |
| 177 | std::vector<char> frame_chunk2(frame.begin() + 50, frame.end()); |
| 178 | |
| 179 | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); |
| 180 | CheckedMemcpy(rbuf, frame_chunk1); |
| 181 | ASSERT_TRUE(bfd.EndReceive(frame_chunk1.size())); |
| 182 | |
| 183 | rbuf = bfd.BeginReceive(); |
| 184 | ASSERT_TRUE(bfd.EndReceive(0)); |
| 185 | |
| 186 | rbuf = bfd.BeginReceive(); |
| 187 | CheckedMemcpy(rbuf, frame_chunk2); |
| 188 | ASSERT_TRUE(bfd.EndReceive(frame_chunk2.size())); |
| 189 | |
| 190 | // Excactly one frame should be decoded, with no leftover buffer. |
| 191 | std::unique_ptr<Frame> decoded_frame = bfd.PopNextFrame(); |
| 192 | ASSERT_TRUE(decoded_frame); |
| 193 | ASSERT_TRUE(FrameEq(frame, *decoded_frame)); |
| 194 | ASSERT_FALSE(bfd.PopNextFrame()); |
| 195 | ASSERT_EQ(0u, bfd.size()); |
| 196 | } |
| 197 | |
| 198 | // Tests the case of a EndReceive(4) where the header has no payload. The frame |
| 199 | // should be just skipped and not returned by PopNextFrame(). |
| 200 | TEST(BufferedFrameDeserializerTest, EmptyPayload) { |
| 201 | BufferedFrameDeserializer bfd; |
| 202 | std::vector<char> frame = GetSimpleFrame(100); |
| 203 | |
| 204 | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); |
| 205 | std::vector<char> empty_frame(kHeaderSize, 0); |
| 206 | CheckedMemcpy(rbuf, empty_frame); |
| 207 | ASSERT_TRUE(bfd.EndReceive(kHeaderSize)); |
| 208 | |
| 209 | rbuf = bfd.BeginReceive(); |
| 210 | CheckedMemcpy(rbuf, frame); |
| 211 | ASSERT_TRUE(bfd.EndReceive(frame.size())); |
| 212 | |
| 213 | // |fram| should be properly decoded. |
| 214 | std::unique_ptr<Frame> decoded_frame = bfd.PopNextFrame(); |
| 215 | ASSERT_TRUE(decoded_frame); |
| 216 | ASSERT_TRUE(FrameEq(frame, *decoded_frame)); |
| 217 | ASSERT_FALSE(bfd.PopNextFrame()); |
| 218 | } |
| 219 | |
| 220 | // Test the case where a single Receive() returns batches of > 1 whole frames. |
| 221 | // See case C in the comments for BufferedFrameDeserializer::EndReceive(). |
| 222 | TEST(BufferedFrameDeserializerTest, MultipleFramesInOneReceive) { |
| 223 | BufferedFrameDeserializer bfd; |
| 224 | std::vector<std::vector<size_t>> frame_batch_sizes( |
| 225 | {{11}, {13, 17, 19}, {23}, {29, 31}}); |
| 226 | |
| 227 | for (std::vector<size_t>& batch : frame_batch_sizes) { |
| 228 | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); |
| 229 | size_t frame_offset_in_batch = 0; |
| 230 | for (size_t frame_size : batch) { |
| 231 | auto frame = GetSimpleFrame(frame_size); |
| 232 | CheckedMemcpy(rbuf, frame, frame_offset_in_batch); |
| 233 | frame_offset_in_batch += frame.size(); |
| 234 | } |
| 235 | ASSERT_TRUE(bfd.EndReceive(frame_offset_in_batch)); |
| 236 | for (size_t expected_size : batch) { |
| 237 | auto frame = bfd.PopNextFrame(); |
| 238 | ASSERT_TRUE(frame); |
| 239 | ASSERT_EQ(expected_size - kHeaderSize, frame->ByteSize()); |
| 240 | } |
| 241 | ASSERT_FALSE(bfd.PopNextFrame()); |
| 242 | ASSERT_EQ(0u, bfd.size()); |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | TEST(BufferedFrameDeserializerTest, RejectVeryLargeFrames) { |
| 247 | BufferedFrameDeserializer bfd; |
| 248 | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); |
| 249 | const uint32_t kBigSize = std::numeric_limits<uint32_t>::max() - 2; |
| 250 | memcpy(rbuf.data, base::AssumeLittleEndian(&kBigSize), kHeaderSize); |
| 251 | memcpy(rbuf.data + kHeaderSize, "some initial payload", 20); |
| 252 | ASSERT_FALSE(bfd.EndReceive(kHeaderSize + 20)); |
| 253 | } |
| 254 | |
| 255 | // Tests the extreme case of recv() fragmentation. Two valid frames are received |
| 256 | // but each recv() puts one byte at a time. Covers cases A and B commented in |
| 257 | // BufferedFrameDeserializer::EndReceive(). |
| 258 | TEST(BufferedFrameDeserializerTest, HighlyFragmentedFrames) { |
| 259 | BufferedFrameDeserializer bfd; |
| 260 | for (int i = 1; i <= 50; i++) { |
| 261 | std::vector<char> frame = GetSimpleFrame(i * 100); |
| 262 | for (size_t off = 0; off < frame.size(); off++) { |
| 263 | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); |
| 264 | CheckedMemcpy(rbuf, {frame[off]}); |
| 265 | |
| 266 | // The frame should be available only when receiving the last byte. |
| 267 | ASSERT_TRUE(bfd.EndReceive(1)); |
| 268 | if (off < frame.size() - 1) { |
| 269 | ASSERT_FALSE(bfd.PopNextFrame()) << off << "/" << frame.size(); |
| 270 | ASSERT_EQ(off + 1, bfd.size()); |
| 271 | } else { |
| 272 | ASSERT_TRUE(bfd.PopNextFrame()); |
| 273 | } |
| 274 | } |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | // A bunch of valid frames interleaved with frames that have a valid header |
| 279 | // but unparsable payload. The expectation is that PopNextFrame() returns |
| 280 | // nullptr for the unparsable frames but the other frames are decoded peroperly. |
| 281 | TEST(BufferedFrameDeserializerTest, CanRecoverAfterUnparsableFrames) { |
| 282 | BufferedFrameDeserializer bfd; |
| 283 | for (int i = 1; i <= 50; i++) { |
| 284 | const size_t size = i * 10; |
| 285 | std::vector<char> frame = GetSimpleFrame(size); |
| 286 | const bool unparsable = (i % 3) == 1; |
| 287 | if (unparsable) |
| 288 | memset(frame.data() + kHeaderSize, 0xFF, size - kHeaderSize); |
| 289 | |
| 290 | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); |
| 291 | CheckedMemcpy(rbuf, frame); |
| 292 | ASSERT_TRUE(bfd.EndReceive(frame.size())); |
| 293 | |
| 294 | // Excactly one frame should be decoded if |parsable|. In any case no |
| 295 | // leftover bytes should be left in the buffer. |
| 296 | auto decoded_frame = bfd.PopNextFrame(); |
| 297 | if (unparsable) { |
| 298 | ASSERT_FALSE(decoded_frame); |
| 299 | } else { |
| 300 | ASSERT_TRUE(decoded_frame); |
| 301 | ASSERT_EQ(size - kHeaderSize, decoded_frame->ByteSize()); |
| 302 | } |
| 303 | ASSERT_EQ(0u, bfd.size()); |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | // Test that we can sustain recvs() which constantly max out the capacity. |
| 308 | // It sets up four frames: |
| 309 | // |frame1|: small, 1024 + 4 bytes. |
| 310 | // |frame2|: as big as the |kMaxCapacity|. Its recv() is split into two chunks. |
| 311 | // |frame3|: together with the 2nd part of |frame2| it maxes out capacity again. |
| 312 | // |frame4|: as big as the |kMaxCapacity|. Received in one recv(), no splits. |
| 313 | // |
| 314 | // Which are then recv()'d in a loop in the following way. |
| 315 | // |------------ max recv capacity ------------| |
| 316 | // 1. [ frame1 ] [ frame2_chunk1 ..... ] |
| 317 | // 2. [ ... frame2_chunk2 ] |
| 318 | // 3. [ frame3 ] |
| 319 | // 4. [ frame 4 ] |
| 320 | TEST(BufferedFrameDeserializerTest, FillCapacity) { |
| 321 | size_t kMaxCapacity = 1024 * 16; |
| 322 | BufferedFrameDeserializer bfd(kMaxCapacity); |
| 323 | |
| 324 | for (int i = 0; i < 3; i++) { |
| 325 | std::vector<char> frame1 = GetSimpleFrame(1024); |
| 326 | std::vector<char> frame2 = GetSimpleFrame(kMaxCapacity); |
| 327 | std::vector<char> frame2_chunk1( |
| 328 | frame2.begin(), frame2.begin() + kMaxCapacity - frame1.size()); |
| 329 | std::vector<char> frame2_chunk2(frame2.begin() + frame2_chunk1.size(), |
| 330 | frame2.end()); |
| 331 | std::vector<char> frame3 = |
| 332 | GetSimpleFrame(kMaxCapacity - frame2_chunk2.size()); |
| 333 | std::vector<char> frame4 = GetSimpleFrame(kMaxCapacity); |
| 334 | ASSERT_EQ(kMaxCapacity, frame1.size() + frame2_chunk1.size()); |
| 335 | ASSERT_EQ(kMaxCapacity, frame2_chunk1.size() + frame2_chunk2.size()); |
| 336 | ASSERT_EQ(kMaxCapacity, frame2_chunk2.size() + frame3.size()); |
| 337 | ASSERT_EQ(kMaxCapacity, frame4.size()); |
| 338 | |
| 339 | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); |
| 340 | CheckedMemcpy(rbuf, frame1); |
| 341 | CheckedMemcpy(rbuf, frame2_chunk1, frame1.size()); |
| 342 | ASSERT_TRUE(bfd.EndReceive(frame1.size() + frame2_chunk1.size())); |
| 343 | |
| 344 | rbuf = bfd.BeginReceive(); |
| 345 | CheckedMemcpy(rbuf, frame2_chunk2); |
| 346 | ASSERT_TRUE(bfd.EndReceive(frame2_chunk2.size())); |
| 347 | |
| 348 | rbuf = bfd.BeginReceive(); |
| 349 | CheckedMemcpy(rbuf, frame3); |
| 350 | ASSERT_TRUE(bfd.EndReceive(frame3.size())); |
| 351 | |
| 352 | rbuf = bfd.BeginReceive(); |
| 353 | CheckedMemcpy(rbuf, frame4); |
| 354 | ASSERT_TRUE(bfd.EndReceive(frame4.size())); |
| 355 | |
| 356 | std::unique_ptr<Frame> decoded_frame_1 = bfd.PopNextFrame(); |
| 357 | ASSERT_TRUE(decoded_frame_1); |
| 358 | ASSERT_TRUE(FrameEq(frame1, *decoded_frame_1)); |
| 359 | |
| 360 | std::unique_ptr<Frame> decoded_frame_2 = bfd.PopNextFrame(); |
| 361 | ASSERT_TRUE(decoded_frame_2); |
| 362 | ASSERT_TRUE(FrameEq(frame2, *decoded_frame_2)); |
| 363 | |
| 364 | std::unique_ptr<Frame> decoded_frame_3 = bfd.PopNextFrame(); |
| 365 | ASSERT_TRUE(decoded_frame_3); |
| 366 | ASSERT_TRUE(FrameEq(frame3, *decoded_frame_3)); |
| 367 | |
| 368 | std::unique_ptr<Frame> decoded_frame_4 = bfd.PopNextFrame(); |
| 369 | ASSERT_TRUE(decoded_frame_4); |
| 370 | ASSERT_TRUE(FrameEq(frame4, *decoded_frame_4)); |
| 371 | |
| 372 | ASSERT_FALSE(bfd.PopNextFrame()); |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | } // namespace |
| 377 | } // namespace ipc |
| 378 | } // namespace perfetto |