| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/tracing/core/proto_zero_message.h" | 5 #include "components/tracing/core/proto_zero_message.h" |
| 6 | 6 |
| 7 #include <limits> | 7 #include <limits> |
| 8 #include <memory> | 8 #include <memory> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| 11 #include "base/hash.h" | 11 #include "base/hash.h" |
| 12 #include "components/tracing/core/proto_utils.h" | 12 #include "components/tracing/core/proto_utils.h" |
| 13 #include "components/tracing/core/proto_zero_message_handle.h" |
| 13 #include "components/tracing/test/fake_scattered_buffer.h" | 14 #include "components/tracing/test/fake_scattered_buffer.h" |
| 14 #include "testing/gtest/include/gtest/gtest.h" | 15 #include "testing/gtest/include/gtest/gtest.h" |
| 15 | 16 |
| 16 namespace tracing { | 17 namespace tracing { |
| 17 namespace v2 { | 18 namespace v2 { |
| 18 | 19 |
| 19 const size_t kChunkSize = 16; | 20 const size_t kChunkSize = 16; |
| 20 const uint8_t kTestBytes[] = {0, 0, 0, 0, 0x42, 1, 0x42, 0xff, 0x42, 0}; | 21 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 kStartWatermark[] = {'a', 'b', 'c', 'd', '1', '2', '3', '\0'}; |
| 22 const char kEndWatermark[] = {'9', '8', '7', '6', 'z', 'w', 'y', '\0'}; | 23 const char kEndWatermark[] = {'9', '8', '7', '6', 'z', 'w', 'y', '\0'}; |
| 23 | 24 |
| 24 class FakeMessage : public ProtoZeroMessage { | 25 class FakeRootMessage : public ProtoZeroMessage {}; |
| 25 public: | 26 class FakeChildMessage : public ProtoZeroMessage {}; |
| 26 }; | |
| 27 | 27 |
| 28 class ProtoZeroMessageTest : public ::testing::Test { | 28 class ProtoZeroMessageTest : public ::testing::Test { |
| 29 public: | 29 public: |
| 30 void SetUp() override { | 30 void SetUp() override { |
| 31 buffer_.reset(new FakeScatteredBuffer(kChunkSize)); | 31 buffer_.reset(new FakeScatteredBuffer(kChunkSize)); |
| 32 stream_writer_.reset(new ScatteredStreamWriter(buffer_.get())); | 32 stream_writer_.reset(new ScatteredStreamWriter(buffer_.get())); |
| 33 readback_pos_ = 0; | 33 readback_pos_ = 0; |
| 34 } | 34 } |
| 35 | 35 |
| 36 void TearDown() override { | 36 void TearDown() override { |
| 37 // Check that none of the messages created by the text fixtures below did | 37 // Check that none of the messages created by the text fixtures below did |
| 38 // under/overflow their heap boundaries. | 38 // under/overflow their heap boundaries. |
| 39 for (std::unique_ptr<uint8_t[]>& mem : messages_) { | 39 for (std::unique_ptr<uint8_t[]>& mem : messages_) { |
| 40 EXPECT_STREQ(kStartWatermark, reinterpret_cast<char*>(mem.get())); | 40 EXPECT_STREQ(kStartWatermark, reinterpret_cast<char*>(mem.get())); |
| 41 EXPECT_STREQ(kEndWatermark, | 41 EXPECT_STREQ(kEndWatermark, |
| 42 reinterpret_cast<char*>(mem.get() + sizeof(kStartWatermark) + | 42 reinterpret_cast<char*>(mem.get() + sizeof(kStartWatermark) + |
| 43 sizeof(ProtoZeroMessage))); | 43 sizeof(ProtoZeroMessage))); |
| 44 mem.reset(); | 44 mem.reset(); |
| 45 } | 45 } |
| 46 messages_.clear(); | 46 messages_.clear(); |
| 47 stream_writer_.reset(); | 47 stream_writer_.reset(); |
| 48 buffer_.reset(); | 48 buffer_.reset(); |
| 49 } | 49 } |
| 50 | 50 |
| 51 ProtoZeroMessage* NewMessage() { | 51 FakeRootMessage* NewMessage() { |
| 52 std::unique_ptr<uint8_t[]> mem( | 52 std::unique_ptr<uint8_t[]> mem( |
| 53 new uint8_t[sizeof(kStartWatermark) + sizeof(ProtoZeroMessage) + | 53 new uint8_t[sizeof(kStartWatermark) + sizeof(FakeRootMessage) + |
| 54 sizeof(kEndWatermark)]); | 54 sizeof(kEndWatermark)]); |
| 55 uint8_t* msg_start = mem.get() + sizeof(kStartWatermark); | 55 uint8_t* msg_start = mem.get() + sizeof(kStartWatermark); |
| 56 memcpy(mem.get(), kStartWatermark, sizeof(kStartWatermark)); | 56 memcpy(mem.get(), kStartWatermark, sizeof(kStartWatermark)); |
| 57 memset(msg_start, 0, sizeof(ProtoZeroMessage)); | 57 memset(msg_start, 0, sizeof(FakeRootMessage)); |
| 58 memcpy(msg_start + sizeof(ProtoZeroMessage), kEndWatermark, | 58 memcpy(msg_start + sizeof(FakeRootMessage), kEndWatermark, |
| 59 sizeof(kEndWatermark)); | 59 sizeof(kEndWatermark)); |
| 60 messages_.push_back(std::move(mem)); | 60 messages_.push_back(std::move(mem)); |
| 61 ProtoZeroMessage* msg = reinterpret_cast<ProtoZeroMessage*>(msg_start); | 61 FakeRootMessage* msg = reinterpret_cast<FakeRootMessage*>(msg_start); |
| 62 msg->Reset(stream_writer_.get()); | 62 msg->Reset(stream_writer_.get()); |
| 63 return msg; | 63 return msg; |
| 64 } | 64 } |
| 65 | 65 |
| 66 size_t GetNumSerializedBytes() { | 66 size_t GetNumSerializedBytes() { |
| 67 if (buffer_->chunks().empty()) | 67 if (buffer_->chunks().empty()) |
| 68 return 0; | 68 return 0; |
| 69 return buffer_->chunks().size() * kChunkSize - | 69 return buffer_->chunks().size() * kChunkSize - |
| 70 stream_writer_->bytes_available(); | 70 stream_writer_->bytes_available(); |
| 71 } | 71 } |
| 72 | 72 |
| 73 std::string GetNextSerializedBytes(size_t num_bytes) { | 73 std::string GetNextSerializedBytes(size_t num_bytes) { |
| 74 size_t old_readback_pos = readback_pos_; | 74 size_t old_readback_pos = readback_pos_; |
| 75 readback_pos_ += num_bytes; | 75 readback_pos_ += num_bytes; |
| 76 return buffer_->GetBytesAsString(old_readback_pos, num_bytes); | 76 return buffer_->GetBytesAsString(old_readback_pos, num_bytes); |
| 77 } | 77 } |
| 78 | 78 |
| 79 static void BuildNestedMessages(uint32_t depth, ProtoZeroMessage* msg) { | 79 static void BuildNestedMessages(uint32_t depth, ProtoZeroMessage* msg) { |
| 80 for (uint32_t i = 1; i <= 128; ++i) | 80 for (uint32_t i = 1; i <= 128; ++i) |
| 81 msg->AppendBytes(i, kTestBytes, sizeof(kTestBytes)); | 81 msg->AppendBytes(i, kTestBytes, sizeof(kTestBytes)); |
| 82 | 82 |
| 83 if (depth < ProtoZeroMessage::kMaxNestingDepth) { | 83 if (depth < ProtoZeroMessage::kMaxNestingDepth) { |
| 84 auto* child_msg = msg->BeginNestedMessage<FakeMessage>(1 + depth * 10); | 84 auto* nested_msg = |
| 85 BuildNestedMessages(depth + 1, child_msg); | 85 msg->BeginNestedMessage<FakeChildMessage>(1 + depth * 10); |
| 86 BuildNestedMessages(depth + 1, nested_msg); |
| 86 } | 87 } |
| 87 | 88 |
| 88 for (uint32_t i = 129; i <= 256; ++i) | 89 for (uint32_t i = 129; i <= 256; ++i) |
| 89 msg->AppendVarIntU32(i, 42); | 90 msg->AppendVarIntU32(i, 42); |
| 90 | 91 |
| 91 if ((depth & 2) == 0) | 92 if ((depth & 2) == 0) |
| 92 msg->Finalize(); | 93 msg->Finalize(); |
| 93 } | 94 } |
| 94 | 95 |
| 95 private: | 96 private: |
| (...skipping 28 matching lines...) Expand all Loading... |
| 124 ASSERT_EQ("31182D4454FB210940", GetNextSerializedBytes(9)); | 125 ASSERT_EQ("31182D4454FB210940", GetNextSerializedBytes(9)); |
| 125 ASSERT_EQ("3A0A00000000420142FF4200", GetNextSerializedBytes(12)); | 126 ASSERT_EQ("3A0A00000000420142FF4200", GetNextSerializedBytes(12)); |
| 126 ASSERT_EQ("8A101630313233343536373839616263646566414243444546", | 127 ASSERT_EQ("8A101630313233343536373839616263646566414243444546", |
| 127 GetNextSerializedBytes(25)); | 128 GetNextSerializedBytes(25)); |
| 128 } | 129 } |
| 129 | 130 |
| 130 TEST_F(ProtoZeroMessageTest, NestedMessagesSimple) { | 131 TEST_F(ProtoZeroMessageTest, NestedMessagesSimple) { |
| 131 ProtoZeroMessage* root_msg = NewMessage(); | 132 ProtoZeroMessage* root_msg = NewMessage(); |
| 132 root_msg->AppendVarIntU32(1 /* field_id */, 1); | 133 root_msg->AppendVarIntU32(1 /* field_id */, 1); |
| 133 | 134 |
| 134 FakeMessage* nested_msg = | 135 FakeChildMessage* nested_msg = |
| 135 root_msg->BeginNestedMessage<FakeMessage>(128 /* field_id */); | 136 root_msg->BeginNestedMessage<FakeChildMessage>(128 /* field_id */); |
| 136 ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(nested_msg) % sizeof(void*)); | 137 ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(nested_msg) % sizeof(void*)); |
| 137 nested_msg->AppendVarIntU32(2 /* field_id */, 2); | 138 nested_msg->AppendVarIntU32(2 /* field_id */, 2); |
| 138 | 139 |
| 139 nested_msg = root_msg->BeginNestedMessage<FakeMessage>(129 /* field_id */); | 140 nested_msg = |
| 141 root_msg->BeginNestedMessage<FakeChildMessage>(129 /* field_id */); |
| 140 nested_msg->AppendVarIntU32(4 /* field_id */, 2); | 142 nested_msg->AppendVarIntU32(4 /* field_id */, 2); |
| 141 | 143 |
| 142 root_msg->AppendVarIntU32(5 /* field_id */, 3); | 144 root_msg->AppendVarIntU32(5 /* field_id */, 3); |
| 143 | 145 |
| 144 // The expected size of the root message is supposed to be 20 bytes: | 146 // 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) | 147 // 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) | 148 // 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 | 149 // 2 bytes for the varint field (id: 2) of the 1st nested message |
| 148 // 6 bytes for the premable of the 2nd nested message | 150 // 6 bytes for the premable of the 2nd nested message |
| 149 // 2 bytes for the varint field (id: 4) of the 2nd nested message. | 151 // 2 bytes for the varint field (id: 4) of the 2nd nested message. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 163 ASSERT_EQ("8A0882808000", GetNextSerializedBytes(6)); | 165 ASSERT_EQ("8A0882808000", GetNextSerializedBytes(6)); |
| 164 ASSERT_EQ("2002", GetNextSerializedBytes(2)); | 166 ASSERT_EQ("2002", GetNextSerializedBytes(2)); |
| 165 | 167 |
| 166 ASSERT_EQ("2803", GetNextSerializedBytes(2)); | 168 ASSERT_EQ("2803", GetNextSerializedBytes(2)); |
| 167 } | 169 } |
| 168 | 170 |
| 169 // Checks that the size field of root and nested messages is properly written | 171 // Checks that the size field of root and nested messages is properly written |
| 170 // on finalization. | 172 // on finalization. |
| 171 TEST_F(ProtoZeroMessageTest, BackfillSizeOnFinalization) { | 173 TEST_F(ProtoZeroMessageTest, BackfillSizeOnFinalization) { |
| 172 ProtoZeroMessage* root_msg = NewMessage(); | 174 ProtoZeroMessage* root_msg = NewMessage(); |
| 173 uint8_t root_msg_size[proto::kMessageLengthFieldSize]; | 175 uint8_t root_msg_size[proto::kMessageLengthFieldSize] = {}; |
| 174 root_msg->set_size_field( | 176 root_msg->set_size_field( |
| 175 {&root_msg_size[0], &root_msg_size[proto::kMessageLengthFieldSize]}); | 177 {&root_msg_size[0], &root_msg_size[proto::kMessageLengthFieldSize]}); |
| 176 root_msg->AppendVarIntU32(1, 0x42); | 178 root_msg->AppendVarIntU32(1, 0x42); |
| 177 | 179 |
| 178 FakeMessage* nested_msg_1 = root_msg->BeginNestedMessage<FakeMessage>(2); | 180 FakeChildMessage* nested_msg_1 = |
| 181 root_msg->BeginNestedMessage<FakeChildMessage>(2); |
| 179 nested_msg_1->AppendVarIntU32(3, 0x43); | 182 nested_msg_1->AppendVarIntU32(3, 0x43); |
| 180 | 183 |
| 181 FakeMessage* nested_msg_2 = nested_msg_1->BeginNestedMessage<FakeMessage>(4); | 184 FakeChildMessage* nested_msg_2 = |
| 185 nested_msg_1->BeginNestedMessage<FakeChildMessage>(4); |
| 182 uint8_t buf200[200]; | 186 uint8_t buf200[200]; |
| 183 memset(buf200, 0x42, sizeof(buf200)); | 187 memset(buf200, 0x42, sizeof(buf200)); |
| 184 nested_msg_2->AppendBytes(5, buf200, sizeof(buf200)); | 188 nested_msg_2->AppendBytes(5, buf200, sizeof(buf200)); |
| 185 | 189 |
| 186 root_msg->inc_size_already_written(6); | 190 root_msg->inc_size_already_written(6); |
| 187 | 191 |
| 188 // The value returned by Finalize() should be == the full size of |root_msg|. | 192 // The value returned by Finalize() should be == the full size of |root_msg|. |
| 189 EXPECT_EQ(217u, root_msg->Finalize()); | 193 EXPECT_EQ(217u, root_msg->Finalize()); |
| 190 EXPECT_EQ(217u, GetNumSerializedBytes()); | 194 EXPECT_EQ(217u, GetNumSerializedBytes()); |
| 191 | 195 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 219 | 223 |
| 220 // The main point of this test is to stress the code paths and test for | 224 // 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 | 225 // unexpected crashes of the production code. The actual serialization is |
| 222 // already covered in the other text fixtures. Keeping just a final smoke test | 226 // already covered in the other text fixtures. Keeping just a final smoke test |
| 223 // here on the full buffer hash. | 227 // here on the full buffer hash. |
| 224 std::string full_buf = GetNextSerializedBytes(GetNumSerializedBytes()); | 228 std::string full_buf = GetNextSerializedBytes(GetNumSerializedBytes()); |
| 225 uint32_t buf_hash = base::SuperFastHash(full_buf.data(), full_buf.size()); | 229 uint32_t buf_hash = base::SuperFastHash(full_buf.data(), full_buf.size()); |
| 226 EXPECT_EQ(0x14BC1BA3u, buf_hash); | 230 EXPECT_EQ(0x14BC1BA3u, buf_hash); |
| 227 } | 231 } |
| 228 | 232 |
| 233 TEST_F(ProtoZeroMessageTest, MessageHandle) { |
| 234 FakeRootMessage* msg1 = NewMessage(); |
| 235 FakeRootMessage* msg2 = NewMessage(); |
| 236 FakeRootMessage* msg3 = NewMessage(); |
| 237 FakeRootMessage* ignored_msg = NewMessage(); |
| 238 uint8_t msg1_size[proto::kMessageLengthFieldSize] = {}; |
| 239 uint8_t msg2_size[proto::kMessageLengthFieldSize] = {}; |
| 240 uint8_t msg3_size[proto::kMessageLengthFieldSize] = {}; |
| 241 msg1->set_size_field( |
| 242 {&msg1_size[0], &msg1_size[proto::kMessageLengthFieldSize]}); |
| 243 msg2->set_size_field( |
| 244 {&msg2_size[0], &msg2_size[proto::kMessageLengthFieldSize]}); |
| 245 msg3->set_size_field( |
| 246 {&msg3_size[0], &msg3_size[proto::kMessageLengthFieldSize]}); |
| 247 |
| 248 // Test that the handle going out of scope causes the finalization of the |
| 249 // target message. |
| 250 { |
| 251 ProtoZeroMessageHandle<FakeRootMessage> handle1(msg1); |
| 252 handle1->AppendBytes(1 /* field_id */, kTestBytes, 1 /* size */); |
| 253 ASSERT_EQ(0u, msg1_size[0]); |
| 254 } |
| 255 ASSERT_EQ(0x83u, msg1_size[0]); |
| 256 |
| 257 // Test that the handle can be late initialized. |
| 258 ProtoZeroMessageHandle<FakeRootMessage> handle2(ignored_msg); |
| 259 handle2 = ProtoZeroMessageHandle<FakeRootMessage>(msg2); |
| 260 handle2->AppendBytes(1 /* field_id */, kTestBytes, 2 /* size */); |
| 261 ASSERT_EQ(0u, msg2_size[0]); // |msg2| should not be finalized yet. |
| 262 |
| 263 // Test that std::move works and does NOT cause finalization of the moved |
| 264 // message. |
| 265 ProtoZeroMessageHandle<FakeRootMessage> handle_swp(ignored_msg); |
| 266 handle_swp = std::move(handle2); |
| 267 ASSERT_EQ(0u, msg2_size[0]); // msg2 should be NOT finalized yet. |
| 268 handle_swp->AppendBytes(2 /* field_id */, kTestBytes, 3 /* size */); |
| 269 |
| 270 ProtoZeroMessageHandle<FakeRootMessage> handle3(msg3); |
| 271 handle3->AppendBytes(1 /* field_id */, kTestBytes, 4 /* size */); |
| 272 ASSERT_EQ(0u, msg3_size[0]); // msg2 should be NOT finalized yet. |
| 273 |
| 274 // Both |handle3| and |handle_swp| point to a valid message (respectively, |
| 275 // |msg3| and |msg2|). Now move |handle3| into |handle_swp|. |
| 276 handle_swp = std::move(handle3); |
| 277 ASSERT_EQ(0x89u, msg2_size[0]); // |msg2| should be finalized at this point. |
| 278 |
| 279 // At this point writing into handle_swp should actually write into |msg3|. |
| 280 ASSERT_EQ(msg3, &*handle_swp); |
| 281 handle_swp->AppendBytes(2 /* field_id */, kTestBytes, 8 /* size */); |
| 282 ProtoZeroMessageHandle<FakeRootMessage> another_handle(ignored_msg); |
| 283 handle_swp = std::move(another_handle); |
| 284 ASSERT_EQ(0x90u, msg3_size[0]); // |msg3| should be finalized at this point. |
| 285 |
| 286 #if DCHECK_IS_ON() |
| 287 // In developer builds w/ DCHECK on a finalized message should invalidate the |
| 288 // handle, in order to early catch bugs in the client code. |
| 289 FakeRootMessage* msg4 = NewMessage(); |
| 290 ProtoZeroMessageHandle<FakeRootMessage> handle4(msg4); |
| 291 ASSERT_EQ(msg4, &*handle4); |
| 292 msg4->Finalize(); |
| 293 ASSERT_EQ(nullptr, &*handle4); |
| 294 #endif |
| 295 |
| 296 // Test also the behavior of handle with non-root (nested) messages. |
| 297 |
| 298 ContiguousMemoryRange size_msg_2; |
| 299 { |
| 300 auto* nested_msg_1 = NewMessage()->BeginNestedMessage<FakeChildMessage>(3); |
| 301 ProtoZeroMessageHandle<FakeChildMessage> child_handle_1(nested_msg_1); |
| 302 ContiguousMemoryRange size_msg_1 = nested_msg_1->size_field(); |
| 303 memset(size_msg_1.begin, 0, size_msg_1.size()); |
| 304 child_handle_1->AppendVarIntU32(1, 0x11); |
| 305 |
| 306 auto* nested_msg_2 = NewMessage()->BeginNestedMessage<FakeChildMessage>(2); |
| 307 size_msg_2 = nested_msg_2->size_field(); |
| 308 memset(size_msg_2.begin, 0, size_msg_2.size()); |
| 309 ProtoZeroMessageHandle<FakeChildMessage> child_handle_2(nested_msg_2); |
| 310 child_handle_2->AppendVarIntU32(2, 0xFF); |
| 311 |
| 312 // |nested_msg_1| should not be finalized yet. |
| 313 ASSERT_EQ(0u, size_msg_1.begin[0]); |
| 314 |
| 315 // This move should cause |nested_msg_1| to be finalized, but not |
| 316 // |nested_msg_2|, which will be finalized only after the current scope. |
| 317 child_handle_1 = std::move(child_handle_2); |
| 318 ASSERT_EQ(0x82u, size_msg_1.begin[0]); |
| 319 ASSERT_EQ(0u, size_msg_2.begin[0]); |
| 320 } |
| 321 ASSERT_EQ(0x83u, size_msg_2.begin[0]); |
| 322 } |
| 323 |
| 229 } // namespace v2 | 324 } // namespace v2 |
| 230 } // namespace tracing | 325 } // namespace tracing |
| OLD | NEW |