diff options
Diffstat (limited to 'src/processor/network_source_line_server_unittest.cc')
-rw-r--r-- | src/processor/network_source_line_server_unittest.cc | 955 |
1 files changed, 955 insertions, 0 deletions
diff --git a/src/processor/network_source_line_server_unittest.cc b/src/processor/network_source_line_server_unittest.cc new file mode 100644 index 00000000..5e969ccf --- /dev/null +++ b/src/processor/network_source_line_server_unittest.cc @@ -0,0 +1,955 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Unit tests for NetworkSourceLineServer. + +#include <ios> +#include <set> +#include <string> + +#include "breakpad_googletest_includes.h" +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/stack_frame.h" +#include "google_breakpad/processor/symbol_supplier.h" +#include "processor/binarystream.h" +#include "processor/cfi_frame_info.h" +#include "processor/network_source_line_server.h" +#include "processor/network_source_line_protocol.h" +#include "processor/windows_frame_info.h" + +namespace { +using std::ios_base; +using std::set; +using std::string; +using google_breakpad::CFIFrameInfo; +using google_breakpad::CodeModule; +using google_breakpad::binarystream; +using google_breakpad::NetworkInterface; +using google_breakpad::NetworkSourceLineServer; +using google_breakpad::SourceLineResolverInterface; +using google_breakpad::StackFrame; +using google_breakpad::SymbolSupplier; +using google_breakpad::SystemInfo; +using google_breakpad::WindowsFrameInfo; +using ::testing::_; +using ::testing::DoAll; +using ::testing::Invoke; +using ::testing::Property; +using ::testing::Return; +using ::testing::SetArgumentPointee; +// Style guide forbids "using namespace", so at least shorten it. +namespace P = google_breakpad::source_line_protocol; + +class MockNetwork : public NetworkInterface { + public: + MockNetwork() {} + + MOCK_METHOD1(Init, bool(bool listen)); + MOCK_METHOD2(Send, bool(const char *data, size_t length)); + MOCK_METHOD1(WaitToReceive, bool(int timeout)); + MOCK_METHOD3(Receive, bool(char *buffer, size_t buffer_size, + ssize_t &received)); +}; + +class MockSymbolSupplier : public SymbolSupplier { +public: + MockSymbolSupplier() {} + + MOCK_METHOD3(GetSymbolFile, SymbolResult(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file)); + MOCK_METHOD4(GetSymbolFile, SymbolResult(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data)); +}; + +class MockSourceLineResolver : public SourceLineResolverInterface { + public: + MockSourceLineResolver() {} + virtual ~MockSourceLineResolver() {} + + MOCK_METHOD2(LoadModule, bool(const CodeModule *module, + const string &map_file)); + MOCK_METHOD2(LoadModuleUsingMapBuffer, bool(const CodeModule *module, + const string &map_buffer)); + MOCK_METHOD1(UnloadModule, void(const CodeModule *module)); + MOCK_METHOD1(HasModule, bool(const CodeModule *module)); + MOCK_METHOD1(FillSourceLineInfo, void(StackFrame *frame)); + MOCK_METHOD1(FindWindowsFrameInfo, + WindowsFrameInfo*(const StackFrame *frame)); + MOCK_METHOD1(FindCFIFrameInfo, + CFIFrameInfo*(const StackFrame *frame)); +}; + +class TestNetworkSourceLineServer : public NetworkSourceLineServer { + public: + // Override visibility for testing. It's a lot easier to just + // call into this method and verify the result than it would be + // to mock out the calls to the NetworkInterface, even though + // that would ostensibly be more correct and test the code more + // thoroughly. Perhaps if someone has time and figures out a + // clean way to do it this could be changed. + using NetworkSourceLineServer::HandleRequest; + + TestNetworkSourceLineServer(SymbolSupplier *supplier, + SourceLineResolverInterface *resolver, + NetworkInterface *net, + u_int64_t max_symbol_lines = 0) + : NetworkSourceLineServer(supplier, resolver, net, max_symbol_lines) + + {} +}; + +class NetworkSourceLineServerTest : public ::testing::Test { + public: + MockSymbolSupplier supplier; + MockSourceLineResolver resolver; + MockNetwork net; + TestNetworkSourceLineServer *server; + + NetworkSourceLineServerTest() : server(NULL) {} + + void SetUp() { + server = new TestNetworkSourceLineServer(&supplier, &resolver, &net); + } +}; + +TEST_F(NetworkSourceLineServerTest, TestInit) { + EXPECT_CALL(net, Init(true)).WillOnce(Return(true)); + EXPECT_CALL(net, WaitToReceive(0)).WillOnce(Return(false)); + ASSERT_TRUE(server->Initialize()); + EXPECT_FALSE(server->RunOnce(0)); +} + +TEST_F(NetworkSourceLineServerTest, TestMalformedRequest) { + binarystream request; + // send a request without a full sequence number + request << u_int8_t(1); + binarystream response; + EXPECT_FALSE(server->HandleRequest(request, response)); + request.rewind(); + // send a request without a command + request << u_int16_t(1); + EXPECT_FALSE(server->HandleRequest(request, response)); +} + +TEST_F(NetworkSourceLineServerTest, TestUnknownCommand) { + binarystream request; + // send a request with an unknown command + request << u_int16_t(1) << u_int8_t(100); + binarystream response; + ASSERT_TRUE(server->HandleRequest(request, response)); + u_int16_t response_sequence; + u_int8_t response_status; + response >> response_sequence >> response_status; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(u_int16_t(1), response_sequence); + EXPECT_EQ(P::ERROR, int(response_status)); +} + +TEST_F(NetworkSourceLineServerTest, TestHasBasic) { + EXPECT_CALL(resolver, HasModule(_)) + .WillOnce(Return(false)) + .WillOnce(Return(true)); + + binarystream request; + const u_int16_t sequence = 0xA0A0; + // first request should come back as not loaded + request << sequence << P::HAS << string("test.dll") << string("test.pdb") + << string("ABCD1234"); + binarystream response; + ASSERT_TRUE(server->HandleRequest(request, response)); + u_int16_t response_sequence; + u_int8_t response_status, response_data; + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ(P::MODULE_NOT_LOADED, int(response_data)); + // second request should come back as loaded + binarystream request2; + request2 << sequence << P::HAS << string("loaded.dll") << string("loaded.pdb") + << string("ABCD1234"); + ASSERT_TRUE(server->HandleRequest(request2, response)); + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ(P::MODULE_LOADED, int(response_data)); +} + +TEST_F(NetworkSourceLineServerTest, TestMalformedHasRequest) { + binarystream request; + // send request with just command, missing all data + const u_int16_t sequence = 0xA0A0; + request << sequence << P::HAS; + binarystream response; + ASSERT_TRUE(server->HandleRequest(request, response)); + u_int16_t response_sequence; + u_int8_t response_status; + response >> response_sequence >> response_status; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::ERROR, int(response_status)); + // send request with just module name + binarystream request2; + request2 << sequence << P::HAS << string("test.dll"); + ASSERT_TRUE(server->HandleRequest(request2, response)); + response >> response_sequence >> response_status; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::ERROR, int(response_status)); + // send request with module name, debug file, missing debug id + binarystream request3; + request3 << sequence << P::HAS << string("test.dll") << string("test.pdb"); + ASSERT_TRUE(server->HandleRequest(request3, response)); + response >> response_sequence >> response_status; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::ERROR, int(response_status)); +} + +TEST_F(NetworkSourceLineServerTest, TestHasLoad) { + EXPECT_CALL(resolver, HasModule(_)) + .WillOnce(Return(false)) + .WillOnce(Return(false)) + .WillOnce(Return(true)); + EXPECT_CALL(resolver, LoadModuleUsingMapBuffer(_,_)) + .WillOnce(Return(true)); + EXPECT_CALL(supplier, GetSymbolFile(_,_,_,_)) + .WillOnce(Return(SymbolSupplier::FOUND)); + + // verify that the module is not loaded, with a HAS request + binarystream request; + const u_int16_t sequence = 0xA0A0; + request << sequence << P::HAS << string("found.dll") << string("found.pdb") + << string("ABCD1234"); + binarystream response; + ASSERT_TRUE(server->HandleRequest(request, response)); + u_int16_t response_sequence; + u_int8_t response_status, response_data; + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(P::MODULE_NOT_LOADED, int(response_data)); + // now send a load request for this module + binarystream request2; + const u_int16_t sequence2 = 0xB0B0; + request2 << sequence2 << P::LOAD << string("found.dll") << string("found.pdb") + << string("ABCD1234"); + ASSERT_TRUE(server->HandleRequest(request2, response)); + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence2, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_OK), int(response_data)); + // sending another HAS message should now show it as loaded + binarystream request3; + const u_int16_t sequence3 = 0xC0C0; + request3 << sequence3 << P::HAS << string("found.dll") << string("found.pdb") + << string("ABCD1234"); + ASSERT_TRUE(server->HandleRequest(request3, response)); + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence3, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ(P::MODULE_LOADED, int(response_data)); +} + +TEST_F(NetworkSourceLineServerTest, TestLoad) { + EXPECT_CALL(resolver, HasModule(_)) + .Times(3) + .WillRepeatedly(Return(false)); + EXPECT_CALL(resolver, LoadModuleUsingMapBuffer(_,_)) + .WillOnce(Return(false)); + EXPECT_CALL(supplier, GetSymbolFile(_,_,_,_)) + .WillOnce(Return(SymbolSupplier::NOT_FOUND)) + .WillOnce(Return(SymbolSupplier::INTERRUPT)) + .WillOnce(Return(SymbolSupplier::FOUND)); + + // notfound.dll should return LOAD_NOT_FOUND + binarystream request; + const u_int16_t sequence = 0xA0A0; + request << sequence << P::LOAD << string("notfound.dll") + << string("notfound.pdb") << string("ABCD1234"); + binarystream response; + ASSERT_TRUE(server->HandleRequest(request, response)); + u_int16_t response_sequence; + u_int8_t response_status, response_data; + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ(int(P::LOAD_NOT_FOUND), int(response_data)); + // interrupt.dll should return LOAD_INTERRUPT + binarystream request2; + const u_int16_t sequence2 = 0xB0B0; + request2 << sequence2 << P::LOAD << string("interrupt.dll") + << string("interrupt.pdb") << string("0000"); + ASSERT_TRUE(server->HandleRequest(request2, response)); + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence2, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ(int(P::LOAD_INTERRUPT), int(response_data)); + // fail.dll should return LOAD_FAIL + binarystream request3; + const u_int16_t sequence3 = 0xC0C0; + request3 << sequence3 << P::LOAD << string("fail.dll") << string("fail.pdb") + << string("FFFFFFFF"); + ASSERT_TRUE(server->HandleRequest(request3, response)); + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence3, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ(int(P::LOAD_FAIL), int(response_data)); +} + +TEST_F(NetworkSourceLineServerTest, TestMalformedLoadRequest) { + binarystream request; + // send request with just command, missing all data + const u_int16_t sequence = 0xA0A0; + request << sequence << P::LOAD; + binarystream response; + ASSERT_TRUE(server->HandleRequest(request, response)); + u_int16_t response_sequence; + u_int8_t response_status; + response >> response_sequence >> response_status; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::ERROR, int(response_status)); + // send request with just module name + binarystream request2; + request2 << sequence << P::LOAD << string("test.dll"); + ASSERT_TRUE(server->HandleRequest(request2, response)); + response >> response_sequence >> response_status; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::ERROR, int(response_status)); + // send request with module name, debug file, missing debug id + binarystream request3; + request3 << sequence << P::LOAD << string("test.dll") << string("test.pdb"); + ASSERT_TRUE(server->HandleRequest(request3, response)); + response >> response_sequence >> response_status; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::ERROR, int(response_status)); +} + +void FillFullSourceLineInfo(StackFrame *frame) { + frame->function_name = "function1"; + frame->function_base = 0x1200; + frame->source_file_name = "function1.cc"; + frame->source_line = 1; + frame->source_line_base = 0x1230; +} + +void FillPartialSourceLineInfo(StackFrame *frame) { + frame->function_name = "function2"; + frame->function_base = 0xFFF0; +} + +TEST_F(NetworkSourceLineServerTest, TestGet) { + EXPECT_CALL(resolver, FillSourceLineInfo(_)) + .WillOnce(Invoke(FillFullSourceLineInfo)) + .WillOnce(Invoke(FillPartialSourceLineInfo)); + + binarystream request; + const u_int16_t sequence = 0xA0A0; + request << sequence << P::GET << string("loaded.dll") + << string("loaded.pdb") << string("ABCD1234") + << u_int64_t(0x1000) << u_int64_t(0x1234); + binarystream response; + ASSERT_TRUE(server->HandleRequest(request, response)); + u_int16_t response_sequence; + u_int8_t response_status; + string function, source_file; + u_int32_t source_line; + u_int64_t function_base, source_line_base; + response >> response_sequence >> response_status >> function + >> function_base >> source_file >> source_line >> source_line_base; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ("function1", function); + EXPECT_EQ(0x1200, function_base); + EXPECT_EQ("function1.cc", source_file); + EXPECT_EQ(1, source_line); + EXPECT_EQ(0x1230, source_line_base); + + binarystream request2; + const u_int16_t sequence2 = 0xC0C0; + request2 << sequence2 << P::GET << string("loaded.dll") + << string("loaded.pdb") << string("ABCD1234") + << u_int64_t(0x1000) << u_int64_t(0xFFFF); + ASSERT_TRUE(server->HandleRequest(request2, response)); + response >> response_sequence >> response_status >> function + >> function_base >> source_file >> source_line >> source_line_base; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence2, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ("function2", function); + EXPECT_EQ(0xFFF0, function_base); + EXPECT_EQ("", source_file); + EXPECT_EQ(0, source_line); + EXPECT_EQ(0, source_line_base); +} + +WindowsFrameInfo* GetFullWindowsFrameInfo(const StackFrame *frame) { + // return frame info with program string + return new WindowsFrameInfo(1, 2, 3, 0xA, 0xFF, 0xF00, + true, + "x y ="); +} + +WindowsFrameInfo* GetPartialWindowsFrameInfo(const StackFrame *frame) { + // return frame info, no program string + return new WindowsFrameInfo(1, 2, 3, 4, 5, 6, true, ""); +} + +TEST_F(NetworkSourceLineServerTest, TestGetStackWin) { + EXPECT_CALL(resolver, FindWindowsFrameInfo(_)) + .WillOnce(Invoke(GetFullWindowsFrameInfo)) + .WillOnce(Invoke(GetPartialWindowsFrameInfo)) + .WillOnce(Return((WindowsFrameInfo*)NULL)); + + binarystream request; + const u_int16_t sequence = 0xA0A0; + request << sequence << P::GETSTACKWIN << string("loaded.dll") + << string("loaded.pdb") << string("ABCD1234") + << u_int64_t(0x1000) << u_int64_t(0x1234); + binarystream response; + ASSERT_TRUE(server->HandleRequest(request, response)); + u_int16_t response_sequence; + u_int8_t response_status; + string stack_info; + response >> response_sequence >> response_status + >> stack_info; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ("0 0 0 1 2 3 a ff f00 1 x y =", stack_info); + + binarystream request2; + const u_int16_t sequence2 = 0xB0B0; + request2 << sequence2 << P::GETSTACKWIN << string("loaded.dll") + << string("loaded.pdb") << string("ABCD1234") + << u_int64_t(0x1000) << u_int64_t(0xABCD); + ASSERT_TRUE(server->HandleRequest(request2, response)); + response >> response_sequence >> response_status + >> stack_info; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence2, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ("0 0 0 1 2 3 4 5 6 0 1", stack_info); + + binarystream request3; + const u_int16_t sequence3 = 0xC0C0; + request3 << sequence3 << P::GETSTACKWIN << string("loaded.dll") + << string("loaded.pdb") << string("ABCD1234") + << u_int64_t(0x1000) << u_int64_t(0xFFFF); + ASSERT_TRUE(server->HandleRequest(request3, response)); + response >> response_sequence >> response_status + >> stack_info; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence3, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ("", stack_info); +} + + +CFIFrameInfo* GetCFIFrameInfoJustCFA(const StackFrame *frame) { + CFIFrameInfo* cfi = new CFIFrameInfo(); + cfi->SetCFARule("12345678"); + return cfi; +} + +CFIFrameInfo* GetCFIFrameInfoCFARA(const StackFrame *frame) { + CFIFrameInfo* cfi = new CFIFrameInfo(); + cfi->SetCFARule("12345678"); + cfi->SetRARule("abcdefgh"); + return cfi; +} + +CFIFrameInfo* GetCFIFrameInfoLots(const StackFrame *frame) { + CFIFrameInfo* cfi = new CFIFrameInfo(); + cfi->SetCFARule("12345678"); + cfi->SetRARule("abcdefgh"); + cfi->SetRegisterRule("r0", "foo bar"); + cfi->SetRegisterRule("b0", "123 abc +"); + return cfi; +} + +TEST_F(NetworkSourceLineServerTest, TestGetStackCFI) { + EXPECT_CALL(resolver, FindCFIFrameInfo(_)) + .WillOnce(Return((CFIFrameInfo*)NULL)) + .WillOnce(Invoke(GetCFIFrameInfoJustCFA)) + .WillOnce(Invoke(GetCFIFrameInfoCFARA)) + .WillOnce(Invoke(GetCFIFrameInfoLots)); + + binarystream request; + const u_int16_t sequence = 0xA0A0; + request << sequence << P::GETSTACKCFI << string("loaded.dll") + << string("loaded.pdb") << string("ABCD1234") + << u_int64_t(0x1000) << u_int64_t(0x1234); + binarystream response; + ASSERT_TRUE(server->HandleRequest(request, response)); + u_int16_t response_sequence; + u_int8_t response_status; + string stack_info; + response >> response_sequence >> response_status + >> stack_info; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ("", stack_info); + + binarystream request2; + const u_int16_t sequence2 = 0xB0B0; + request2 << sequence2 << P::GETSTACKCFI << string("loaded.dll") + << string("loaded.pdb") << string("ABCD1234") + << u_int64_t(0x1000) << u_int64_t(0xABCD); + ASSERT_TRUE(server->HandleRequest(request2, response)); + response >> response_sequence >> response_status + >> stack_info; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence2, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ(".cfa: 12345678", stack_info); + + binarystream request3; + const u_int16_t sequence3 = 0xC0C0; + request3 << sequence3 << P::GETSTACKCFI << string("loaded.dll") + << string("loaded.pdb") << string("ABCD1234") + << u_int64_t(0x1000) << u_int64_t(0xFFFF); + ASSERT_TRUE(server->HandleRequest(request3, response)); + response >> response_sequence >> response_status + >> stack_info; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence3, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ(".cfa: 12345678 .ra: abcdefgh", stack_info); + + binarystream request4; + const u_int16_t sequence4 = 0xD0D0; + request4 << sequence4 << P::GETSTACKCFI << string("loaded.dll") + << string("loaded.pdb") << string("ABCD1234") + << u_int64_t(0x1000) << u_int64_t(0xFFFF); + ASSERT_TRUE(server->HandleRequest(request4, response)); + response >> response_sequence >> response_status + >> stack_info; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence4, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ(".cfa: 12345678 .ra: abcdefgh b0: 123 abc + r0: foo bar", + stack_info); +} + +TEST_F(NetworkSourceLineServerTest, TestMalformedGetRequest) { + //TODO +} + +TEST(TestMissingMembers, TestServerWithoutSymbolSupplier) { + // Should provide reasonable responses without a SymbolSupplier + MockSourceLineResolver resolver; + MockNetwork net; + TestNetworkSourceLineServer server(NULL, &resolver, &net); + + // All LOAD requests should return LOAD_NOT_FOUND + binarystream request; + binarystream response; + const u_int16_t sequence = 0xB0B0; + u_int16_t response_sequence; + u_int8_t response_status, response_data; + request << sequence << P::LOAD << string("found.dll") << string("found.pdb") + << string("ABCD1234"); + ASSERT_TRUE(server.HandleRequest(request, response)); + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_NOT_FOUND), int(response_data)); +} + +TEST(TestMissingMembers, TestServerWithoutResolver) { + // Should provide reasonable responses without a SourceLineResolver + MockSymbolSupplier supplier; + MockNetwork net; + TestNetworkSourceLineServer server(&supplier, NULL, &net); + + // GET requests should return empty info + binarystream request; + binarystream response; + const u_int16_t sequence = 0xA0A0; + u_int16_t response_sequence; + u_int8_t response_status; + request << sequence << P::GET << string("loaded.dll") + << string("loaded.pdb") << string("ABCD1234") + << u_int64_t(0x1000) << u_int64_t(0x1234); + ASSERT_TRUE(server.HandleRequest(request, response)); + string function, source_file; + u_int32_t source_line; + u_int64_t function_base, source_line_base; + response >> response_sequence >> response_status >> function + >> function_base >> source_file >> source_line >> source_line_base; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ("", function); + EXPECT_EQ(0x0, function_base); + EXPECT_EQ("", source_file); + EXPECT_EQ(0, source_line); + EXPECT_EQ(0x0, source_line_base); + + // GETSTACKWIN requests should return an empty string + binarystream request2; + const u_int16_t sequence2 = 0xB0B0; + request << sequence2 << P::GETSTACKWIN << string("loaded.dll") + << string("loaded.pdb") << string("ABCD1234") + << u_int64_t(0x1000) << u_int64_t(0x1234); + ASSERT_TRUE(server.HandleRequest(request, response)); + string response_string; + response >> response_sequence >> response_status >> response_string; + EXPECT_EQ(sequence2, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + EXPECT_EQ("", response_string); +} + +class TestModuleManagement : public ::testing::Test { +public: + MockSymbolSupplier supplier; + MockSourceLineResolver resolver; + MockNetwork net; + TestNetworkSourceLineServer server; + + // Init server with symbol line limit of 25 + TestModuleManagement() : server(&supplier, &resolver, &net, 25) {} +}; + +TEST_F(TestModuleManagement, TestModuleUnloading) { + EXPECT_CALL(supplier, GetSymbolFile(_,_,_,_)) + .Times(3) + .WillRepeatedly(DoAll(SetArgumentPointee<3>(string("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n")), + Return(SymbolSupplier::FOUND))); + EXPECT_CALL(resolver, HasModule(_)) + .Times(3) + .WillRepeatedly(Return(false)); + EXPECT_CALL(resolver, LoadModuleUsingMapBuffer(_,_)) + .Times(3) + .WillRepeatedly(Return(true)); + EXPECT_CALL(resolver, UnloadModule(Property(&CodeModule::code_file, + string("one.dll|one.pdb|1111")))) + .Times(1); + + // load three modules, each with 10 lines of symbols. + // the third module will overflow the server's symbol line limit, + // and should cause the first module to be unloaded. + binarystream request; + const u_int16_t sequence = 0x1010; + request << sequence << P::LOAD << string("one.dll") << string("one.pdb") + << string("1111"); + binarystream response; + ASSERT_TRUE(server.HandleRequest(request, response)); + u_int16_t response_sequence; + u_int8_t response_status, response_data; + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_OK), int(response_data)); + + binarystream request2; + const u_int16_t sequence2 = 0x2020; + request2 << sequence2 << P::LOAD << string("two.dll") << string("two.pdb") + << string("2222"); + ASSERT_TRUE(server.HandleRequest(request2, response)); + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence2, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_OK), int(response_data)); + + binarystream request3; + const u_int16_t sequence3 = 0x3030; + request3 << sequence3 << P::LOAD << string("three.dll") << string("three.pdb") + << string("3333"); + ASSERT_TRUE(server.HandleRequest(request3, response)); + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence3, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_OK), int(response_data)); +} + +TEST_F(TestModuleManagement, TestSymbolLimitTooLow) { + // load module with symbol count > limit, + // ensure that it doesn't get unloaded even though it's the only module + EXPECT_CALL(supplier, GetSymbolFile(_,_,_,_)) + .WillOnce(DoAll(SetArgumentPointee<3>(string("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n")), + Return(SymbolSupplier::FOUND))); + EXPECT_CALL(resolver, HasModule(_)) + .WillOnce(Return(false)); + EXPECT_CALL(resolver, LoadModuleUsingMapBuffer(_,_)) + .WillOnce(Return(true)); + EXPECT_CALL(resolver, UnloadModule(_)) + .Times(0); + + binarystream request; + const u_int16_t sequence = 0x1010; + request << sequence << P::LOAD << string("one.dll") << string("one.pdb") + << string("1111"); + binarystream response; + ASSERT_TRUE(server.HandleRequest(request, response)); + u_int16_t response_sequence; + u_int8_t response_status, response_data; + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_OK), int(response_data)); +} + +TEST_F(TestModuleManagement, TestModuleLoadLRU) { + // load 2 modules, then re-load the first one, + // then load a third one, causing the second one to be unloaded + EXPECT_CALL(supplier, GetSymbolFile(_,_,_,_)) + .Times(3) + .WillRepeatedly(DoAll(SetArgumentPointee<3>(string("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n")), + Return(SymbolSupplier::FOUND))); + EXPECT_CALL(resolver, HasModule(_)) + .WillOnce(Return(false)) // load module 1 + .WillOnce(Return(false)) // load module 2 + .WillOnce(Return(true)) // module 1 already loaded + .WillOnce(Return(false)); // load module 3 + EXPECT_CALL(resolver, LoadModuleUsingMapBuffer(_,_)) + .Times(3) + .WillRepeatedly(Return(true)); + EXPECT_CALL(resolver, UnloadModule(Property(&CodeModule::code_file, + string("two.dll|two.pdb|2222")))) + .Times(1); + + binarystream request; + const u_int16_t sequence = 0x1010; + request << sequence << P::LOAD << string("one.dll") << string("one.pdb") + << string("1111"); + binarystream response; + ASSERT_TRUE(server.HandleRequest(request, response)); + u_int16_t response_sequence; + u_int8_t response_status, response_data; + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_OK), int(response_data)); + + binarystream request2; + const u_int16_t sequence2 = 0x2020; + request2 << sequence2 << P::LOAD << string("two.dll") << string("two.pdb") + << string("2222"); + ASSERT_TRUE(server.HandleRequest(request2, response)); + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence2, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_OK), int(response_data)); + + binarystream request3; + const u_int16_t sequence3 = 0x3030; + request3 << sequence3 << P::LOAD << string("one.dll") << string("one.pdb") + << string("1111"); + ASSERT_TRUE(server.HandleRequest(request3, response)); + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence3, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_OK), int(response_data)); + + binarystream request4; + const u_int16_t sequence4 = 0x4040; + request4 << sequence4 << P::LOAD << string("three.dll") << string("three.pdb") + << string("3333"); + ASSERT_TRUE(server.HandleRequest(request4, response)); + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence4, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_OK), int(response_data)); +} + +TEST_F(TestModuleManagement, TestModuleGetLRU) { + // load 2 modules, then issue a GET for the first one, + // then load a third one, causing the second one to be unloaded + EXPECT_CALL(supplier, GetSymbolFile(_,_,_,_)) + .Times(3) + .WillRepeatedly(DoAll(SetArgumentPointee<3>(string("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n")), + Return(SymbolSupplier::FOUND))); + EXPECT_CALL(resolver, HasModule(_)) + .Times(3) + .WillRepeatedly(Return(false)); + EXPECT_CALL(resolver, LoadModuleUsingMapBuffer(_,_)) + .Times(3) + .WillRepeatedly(Return(true)); + EXPECT_CALL(resolver, FillSourceLineInfo(_)) + .Times(1); + EXPECT_CALL(resolver, UnloadModule(Property(&CodeModule::code_file, + string("two.dll|two.pdb|2222")))) + .Times(1); + + binarystream request; + const u_int16_t sequence = 0x1010; + request << sequence << P::LOAD << string("one.dll") << string("one.pdb") + << string("1111"); + binarystream response; + ASSERT_TRUE(server.HandleRequest(request, response)); + u_int16_t response_sequence; + u_int8_t response_status, response_data; + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_OK), int(response_data)); + + binarystream request2; + const u_int16_t sequence2 = 0x2020; + request2 << sequence2 << P::LOAD << string("two.dll") << string("two.pdb") + << string("2222"); + ASSERT_TRUE(server.HandleRequest(request2, response)); + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence2, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_OK), int(response_data)); + + binarystream request3; + const u_int16_t sequence3 = 0x3030; + request3 << sequence3 << P::GET << string("one.dll") + << string("one.pdb") << string("1111") + << u_int64_t(0x1000) << u_int64_t(0x1234); + ASSERT_TRUE(server.HandleRequest(request3, response)); + string function, source_file; + u_int32_t source_line; + u_int64_t function_base, source_line_base; + response >> response_sequence >> response_status >> function + >> function_base >> source_file >> source_line >> source_line_base; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence3, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + // Don't care about the rest of the response, really. + + binarystream request4; + const u_int16_t sequence4 = 0x4040; + request4 << sequence4 << P::LOAD << string("three.dll") << string("three.pdb") + << string("3333"); + ASSERT_TRUE(server.HandleRequest(request4, response)); + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence4, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_OK), int(response_data)); +} + +TEST_F(TestModuleManagement, TestModuleGetStackWinLRU) { + // load 2 modules, then issue a GETSTACKWIN for the first one, + // then load a third one, causing the second one to be unloaded + EXPECT_CALL(supplier, GetSymbolFile(_,_,_,_)) + .Times(3) + .WillRepeatedly(DoAll(SetArgumentPointee<3>(string("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n")), + Return(SymbolSupplier::FOUND))); + EXPECT_CALL(resolver, HasModule(_)) + .Times(3) + .WillRepeatedly(Return(false)); + EXPECT_CALL(resolver, LoadModuleUsingMapBuffer(_,_)) + .Times(3) + .WillRepeatedly(Return(true)); + EXPECT_CALL(resolver, FindWindowsFrameInfo(_)) + .WillOnce(Return((WindowsFrameInfo*)NULL)); + EXPECT_CALL(resolver, UnloadModule(Property(&CodeModule::code_file, + string("two.dll|two.pdb|2222")))) + .Times(1); + + binarystream request; + const u_int16_t sequence = 0x1010; + request << sequence << P::LOAD << string("one.dll") << string("one.pdb") + << string("1111"); + binarystream response; + ASSERT_TRUE(server.HandleRequest(request, response)); + u_int16_t response_sequence; + u_int8_t response_status, response_data; + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_OK), int(response_data)); + + binarystream request2; + const u_int16_t sequence2 = 0x2020; + request2 << sequence2 << P::LOAD << string("two.dll") << string("two.pdb") + << string("2222"); + ASSERT_TRUE(server.HandleRequest(request2, response)); + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence2, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_OK), int(response_data)); + + binarystream request3; + const u_int16_t sequence3 = 0x3030; + request3 << sequence3 << P::GETSTACKWIN << string("one.dll") + << string("one.pdb") << string("1111") + << u_int64_t(0x1000) << u_int64_t(0x1234); + ASSERT_TRUE(server.HandleRequest(request3, response)); + string stack_info; + response >> response_sequence >> response_status + >> stack_info; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence3, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + // Don't care about the rest of the response, really. + + binarystream request4; + const u_int16_t sequence4 = 0x4040; + request4 << sequence4 << P::LOAD << string("three.dll") << string("three.pdb") + << string("3333"); + ASSERT_TRUE(server.HandleRequest(request4, response)); + response >> response_sequence >> response_status >> response_data; + ASSERT_FALSE(response.eof()); + EXPECT_EQ(sequence4, response_sequence); + EXPECT_EQ(P::OK, int(response_status)); + ASSERT_EQ(int(P::LOAD_OK), int(response_data)); +} + +} // namespace + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} |