/* * 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 #include #include "gtest/gtest.h" #include "perfetto/base/logging.h" #include "perfetto/base/utils.h" #include "src/ipc/wire_protocol.pb.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 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 padding; char padding_char = '0'; const uint32_t payload_size = static_cast(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(padding.data(), padding_size); } PERFETTO_CHECK(frame.ByteSize() == static_cast(payload_size)); std::vector encoded_frame; encoded_frame.resize(size); char* enc_buf = encoded_frame.data(); PERFETTO_CHECK(frame.SerializeToArray(enc_buf + kHeaderSize, static_cast(payload_size))); memcpy(enc_buf, base::AssumeLittleEndian(&payload_size), kHeaderSize); PERFETTO_CHECK(encoded_frame.size() == size); return encoded_frame; } void CheckedMemcpy(BufferedFrameDeserializer::ReceiveBuffer rbuf, const std::vector& 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 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 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(static_cast(size - kHeaderSize), decoded_frame->ByteSize()); 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 serialized_frame; uint32_t payload_size = static_cast(frame.ByteSize()); serialized_frame.resize(kHeaderSize + payload_size); ASSERT_TRUE(frame.SerializeToArray(serialized_frame.data() + kHeaderSize, static_cast(payload_size))); memcpy(serialized_frame.data(), base::AssumeLittleEndian(&payload_size), kHeaderSize); std::vector simple_frame = GetSimpleFrame(32); std::vector 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 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 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 decoded_simple_frame = bfd.PopNextFrame(); ASSERT_TRUE(decoded_simple_frame); ASSERT_EQ(static_cast(simple_frame.size() - kHeaderSize), decoded_simple_frame->ByteSize()); std::unique_ptr 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 frame = GetSimpleFrame(100); std::vector frame_chunk1(frame.begin(), frame.begin() + 50); std::vector 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 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 frame = GetSimpleFrame(100); BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive(); std::vector 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 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> frame_batch_sizes( {{11}, {13, 17, 19}, {23}, {29, 31}}); for (std::vector& 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(static_cast(expected_size - kHeaderSize), frame->ByteSize()); } 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::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 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 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(static_cast(size - kHeaderSize), decoded_frame->ByteSize()); } 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 frame1 = GetSimpleFrame(1024); std::vector frame2 = GetSimpleFrame(kMaxCapacity); std::vector frame2_chunk1( frame2.begin(), frame2.begin() + static_cast(kMaxCapacity - frame1.size())); std::vector frame2_chunk2( frame2.begin() + static_cast(frame2_chunk1.size()), frame2.end()); std::vector frame3 = GetSimpleFrame(kMaxCapacity - frame2_chunk2.size()); std::vector 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 decoded_frame_1 = bfd.PopNextFrame(); ASSERT_TRUE(decoded_frame_1); ASSERT_TRUE(FrameEq(frame1, *decoded_frame_1)); std::unique_ptr decoded_frame_2 = bfd.PopNextFrame(); ASSERT_TRUE(decoded_frame_2); ASSERT_TRUE(FrameEq(frame2, *decoded_frame_2)); std::unique_ptr decoded_frame_3 = bfd.PopNextFrame(); ASSERT_TRUE(decoded_frame_3); ASSERT_TRUE(FrameEq(frame3, *decoded_frame_3)); std::unique_ptr 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