| // Copyright 2019 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <string> |
| |
| #include "src/debug/wasm/gdb-server/packet.h" |
| #include "src/debug/wasm/gdb-server/session.h" |
| #include "src/debug/wasm/gdb-server/transport.h" |
| #include "test/unittests/test-utils.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace wasm { |
| namespace gdb_server { |
| |
| using ::testing::_; |
| using ::testing::Return; |
| using ::testing::SetArrayArgument; |
| using ::testing::StrEq; |
| |
| class WasmGdbRemoteTest : public ::testing::Test {}; |
| |
| TEST_F(WasmGdbRemoteTest, GdbRemotePacketAddChars) { |
| Packet packet; |
| |
| // Read empty packet |
| bool end_of_packet = packet.EndOfPacket(); |
| EXPECT_TRUE(end_of_packet); |
| |
| // Add raw chars |
| packet.AddRawChar('4'); |
| packet.AddRawChar('2'); |
| |
| std::string str; |
| packet.GetString(&str); |
| EXPECT_EQ("42", str); |
| } |
| |
| TEST_F(WasmGdbRemoteTest, GdbRemotePacketAddBlock) { |
| static const uint8_t block[] = {0x01, 0x02, 0x03, 0x04, 0x05, |
| 0x06, 0x07, 0x08, 0x09}; |
| static const size_t kLen = sizeof(block) / sizeof(uint8_t); |
| Packet packet; |
| packet.AddBlock(block, kLen); |
| |
| uint8_t buffer[kLen]; |
| bool ok = packet.GetBlock(buffer, kLen); |
| EXPECT_TRUE(ok); |
| EXPECT_EQ(0, memcmp(block, buffer, kLen)); |
| |
| packet.Rewind(); |
| std::string str; |
| ok = packet.GetString(&str); |
| EXPECT_TRUE(ok); |
| EXPECT_EQ("010203040506070809", str); |
| } |
| |
| TEST_F(WasmGdbRemoteTest, GdbRemotePacketAddString) { |
| Packet packet; |
| packet.AddHexString("foobar"); |
| |
| std::string str; |
| bool ok = packet.GetString(&str); |
| EXPECT_TRUE(ok); |
| EXPECT_EQ("666f6f626172", str); |
| |
| packet.Clear(); |
| packet.AddHexString("GDB"); |
| ok = packet.GetString(&str); |
| EXPECT_TRUE(ok); |
| EXPECT_EQ("474442", str); |
| } |
| |
| TEST_F(WasmGdbRemoteTest, GdbRemotePacketAddNumbers) { |
| Packet packet; |
| |
| static const uint64_t u64_val = 0xdeadbeef89abcdef; |
| static const uint8_t u8_val = 0x42; |
| packet.AddNumberSep(u64_val, ';'); |
| packet.AddWord8(u8_val); |
| |
| std::string str; |
| packet.GetString(&str); |
| EXPECT_EQ("deadbeef89abcdef;42", str); |
| |
| packet.Rewind(); |
| uint64_t val = 0; |
| char sep = '\0'; |
| bool ok = packet.GetNumberSep(&val, &sep); |
| EXPECT_TRUE(ok); |
| EXPECT_EQ(u64_val, val); |
| uint8_t b = 0; |
| ok = packet.GetWord8(&b); |
| EXPECT_TRUE(ok); |
| EXPECT_EQ(u8_val, b); |
| } |
| |
| TEST_F(WasmGdbRemoteTest, GdbRemotePacketSequenceNumber) { |
| Packet packet_with_sequence_num; |
| packet_with_sequence_num.AddWord8(42); |
| packet_with_sequence_num.AddRawChar(':'); |
| packet_with_sequence_num.AddHexString("foobar"); |
| |
| int32_t sequence_num = 0; |
| packet_with_sequence_num.ParseSequence(); |
| bool ok = packet_with_sequence_num.GetSequence(&sequence_num); |
| EXPECT_TRUE(ok); |
| EXPECT_EQ(42, sequence_num); |
| |
| Packet packet_without_sequence_num; |
| packet_without_sequence_num.AddHexString("foobar"); |
| |
| packet_without_sequence_num.ParseSequence(); |
| ok = packet_without_sequence_num.GetSequence(&sequence_num); |
| EXPECT_FALSE(ok); |
| } |
| |
| TEST_F(WasmGdbRemoteTest, GdbRemotePacketRunLengthEncoded) { |
| Packet packet1; |
| packet1.AddRawChar('0'); |
| packet1.AddRawChar('*'); |
| packet1.AddRawChar(' '); |
| |
| std::string str1; |
| bool ok = packet1.GetHexString(&str1); |
| EXPECT_TRUE(ok); |
| EXPECT_EQ("0000", std::string(packet1.GetPayload())); |
| |
| Packet packet2; |
| packet2.AddRawChar('1'); |
| packet2.AddRawChar('2'); |
| packet2.AddRawChar('3'); |
| packet2.AddRawChar('*'); |
| packet2.AddRawChar(' '); |
| packet2.AddRawChar('a'); |
| packet2.AddRawChar('b'); |
| |
| std::string str2; |
| ok = packet2.GetHexString(&str2); |
| EXPECT_TRUE(ok); |
| EXPECT_EQ("123333ab", std::string(packet2.GetPayload())); |
| } |
| |
| TEST_F(WasmGdbRemoteTest, GdbRemoteUtilStringSplit) { |
| std::vector<std::string> parts1 = StringSplit({}, ","); |
| EXPECT_EQ(size_t(0), parts1.size()); |
| |
| auto parts2 = StringSplit("a", nullptr); |
| EXPECT_EQ(size_t(1), parts2.size()); |
| EXPECT_EQ("a", parts2[0]); |
| |
| auto parts3 = StringSplit(";a;bc;def;", ","); |
| EXPECT_EQ(size_t(1), parts3.size()); |
| EXPECT_EQ(";a;bc;def;", parts3[0]); |
| |
| auto parts4 = StringSplit(";a;bc;def;", ";"); |
| EXPECT_EQ(size_t(3), parts4.size()); |
| EXPECT_EQ("a", parts4[0]); |
| EXPECT_EQ("bc", parts4[1]); |
| EXPECT_EQ("def", parts4[2]); |
| } |
| |
| class MockTransport : public TransportBase { |
| public: |
| MOCK_METHOD(bool, AcceptConnection, (), (override)); |
| MOCK_METHOD(bool, Read, (char*, int32_t), (override)); |
| MOCK_METHOD(bool, Write, (const char*, int32_t), (override)); |
| MOCK_METHOD(bool, IsDataAvailable, (), (override)); |
| MOCK_METHOD(bool, IsDataAvailable, (), (const, override)); |
| MOCK_METHOD(void, Disconnect, (), (override)); |
| MOCK_METHOD(void, Close, (), (override)); |
| MOCK_METHOD(void, WaitForDebugStubEvent, (), (override)); |
| MOCK_METHOD(bool, SignalThreadEvent, (), (override)); |
| }; |
| |
| TEST_F(WasmGdbRemoteTest, GdbRemoteSessionSendPacket) { |
| const char* ack_buffer = "+"; |
| |
| MockTransport mock_transport; |
| EXPECT_CALL(mock_transport, Write(StrEq("$474442#39"), 10)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(mock_transport, Read(_, _)) |
| .Times(1) |
| .WillOnce( |
| DoAll(SetArrayArgument<0>(ack_buffer, ack_buffer + 1), Return(true))); |
| |
| Session session(&mock_transport); |
| |
| Packet packet; |
| packet.AddHexString("GDB"); |
| bool ok = session.SendPacket(&packet); |
| EXPECT_TRUE(ok); |
| } |
| |
| TEST_F(WasmGdbRemoteTest, GdbRemoteSessionSendPacketDisconnectOnNoAck) { |
| MockTransport mock_transport; |
| EXPECT_CALL(mock_transport, Write(StrEq("$474442#39"), 10)) |
| .Times(1) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(mock_transport, Read(_, _)).Times(1).WillOnce(Return(false)); |
| EXPECT_CALL(mock_transport, Disconnect()).Times(1); |
| |
| Session session(&mock_transport); |
| |
| Packet packet; |
| packet.AddHexString("GDB"); |
| bool ok = session.SendPacket(&packet); |
| EXPECT_FALSE(ok); |
| } |
| |
| TEST_F(WasmGdbRemoteTest, GdbRemoteSessionGetPacketCheckChecksum) { |
| const char* buffer_bad = "$47#00"; |
| const char* buffer_ok = "$47#6b"; |
| |
| MockTransport mock_transport; |
| EXPECT_CALL(mock_transport, Read(_, _)) |
| .WillOnce( |
| DoAll(SetArrayArgument<0>(buffer_bad, buffer_bad + 1), Return(true))) |
| .WillOnce(DoAll(SetArrayArgument<0>(buffer_bad + 1, buffer_bad + 2), |
| Return(true))) |
| .WillOnce(DoAll(SetArrayArgument<0>(buffer_bad + 2, buffer_bad + 3), |
| Return(true))) |
| .WillOnce(DoAll(SetArrayArgument<0>(buffer_bad + 3, buffer_bad + 4), |
| Return(true))) |
| .WillOnce(DoAll(SetArrayArgument<0>(buffer_bad + 4, buffer_bad + 5), |
| Return(true))) |
| .WillOnce(DoAll(SetArrayArgument<0>(buffer_bad + 5, buffer_bad + 6), |
| Return(true))) |
| .WillOnce( |
| DoAll(SetArrayArgument<0>(buffer_ok, buffer_ok + 1), Return(true))) |
| .WillOnce(DoAll(SetArrayArgument<0>(buffer_ok + 1, buffer_ok + 2), |
| Return(true))) |
| .WillOnce(DoAll(SetArrayArgument<0>(buffer_ok + 2, buffer_ok + 3), |
| Return(true))) |
| .WillOnce(DoAll(SetArrayArgument<0>(buffer_ok + 3, buffer_ok + 4), |
| Return(true))) |
| .WillOnce(DoAll(SetArrayArgument<0>(buffer_ok + 4, buffer_ok + 5), |
| Return(true))) |
| .WillOnce(DoAll(SetArrayArgument<0>(buffer_ok + 5, buffer_ok + 6), |
| Return(true))); |
| EXPECT_CALL(mock_transport, Write(StrEq("-"), 1)) // Signal bad packet |
| .Times(1) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(mock_transport, Write(StrEq("+"), 1)) // Signal ack |
| .Times(1) |
| .WillOnce(Return(true)); |
| |
| Session session(&mock_transport); |
| |
| Packet packet; |
| bool ok = session.GetPacket(&packet); |
| EXPECT_TRUE(ok); |
| char ch; |
| ok = packet.GetBlock(&ch, 1); |
| EXPECT_TRUE(ok); |
| EXPECT_EQ('G', ch); |
| } |
| |
| TEST_F(WasmGdbRemoteTest, GdbRemoteSessionGetPacketDisconnectOnReadFailure) { |
| MockTransport mock_transport; |
| EXPECT_CALL(mock_transport, Read(_, _)).Times(1).WillOnce(Return(false)); |
| EXPECT_CALL(mock_transport, Disconnect()).Times(1); |
| |
| Session session(&mock_transport); |
| Packet packet; |
| bool ok = session.GetPacket(&packet); |
| EXPECT_FALSE(ok); |
| } |
| |
| } // namespace gdb_server |
| } // namespace wasm |
| } // namespace internal |
| } // namespace v8 |