| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "components/tracing/core/proto_zero_message.h" |
| 6 |
| 7 #include <limits> |
| 8 #include <memory> |
| 9 #include <vector> |
| 10 |
| 11 #include "base/hash.h" |
| 12 #include "components/tracing/core/proto_utils.h" |
| 13 #include "components/tracing/test/fake_scattered_buffer.h" |
| 14 #include "testing/gtest/include/gtest/gtest.h" |
| 15 |
| 16 namespace tracing { |
| 17 namespace v2 { |
| 18 |
| 19 const size_t kChunkSize = 16; |
| 20 const uint8_t kTestBytes[] = {0, 0, 0, 0, 0x42, 1, 0x42, 0xff, 0x42, 0}; |
| 21 const char kStartWatermark[] = {'a', 'b', 'c', 'd', '1', '2', '3', '\0'}; |
| 22 const char kEndWatermark[] = {'9', '8', '7', '6', 'z', 'w', 'y', '\0'}; |
| 23 |
| 24 class FakeMessage : public ProtoZeroMessage { |
| 25 public: |
| 26 }; |
| 27 |
| 28 class ProtoZeroMessageTest : public ::testing::Test { |
| 29 public: |
| 30 void SetUp() override { |
| 31 buffer_.reset(new FakeScatteredBuffer(kChunkSize)); |
| 32 stream_writer_.reset(new ScatteredStreamWriter(buffer_.get())); |
| 33 readback_pos_ = 0; |
| 34 } |
| 35 |
| 36 void TearDown() override { |
| 37 // Check that none of the messages created by the text fixtures below did |
| 38 // under/overflow their heap boundaries. |
| 39 for (std::unique_ptr<uint8_t[]>& mem : messages_) { |
| 40 EXPECT_STREQ(kStartWatermark, reinterpret_cast<char*>(mem.get())); |
| 41 EXPECT_STREQ(kEndWatermark, |
| 42 reinterpret_cast<char*>(mem.get() + sizeof(kStartWatermark) + |
| 43 sizeof(ProtoZeroMessage))); |
| 44 mem.reset(); |
| 45 } |
| 46 messages_.clear(); |
| 47 stream_writer_.reset(); |
| 48 buffer_.reset(); |
| 49 } |
| 50 |
| 51 ProtoZeroMessage* NewMessage() { |
| 52 std::unique_ptr<uint8_t[]> mem( |
| 53 new uint8_t[sizeof(kStartWatermark) + sizeof(ProtoZeroMessage) + |
| 54 sizeof(kEndWatermark)]); |
| 55 uint8_t* msg_start = mem.get() + sizeof(kStartWatermark); |
| 56 memcpy(mem.get(), kStartWatermark, sizeof(kStartWatermark)); |
| 57 memset(msg_start, 0, sizeof(ProtoZeroMessage)); |
| 58 memcpy(msg_start + sizeof(ProtoZeroMessage), kEndWatermark, |
| 59 sizeof(kEndWatermark)); |
| 60 messages_.push_back(std::move(mem)); |
| 61 ProtoZeroMessage* msg = reinterpret_cast<ProtoZeroMessage*>(msg_start); |
| 62 msg->Reset(stream_writer_.get()); |
| 63 return msg; |
| 64 } |
| 65 |
| 66 size_t GetNumSerializedBytes() { |
| 67 if (buffer_->chunks().empty()) |
| 68 return 0; |
| 69 return buffer_->chunks().size() * kChunkSize - |
| 70 stream_writer_->bytes_available(); |
| 71 } |
| 72 |
| 73 std::string GetNextSerializedBytes(size_t num_bytes) { |
| 74 size_t old_readback_pos = readback_pos_; |
| 75 readback_pos_ += num_bytes; |
| 76 return buffer_->GetBytesAsString(old_readback_pos, num_bytes); |
| 77 } |
| 78 |
| 79 static void BuildNestedMessages(uint32_t depth, ProtoZeroMessage* msg) { |
| 80 for (uint32_t i = 1; i <= 128; ++i) |
| 81 msg->AppendBytes(i, kTestBytes, sizeof(kTestBytes)); |
| 82 |
| 83 if (depth < ProtoZeroMessage::kMaxNestingDepth) { |
| 84 auto* child_msg = msg->BeginNestedMessage<FakeMessage>(1 + depth * 10); |
| 85 BuildNestedMessages(depth + 1, child_msg); |
| 86 } |
| 87 |
| 88 for (uint32_t i = 129; i <= 256; ++i) |
| 89 msg->AppendVarIntU32(i, 42); |
| 90 |
| 91 if ((depth & 2) == 0) |
| 92 msg->Finalize(); |
| 93 } |
| 94 |
| 95 private: |
| 96 std::unique_ptr<FakeScatteredBuffer> buffer_; |
| 97 std::unique_ptr<ScatteredStreamWriter> stream_writer_; |
| 98 std::vector<std::unique_ptr<uint8_t[]>> messages_; |
| 99 size_t readback_pos_; |
| 100 }; |
| 101 |
| 102 TEST_F(ProtoZeroMessageTest, BasicTypesNoNesting) { |
| 103 ProtoZeroMessage* msg = NewMessage(); |
| 104 msg->AppendVarIntU32(1 /* field_id */, 0); |
| 105 msg->AppendVarIntU32(2 /* field_id */, std::numeric_limits<uint32_t>::max()); |
| 106 msg->AppendVarIntU64(3 /* field_id */, 42); |
| 107 msg->AppendVarIntU64(4 /* field_id */, std::numeric_limits<uint64_t>::max()); |
| 108 msg->AppendFloat(5 /* field_id */, 3.1415f); |
| 109 msg->AppendDouble(6 /* field_id */, 3.14159265358979323846); |
| 110 msg->AppendBytes(7 /* field_id */, kTestBytes, sizeof(kTestBytes)); |
| 111 |
| 112 // Field ids > 16 are expected to be varint encoded (preamble > 1 byte) |
| 113 msg->AppendString(257 /* field_id */, "0123456789abcdefABCDEF"); |
| 114 |
| 115 EXPECT_EQ(72u, msg->Finalize()); |
| 116 EXPECT_EQ(72u, GetNumSerializedBytes()); |
| 117 |
| 118 // These lines match the serialization of the Append* calls above. |
| 119 ASSERT_EQ("0800", GetNextSerializedBytes(2)); |
| 120 ASSERT_EQ("10FFFFFFFF0F", GetNextSerializedBytes(6)); |
| 121 ASSERT_EQ("182A", GetNextSerializedBytes(2)); |
| 122 ASSERT_EQ("20FFFFFFFFFFFFFFFFFF01", GetNextSerializedBytes(11)); |
| 123 ASSERT_EQ("2D560E4940", GetNextSerializedBytes(5)); |
| 124 ASSERT_EQ("31182D4454FB210940", GetNextSerializedBytes(9)); |
| 125 ASSERT_EQ("3A0A00000000420142FF4200", GetNextSerializedBytes(12)); |
| 126 ASSERT_EQ("8A101630313233343536373839616263646566414243444546", |
| 127 GetNextSerializedBytes(25)); |
| 128 } |
| 129 |
| 130 TEST_F(ProtoZeroMessageTest, NestedMessagesSimple) { |
| 131 ProtoZeroMessage* root_msg = NewMessage(); |
| 132 root_msg->AppendVarIntU32(1 /* field_id */, 1); |
| 133 |
| 134 FakeMessage* nested_msg = |
| 135 root_msg->BeginNestedMessage<FakeMessage>(128 /* field_id */); |
| 136 ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(nested_msg) % sizeof(void*)); |
| 137 nested_msg->AppendVarIntU32(2 /* field_id */, 2); |
| 138 |
| 139 nested_msg = root_msg->BeginNestedMessage<FakeMessage>(129 /* field_id */); |
| 140 nested_msg->AppendVarIntU32(4 /* field_id */, 2); |
| 141 |
| 142 root_msg->AppendVarIntU32(5 /* field_id */, 3); |
| 143 |
| 144 // The expected size of the root message is supposed to be 20 bytes: |
| 145 // 2 bytes for the varint field (id: 1) (1 for preamble and one for payload) |
| 146 // 6 bytes for the preamble of the 1st nested message (2 for id, 4 for size) |
| 147 // 2 bytes for the varint field (id: 2) of the 1st nested message |
| 148 // 6 bytes for the premable of the 2nd nested message |
| 149 // 2 bytes for the varint field (id: 4) of the 2nd nested message. |
| 150 // 2 bytes for the last varint (id : 5) field of the root message. |
| 151 // Test also that finalization is idempontent and Finalize() can be safely |
| 152 // called more than once without side effects. |
| 153 for (int i = 0; i < 3; ++i) { |
| 154 EXPECT_EQ(20u, root_msg->Finalize()); |
| 155 EXPECT_EQ(20u, GetNumSerializedBytes()); |
| 156 } |
| 157 |
| 158 ASSERT_EQ("0801", GetNextSerializedBytes(2)); |
| 159 |
| 160 ASSERT_EQ("820882808000", GetNextSerializedBytes(6)); |
| 161 ASSERT_EQ("1002", GetNextSerializedBytes(2)); |
| 162 |
| 163 ASSERT_EQ("8A0882808000", GetNextSerializedBytes(6)); |
| 164 ASSERT_EQ("2002", GetNextSerializedBytes(2)); |
| 165 |
| 166 ASSERT_EQ("2803", GetNextSerializedBytes(2)); |
| 167 } |
| 168 |
| 169 // Checks that the size field of root and nested messages is properly written |
| 170 // on finalization. |
| 171 TEST_F(ProtoZeroMessageTest, BackfillSizeOnFinalization) { |
| 172 ProtoZeroMessage* root_msg = NewMessage(); |
| 173 uint8_t root_msg_size[proto::kMessageLengthFieldSize]; |
| 174 root_msg->set_size_field( |
| 175 {&root_msg_size[0], &root_msg_size[proto::kMessageLengthFieldSize]}); |
| 176 root_msg->AppendVarIntU32(1, 0x42); |
| 177 |
| 178 FakeMessage* nested_msg_1 = root_msg->BeginNestedMessage<FakeMessage>(2); |
| 179 nested_msg_1->AppendVarIntU32(3, 0x43); |
| 180 |
| 181 FakeMessage* nested_msg_2 = nested_msg_1->BeginNestedMessage<FakeMessage>(4); |
| 182 uint8_t buf200[200]; |
| 183 memset(buf200, 0x42, sizeof(buf200)); |
| 184 nested_msg_2->AppendBytes(5, buf200, sizeof(buf200)); |
| 185 |
| 186 root_msg->inc_size_already_written(6); |
| 187 |
| 188 // The value returned by Finalize() should be == the full size of |root_msg|. |
| 189 EXPECT_EQ(217u, root_msg->Finalize()); |
| 190 EXPECT_EQ(217u, GetNumSerializedBytes()); |
| 191 |
| 192 // However the size written in the size field should take into account the |
| 193 // inc_size_already_written() call and be equal to 118 - 6 = 112, encoded |
| 194 // in a rendundant varint encoding of kMessageLengthFieldSize bytes. |
| 195 EXPECT_STREQ("\xD3\x81\x80\x00", reinterpret_cast<char*>(root_msg_size)); |
| 196 |
| 197 // Skip 2 bytes for the 0x42 varint + 1 byte for the |nested_msg_1| preamble. |
| 198 GetNextSerializedBytes(3); |
| 199 |
| 200 // Check that the size of |nested_msg_1| was backfilled. Its size is: |
| 201 // 203 bytes for |nest_mesg_2| (see below) + 5 bytes for its preamble + |
| 202 // 2 bytes for the 0x43 varint = 210 bytes. |
| 203 EXPECT_EQ("D2818000", GetNextSerializedBytes(4)); |
| 204 |
| 205 // Skip 2 bytes for the 0x43 varint + 1 byte for the |nested_msg_2| preamble. |
| 206 GetNextSerializedBytes(3); |
| 207 |
| 208 // Check that the size of |nested_msg_2| was backfilled. Its size is: |
| 209 // 200 bytes (for |buf200|) + 3 bytes for its preamble = 203 bytes. |
| 210 EXPECT_EQ("CB818000", GetNextSerializedBytes(4)); |
| 211 } |
| 212 |
| 213 TEST_F(ProtoZeroMessageTest, StressTest) { |
| 214 std::vector<ProtoZeroMessage*> nested_msgs; |
| 215 |
| 216 ProtoZeroMessage* root_msg = NewMessage(); |
| 217 BuildNestedMessages(0, root_msg); |
| 218 root_msg->Finalize(); |
| 219 |
| 220 // The main point of this test is to stress the code paths and test for |
| 221 // unexpected crashes of the production code. The actual serialization is |
| 222 // already covered in the other text fixtures. Keeping just a final smoke test |
| 223 // here on the full buffer hash. |
| 224 std::string full_buf = GetNextSerializedBytes(GetNumSerializedBytes()); |
| 225 uint32_t buf_hash = base::SuperFastHash(full_buf.data(), full_buf.size()); |
| 226 EXPECT_EQ(0x14BC1BA3u, buf_hash); |
| 227 } |
| 228 |
| 229 } // namespace v2 |
| 230 } // namespace tracing |
| OLD | NEW |