|  | /* | 
|  | * Copyright (C) 2017 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include "src/ipc/buffered_frame_deserializer.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <string> | 
|  |  | 
|  | #include "perfetto/base/logging.h" | 
|  | #include "perfetto/ext/base/utils.h" | 
|  | #include "test/gtest_and_gmock.h" | 
|  |  | 
|  | #include "protos/perfetto/ipc/wire_protocol.gen.h" | 
|  |  | 
|  | namespace perfetto { | 
|  | namespace ipc { | 
|  | namespace { | 
|  |  | 
|  | constexpr uint32_t kHeaderSize = sizeof(uint32_t); | 
|  |  | 
|  | // Generates a parsable Frame of exactly |size| bytes (including header). | 
|  | std::vector<char> GetSimpleFrame(size_t size) { | 
|  | // A bit of reverse math of the proto encoding: a Frame which has only the | 
|  | // |data_for_testing| fields, will require for each data_for_testing that is | 
|  | // up to 127 bytes: | 
|  | // - 1 byte to write the field preamble (field type and id). | 
|  | // - 1 byte to write the field size, if 0 < size <= 127. | 
|  | // - N bytes for the actual content (|padding| below). | 
|  | // So below we split the payload into chunks of <= 127 bytes, keeping into | 
|  | // account the extra 2 bytes for each chunk. | 
|  | Frame frame; | 
|  | std::vector<char> padding; | 
|  | char padding_char = '0'; | 
|  | const uint32_t payload_size = static_cast<uint32_t>(size - kHeaderSize); | 
|  | for (uint32_t size_left = payload_size; size_left > 0;) { | 
|  | PERFETTO_CHECK(size_left >= 2);  // We cannot produce frames < 2 bytes. | 
|  | uint32_t padding_size; | 
|  | if (size_left <= 127) { | 
|  | padding_size = size_left - 2; | 
|  | size_left = 0; | 
|  | } else { | 
|  | padding_size = 124; | 
|  | size_left -= padding_size + 2; | 
|  | } | 
|  | padding.resize(padding_size); | 
|  | for (uint32_t i = 0; i < padding_size; i++) { | 
|  | padding_char = padding_char == 'z' ? '0' : padding_char + 1; | 
|  | padding[i] = padding_char; | 
|  | } | 
|  | frame.add_data_for_testing(std::string(padding.data(), padding_size)); | 
|  | } | 
|  | PERFETTO_CHECK(frame.SerializeAsString().size() == payload_size); | 
|  | std::vector<char> encoded_frame; | 
|  | encoded_frame.resize(size); | 
|  | char* enc_buf = encoded_frame.data(); | 
|  |  | 
|  | std::string payload = frame.SerializeAsString(); | 
|  | memcpy(enc_buf, base::AssumeLittleEndian(&payload_size), kHeaderSize); | 
|  | memcpy(enc_buf + kHeaderSize, payload.data(), payload.size()); | 
|  | PERFETTO_CHECK(encoded_frame.size() == size); | 
|  | return encoded_frame; | 
|  | } | 
|  |  | 
|  | void CheckedMemcpy(BufferedFrameDeserializer::ReceiveBuffer rbuf, | 
|  | const std::vector<char>& encoded_frame, | 
|  | size_t offset = 0) { | 
|  | ASSERT_GE(rbuf.size, encoded_frame.size() + offset); | 
|  | memcpy(rbuf.data + offset, encoded_frame.data(), encoded_frame.size()); | 
|  | } | 
|  |  | 
|  | bool FrameEq(std::vector<char> expected_frame_with_header, const Frame& frame) { | 
|  | std::string reserialized_frame = frame.SerializeAsString(); | 
|  |  | 
|  | size_t expected_size = expected_frame_with_header.size() - kHeaderSize; | 
|  | EXPECT_EQ(expected_size, reserialized_frame.size()); | 
|  | if (expected_size != reserialized_frame.size()) | 
|  | return false; | 
|  |  | 
|  | return memcmp(reserialized_frame.data(), | 
|  | expected_frame_with_header.data() + kHeaderSize, | 
|  | reserialized_frame.size()) == 0; | 
|  | } | 
|  |  | 
|  | // Tests the simple case where each recv() just returns one whole header+frame. | 
|  | TEST(BufferedFrameDeserializerTest, WholeMessages) { | 
|  | BufferedFrameDeserializer bfd; | 
|  | for (size_t i = 1; i <= 50; i++) { | 
|  | const size_t size = i * 10; | 
|  | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); | 
|  |  | 
|  | ASSERT_NE(nullptr, rbuf.data); | 
|  | std::vector<char> frame = GetSimpleFrame(size); | 
|  | CheckedMemcpy(rbuf, frame); | 
|  | ASSERT_TRUE(bfd.EndReceive(frame.size())); | 
|  |  | 
|  | // Excactly one frame should be decoded, with no leftover buffer. | 
|  | auto decoded_frame = bfd.PopNextFrame(); | 
|  | ASSERT_TRUE(decoded_frame); | 
|  | ASSERT_EQ(size - kHeaderSize, decoded_frame->SerializeAsString().size()); | 
|  | ASSERT_FALSE(bfd.PopNextFrame()); | 
|  | ASSERT_EQ(0u, bfd.size()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Sends first a simple test frame. Then creates a realistic Frame fragmenting | 
|  | // it in three chunks and tests that the decoded Frame matches the original one. | 
|  | // The recv() sequence is as follows: | 
|  | // 1. [ simple_frame ] [ frame_chunk1 ... ] | 
|  | // 2. [ ... frame_chunk2 ... ] | 
|  | // 3. [ ... frame_chunk3 ] | 
|  | TEST(BufferedFrameDeserializerTest, FragmentedFrameIsCorrectlyDeserialized) { | 
|  | BufferedFrameDeserializer bfd; | 
|  | Frame frame; | 
|  | frame.set_request_id(42); | 
|  | auto* bind_reply = frame.mutable_msg_bind_service_reply(); | 
|  | bind_reply->set_success(true); | 
|  | bind_reply->set_service_id(0x4242); | 
|  | auto* method = bind_reply->add_methods(); | 
|  | method->set_id(0x424242); | 
|  | method->set_name("foo"); | 
|  | std::vector<char> serialized_frame; | 
|  |  | 
|  | std::string payload = frame.SerializeAsString(); | 
|  | uint32_t payload_size = static_cast<uint32_t>(payload.size()); | 
|  | serialized_frame.resize(kHeaderSize + payload_size); | 
|  | memcpy(serialized_frame.data() + kHeaderSize, payload.data(), payload_size); | 
|  | memcpy(serialized_frame.data(), base::AssumeLittleEndian(&payload_size), | 
|  | kHeaderSize); | 
|  |  | 
|  | std::vector<char> simple_frame = GetSimpleFrame(32); | 
|  | std::vector<char> frame_chunk1(serialized_frame.begin(), | 
|  | serialized_frame.begin() + 5); | 
|  | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); | 
|  | CheckedMemcpy(rbuf, simple_frame); | 
|  | CheckedMemcpy(rbuf, frame_chunk1, simple_frame.size()); | 
|  | ASSERT_TRUE(bfd.EndReceive(simple_frame.size() + frame_chunk1.size())); | 
|  |  | 
|  | std::vector<char> frame_chunk2(serialized_frame.begin() + 5, | 
|  | serialized_frame.begin() + 10); | 
|  | rbuf = bfd.BeginReceive(); | 
|  | CheckedMemcpy(rbuf, frame_chunk2); | 
|  | ASSERT_TRUE(bfd.EndReceive(frame_chunk2.size())); | 
|  |  | 
|  | std::vector<char> frame_chunk3(serialized_frame.begin() + 10, | 
|  | serialized_frame.end()); | 
|  | rbuf = bfd.BeginReceive(); | 
|  | CheckedMemcpy(rbuf, frame_chunk3); | 
|  | ASSERT_TRUE(bfd.EndReceive(frame_chunk3.size())); | 
|  |  | 
|  | // Validate the received frame2. | 
|  | std::unique_ptr<Frame> decoded_simple_frame = bfd.PopNextFrame(); | 
|  | ASSERT_TRUE(decoded_simple_frame); | 
|  | ASSERT_EQ(simple_frame.size() - kHeaderSize, | 
|  | decoded_simple_frame->SerializeAsString().size()); | 
|  |  | 
|  | std::unique_ptr<Frame> decoded_frame = bfd.PopNextFrame(); | 
|  | ASSERT_TRUE(decoded_frame); | 
|  | ASSERT_TRUE(FrameEq(serialized_frame, *decoded_frame)); | 
|  | } | 
|  |  | 
|  | // Tests the case of a EndReceive(0) while receiving a valid frame in chunks. | 
|  | TEST(BufferedFrameDeserializerTest, ZeroSizedReceive) { | 
|  | BufferedFrameDeserializer bfd; | 
|  | std::vector<char> frame = GetSimpleFrame(100); | 
|  | std::vector<char> frame_chunk1(frame.begin(), frame.begin() + 50); | 
|  | std::vector<char> frame_chunk2(frame.begin() + 50, frame.end()); | 
|  |  | 
|  | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); | 
|  | CheckedMemcpy(rbuf, frame_chunk1); | 
|  | ASSERT_TRUE(bfd.EndReceive(frame_chunk1.size())); | 
|  |  | 
|  | rbuf = bfd.BeginReceive(); | 
|  | ASSERT_TRUE(bfd.EndReceive(0)); | 
|  |  | 
|  | rbuf = bfd.BeginReceive(); | 
|  | CheckedMemcpy(rbuf, frame_chunk2); | 
|  | ASSERT_TRUE(bfd.EndReceive(frame_chunk2.size())); | 
|  |  | 
|  | // Excactly one frame should be decoded, with no leftover buffer. | 
|  | std::unique_ptr<Frame> decoded_frame = bfd.PopNextFrame(); | 
|  | ASSERT_TRUE(decoded_frame); | 
|  | ASSERT_TRUE(FrameEq(frame, *decoded_frame)); | 
|  | ASSERT_FALSE(bfd.PopNextFrame()); | 
|  | ASSERT_EQ(0u, bfd.size()); | 
|  | } | 
|  |  | 
|  | // Tests the case of a EndReceive(4) where the header has no payload. The frame | 
|  | // should be just skipped and not returned by PopNextFrame(). | 
|  | TEST(BufferedFrameDeserializerTest, EmptyPayload) { | 
|  | BufferedFrameDeserializer bfd; | 
|  | std::vector<char> frame = GetSimpleFrame(100); | 
|  |  | 
|  | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); | 
|  | std::vector<char> empty_frame(kHeaderSize, 0); | 
|  | CheckedMemcpy(rbuf, empty_frame); | 
|  | ASSERT_TRUE(bfd.EndReceive(kHeaderSize)); | 
|  |  | 
|  | rbuf = bfd.BeginReceive(); | 
|  | CheckedMemcpy(rbuf, frame); | 
|  | ASSERT_TRUE(bfd.EndReceive(frame.size())); | 
|  |  | 
|  | // |fram| should be properly decoded. | 
|  | std::unique_ptr<Frame> decoded_frame = bfd.PopNextFrame(); | 
|  | ASSERT_TRUE(decoded_frame); | 
|  | ASSERT_TRUE(FrameEq(frame, *decoded_frame)); | 
|  | ASSERT_FALSE(bfd.PopNextFrame()); | 
|  | } | 
|  |  | 
|  | // Test the case where a single Receive() returns batches of > 1 whole frames. | 
|  | // See case C in the comments for BufferedFrameDeserializer::EndReceive(). | 
|  | TEST(BufferedFrameDeserializerTest, MultipleFramesInOneReceive) { | 
|  | BufferedFrameDeserializer bfd; | 
|  | std::vector<std::vector<size_t>> frame_batch_sizes( | 
|  | {{11}, {13, 17, 19}, {23}, {29, 31}}); | 
|  |  | 
|  | for (std::vector<size_t>& batch : frame_batch_sizes) { | 
|  | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); | 
|  | size_t frame_offset_in_batch = 0; | 
|  | for (size_t frame_size : batch) { | 
|  | auto frame = GetSimpleFrame(frame_size); | 
|  | CheckedMemcpy(rbuf, frame, frame_offset_in_batch); | 
|  | frame_offset_in_batch += frame.size(); | 
|  | } | 
|  | ASSERT_TRUE(bfd.EndReceive(frame_offset_in_batch)); | 
|  | for (size_t expected_size : batch) { | 
|  | auto frame = bfd.PopNextFrame(); | 
|  | ASSERT_TRUE(frame); | 
|  | ASSERT_EQ(expected_size - kHeaderSize, frame->SerializeAsString().size()); | 
|  | } | 
|  | ASSERT_FALSE(bfd.PopNextFrame()); | 
|  | ASSERT_EQ(0u, bfd.size()); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(BufferedFrameDeserializerTest, RejectVeryLargeFrames) { | 
|  | BufferedFrameDeserializer bfd; | 
|  | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); | 
|  | const uint32_t kBigSize = std::numeric_limits<uint32_t>::max() - 2; | 
|  | memcpy(rbuf.data, base::AssumeLittleEndian(&kBigSize), kHeaderSize); | 
|  | memcpy(rbuf.data + kHeaderSize, "some initial payload", 20); | 
|  | ASSERT_FALSE(bfd.EndReceive(kHeaderSize + 20)); | 
|  | } | 
|  |  | 
|  | // Tests the extreme case of recv() fragmentation. Two valid frames are received | 
|  | // but each recv() puts one byte at a time. Covers cases A and B commented in | 
|  | // BufferedFrameDeserializer::EndReceive(). | 
|  | TEST(BufferedFrameDeserializerTest, HighlyFragmentedFrames) { | 
|  | BufferedFrameDeserializer bfd; | 
|  | for (size_t i = 1; i <= 50; i++) { | 
|  | std::vector<char> frame = GetSimpleFrame(i * 100); | 
|  | for (size_t off = 0; off < frame.size(); off++) { | 
|  | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); | 
|  | CheckedMemcpy(rbuf, {frame[off]}); | 
|  |  | 
|  | // The frame should be available only when receiving the last byte. | 
|  | ASSERT_TRUE(bfd.EndReceive(1)); | 
|  | if (off < frame.size() - 1) { | 
|  | ASSERT_FALSE(bfd.PopNextFrame()) << off << "/" << frame.size(); | 
|  | ASSERT_EQ(off + 1, bfd.size()); | 
|  | } else { | 
|  | ASSERT_TRUE(bfd.PopNextFrame()); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // A bunch of valid frames interleaved with frames that have a valid header | 
|  | // but unparsable payload. The expectation is that PopNextFrame() returns | 
|  | // nullptr for the unparsable frames but the other frames are decoded peroperly. | 
|  | TEST(BufferedFrameDeserializerTest, CanRecoverAfterUnparsableFrames) { | 
|  | BufferedFrameDeserializer bfd; | 
|  | for (size_t i = 1; i <= 50; i++) { | 
|  | const size_t size = i * 10; | 
|  | std::vector<char> frame = GetSimpleFrame(size); | 
|  | const bool unparsable = (i % 3) == 1; | 
|  | if (unparsable) | 
|  | memset(frame.data() + kHeaderSize, 0xFF, size - kHeaderSize); | 
|  |  | 
|  | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); | 
|  | CheckedMemcpy(rbuf, frame); | 
|  | ASSERT_TRUE(bfd.EndReceive(frame.size())); | 
|  |  | 
|  | // Excactly one frame should be decoded if |parsable|. In any case no | 
|  | // leftover bytes should be left in the buffer. | 
|  | auto decoded_frame = bfd.PopNextFrame(); | 
|  | if (unparsable) { | 
|  | ASSERT_FALSE(decoded_frame); | 
|  | } else { | 
|  | ASSERT_TRUE(decoded_frame); | 
|  | ASSERT_EQ(size - kHeaderSize, decoded_frame->SerializeAsString().size()); | 
|  | } | 
|  | ASSERT_EQ(0u, bfd.size()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Test that we can sustain recvs() which constantly max out the capacity. | 
|  | // It sets up four frames: | 
|  | // |frame1|: small, 1024 + 4 bytes. | 
|  | // |frame2|: as big as the |kMaxCapacity|. Its recv() is split into two chunks. | 
|  | // |frame3|: together with the 2nd part of |frame2| it maxes out capacity again. | 
|  | // |frame4|: as big as the |kMaxCapacity|. Received in one recv(), no splits. | 
|  | // | 
|  | // Which are then recv()'d in a loop in the following way. | 
|  | //    |------------ max recv capacity ------------| | 
|  | // 1. [       frame1      ] [ frame2_chunk1 ..... ] | 
|  | // 2. [ ... frame2_chunk2 ] | 
|  | // 3. [  frame3  ] | 
|  | // 4. [               frame 4                     ] | 
|  | TEST(BufferedFrameDeserializerTest, FillCapacity) { | 
|  | size_t kMaxCapacity = 1024 * 16; | 
|  | BufferedFrameDeserializer bfd(kMaxCapacity); | 
|  |  | 
|  | for (int i = 0; i < 3; i++) { | 
|  | std::vector<char> frame1 = GetSimpleFrame(1024); | 
|  | std::vector<char> frame2 = GetSimpleFrame(kMaxCapacity); | 
|  | std::vector<char> frame2_chunk1( | 
|  | frame2.begin(), | 
|  | frame2.begin() + static_cast<ptrdiff_t>(kMaxCapacity - frame1.size())); | 
|  | std::vector<char> frame2_chunk2( | 
|  | frame2.begin() + static_cast<ptrdiff_t>(frame2_chunk1.size()), | 
|  | frame2.end()); | 
|  | std::vector<char> frame3 = | 
|  | GetSimpleFrame(kMaxCapacity - frame2_chunk2.size()); | 
|  | std::vector<char> frame4 = GetSimpleFrame(kMaxCapacity); | 
|  | ASSERT_EQ(kMaxCapacity, frame1.size() + frame2_chunk1.size()); | 
|  | ASSERT_EQ(kMaxCapacity, frame2_chunk1.size() + frame2_chunk2.size()); | 
|  | ASSERT_EQ(kMaxCapacity, frame2_chunk2.size() + frame3.size()); | 
|  | ASSERT_EQ(kMaxCapacity, frame4.size()); | 
|  |  | 
|  | BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); | 
|  | CheckedMemcpy(rbuf, frame1); | 
|  | CheckedMemcpy(rbuf, frame2_chunk1, frame1.size()); | 
|  | ASSERT_TRUE(bfd.EndReceive(frame1.size() + frame2_chunk1.size())); | 
|  |  | 
|  | rbuf = bfd.BeginReceive(); | 
|  | CheckedMemcpy(rbuf, frame2_chunk2); | 
|  | ASSERT_TRUE(bfd.EndReceive(frame2_chunk2.size())); | 
|  |  | 
|  | rbuf = bfd.BeginReceive(); | 
|  | CheckedMemcpy(rbuf, frame3); | 
|  | ASSERT_TRUE(bfd.EndReceive(frame3.size())); | 
|  |  | 
|  | rbuf = bfd.BeginReceive(); | 
|  | CheckedMemcpy(rbuf, frame4); | 
|  | ASSERT_TRUE(bfd.EndReceive(frame4.size())); | 
|  |  | 
|  | std::unique_ptr<Frame> decoded_frame_1 = bfd.PopNextFrame(); | 
|  | ASSERT_TRUE(decoded_frame_1); | 
|  | ASSERT_TRUE(FrameEq(frame1, *decoded_frame_1)); | 
|  |  | 
|  | std::unique_ptr<Frame> decoded_frame_2 = bfd.PopNextFrame(); | 
|  | ASSERT_TRUE(decoded_frame_2); | 
|  | ASSERT_TRUE(FrameEq(frame2, *decoded_frame_2)); | 
|  |  | 
|  | std::unique_ptr<Frame> decoded_frame_3 = bfd.PopNextFrame(); | 
|  | ASSERT_TRUE(decoded_frame_3); | 
|  | ASSERT_TRUE(FrameEq(frame3, *decoded_frame_3)); | 
|  |  | 
|  | std::unique_ptr<Frame> decoded_frame_4 = bfd.PopNextFrame(); | 
|  | ASSERT_TRUE(decoded_frame_4); | 
|  | ASSERT_TRUE(FrameEq(frame4, *decoded_frame_4)); | 
|  |  | 
|  | ASSERT_FALSE(bfd.PopNextFrame()); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace ipc | 
|  | }  // namespace perfetto |