aboutsummaryrefslogtreecommitdiff
path: root/src/processor/network_source_line_resolver_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/processor/network_source_line_resolver_unittest.cc')
-rw-r--r--src/processor/network_source_line_resolver_unittest.cc535
1 files changed, 535 insertions, 0 deletions
diff --git a/src/processor/network_source_line_resolver_unittest.cc b/src/processor/network_source_line_resolver_unittest.cc
new file mode 100644
index 00000000..bf0aae78
--- /dev/null
+++ b/src/processor/network_source_line_resolver_unittest.cc
@@ -0,0 +1,535 @@
+// 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 NetworkSourceLineResolver.
+
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "google_breakpad/processor/network_source_line_resolver.h"
+#include "google_breakpad/processor/stack_frame.h"
+#include "google_breakpad/processor/symbol_supplier.h"
+#include "processor/basic_code_module.h"
+#include "processor/binarystream.h"
+#include "processor/cfi_frame_info.h"
+#include "processor/network_interface.h"
+#include "processor/network_source_line_protocol.h"
+#include "processor/windows_frame_info.h"
+
+namespace google_breakpad {
+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));
+};
+}
+
+namespace {
+using std::string;
+using google_breakpad::binarystream;
+using google_breakpad::BasicCodeModule;
+using google_breakpad::CFIFrameInfo;
+using google_breakpad::MockNetwork;
+using google_breakpad::NetworkSourceLineResolver;
+using google_breakpad::StackFrame;
+using google_breakpad::SymbolSupplier;
+using google_breakpad::WindowsFrameInfo;
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+using namespace google_breakpad::source_line_protocol;
+
+class TestNetworkSourceLineResolver : public ::testing::Test {
+public:
+ TestNetworkSourceLineResolver() : resolver(NULL) {}
+
+ void SetUp() {
+ EXPECT_CALL(net, Init(false)).WillOnce(Return(true));
+ resolver = new NetworkSourceLineResolver(&net, 0);
+ }
+
+ NetworkSourceLineResolver *resolver;
+ MockNetwork net;
+};
+
+bool GeneratePositiveHasResponse(char *buffer, size_t buffer_size,
+ ssize_t &received) {
+ binarystream stream;
+ stream << u_int16_t(0) << OK << MODULE_LOADED;
+ string s = stream.str();
+ received = s.length();
+ memcpy(buffer, s.c_str(), s.length());
+ return true;
+}
+
+bool GenerateNegativeHasResponse(char *buffer, size_t buffer_size,
+ ssize_t &received) {
+ binarystream stream;
+ stream << u_int16_t(1) << OK << MODULE_NOT_LOADED;
+ string s = stream.str();
+ received = s.length();
+ memcpy(buffer, s.c_str(), s.length());
+ return true;
+}
+
+TEST_F(TestNetworkSourceLineResolver, TestHasMessages) {
+ EXPECT_CALL(net, Send(_,_))
+ .WillOnce(Return(true))
+ .WillOnce(Return(true));
+ EXPECT_CALL(net, WaitToReceive(0))
+ .WillOnce(Return(true))
+ .WillOnce(Return(true));
+ EXPECT_CALL(net, Receive(_,_,_))
+ .WillOnce(Invoke(GeneratePositiveHasResponse))
+ .WillOnce(Invoke(GenerateNegativeHasResponse));
+ ASSERT_NE(resolver, (NetworkSourceLineResolver*)NULL);
+ BasicCodeModule module(0x0, 0x0, "test.dll", "test.pdb", "ABCD", "", "");
+ EXPECT_TRUE(resolver->HasModule(&module));
+ BasicCodeModule module2(0x0, 0x0, "test2.dll", "test2.pdb", "FFFF", "", "");
+ EXPECT_FALSE(resolver->HasModule(&module2));
+ // calling again should hit the cache, and not the network
+ EXPECT_TRUE(resolver->HasModule(&module));
+}
+
+bool GenerateErrorResponse(char *buffer, size_t buffer_size,
+ ssize_t &received) {
+ binarystream stream;
+ stream << u_int16_t(0) << ERROR;
+ string s = stream.str();
+ received = s.length();
+ memcpy(buffer, s.c_str(), s.length());
+ return true;
+}
+
+TEST_F(TestNetworkSourceLineResolver, TestHasErrorResponse) {
+ EXPECT_CALL(net, Send(_,_))
+ .WillOnce(Return(true));
+ EXPECT_CALL(net, WaitToReceive(0))
+ .WillOnce(Return(true));
+ EXPECT_CALL(net, Receive(_,_,_))
+ .WillOnce(Invoke(GenerateErrorResponse));
+ ASSERT_NE(resolver, (NetworkSourceLineResolver*)NULL);
+ BasicCodeModule module(0x0, 0x0, "test.dll", "test.pdb", "ABCD", "", "");
+ // error packet should function as a not found
+ EXPECT_FALSE(resolver->HasModule(&module));
+}
+
+// GenerateLoadResponse will generate (LOAD_NOT_FOUND, LOAD_INTERRUPT,
+// LOAD_FAIL, LOAD_OK) in order.
+class LoadHelper {
+ public:
+ LoadHelper() : response(LOAD_NOT_FOUND), sequence(0) {}
+ bool GenerateLoadResponse(char *buffer, size_t buffer_size,
+ ssize_t &received) {
+ binarystream stream;
+ stream << sequence << OK << response;
+ string s = stream.str();
+ received = s.length();
+ memcpy(buffer, s.c_str(), s.length());
+ ++sequence;
+ ++response;
+ return true;
+ }
+ u_int8_t response;
+ u_int16_t sequence;
+};
+
+TEST_F(TestNetworkSourceLineResolver, TestLoadMessages) {
+ EXPECT_CALL(net, Send(_,_))
+ .Times(4)
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(net, WaitToReceive(0))
+ .Times(4)
+ .WillRepeatedly(Return(true));
+ LoadHelper helper;
+ EXPECT_CALL(net, Receive(_,_,_))
+ .Times(4)
+ .WillRepeatedly(Invoke(&helper, &LoadHelper::GenerateLoadResponse));
+ ASSERT_NE(resolver, (NetworkSourceLineResolver*)NULL);
+ BasicCodeModule module(0x0, 0x0, "test.dll", "test.pdb", "ABCD", "", "");
+ string s;
+ EXPECT_EQ(SymbolSupplier::NOT_FOUND,
+ resolver->GetSymbolFile(&module, NULL, &s));
+ BasicCodeModule module2(0x0, 0x0, "test2.dll", "test2.pdb", "FFFF", "", "");
+ EXPECT_EQ(SymbolSupplier::INTERRUPT,
+ resolver->GetSymbolFile(&module2, NULL, &s));
+ BasicCodeModule module3(0x0, 0x0, "test3.dll", "test3.pdb", "0000", "", "");
+ // a FAIL result from the network should come back as NOT_FOUND
+ EXPECT_EQ(SymbolSupplier::NOT_FOUND,
+ resolver->GetSymbolFile(&module3, NULL, &s));
+ BasicCodeModule module4(0x0, 0x0, "test4.dll", "test4.pdb", "1010", "", "");
+ EXPECT_EQ(SymbolSupplier::FOUND,
+ resolver->GetSymbolFile(&module4, NULL, &s));
+ // calling this should hit the cache, and not the network
+ EXPECT_TRUE(resolver->HasModule(&module4));
+}
+
+TEST_F(TestNetworkSourceLineResolver, TestLoadErrorResponse) {
+ EXPECT_CALL(net, Send(_,_))
+ .WillOnce(Return(true));
+ EXPECT_CALL(net, WaitToReceive(0))
+ .WillOnce(Return(true));
+ EXPECT_CALL(net, Receive(_,_,_))
+ .WillOnce(Invoke(GenerateErrorResponse));
+ ASSERT_NE(resolver, (NetworkSourceLineResolver*)NULL);
+ BasicCodeModule module(0x0, 0x0, "test.dll", "test.pdb", "ABCD", "", "");
+ string s;
+ // error packet should function as NOT_FOUND response
+ EXPECT_EQ(SymbolSupplier::NOT_FOUND,
+ resolver->GetSymbolFile(&module, NULL, &s));
+}
+
+class GetHelper {
+ public:
+ GetHelper() : sequence(1) {}
+ bool GenerateGetResponse(char *buffer, size_t buffer_size,
+ ssize_t &received) {
+ binarystream stream;
+ stream << sequence << OK;
+ switch(sequence) {
+ case 1:
+ // return full info
+ stream << string("test()") << u_int64_t(0x1000) << string("test.c")
+ << u_int32_t(1) << u_int64_t(0x1010);
+ break;
+ case 2:
+ // return full info
+ stream << string("test2()") << u_int64_t(0x2000) << string("test2.c")
+ << u_int32_t(2) << u_int64_t(0x2020);
+ break;
+ case 3:
+ // return just function name
+ stream << string("test3()") << u_int64_t(0x4000) << string("")
+ << u_int32_t(0) << u_int64_t(0);
+ break;
+ }
+ string s = stream.str();
+ received = s.length();
+ memcpy(buffer, s.c_str(), s.length());
+ ++sequence;
+ return true;
+ }
+ u_int16_t sequence;
+};
+
+TEST_F(TestNetworkSourceLineResolver, TestGetMessages) {
+ EXPECT_CALL(net, Send(_,_))
+ .Times(4)
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(net, WaitToReceive(0))
+ .Times(4)
+ .WillRepeatedly(Return(true));
+ GetHelper helper;
+ EXPECT_CALL(net, Receive(_,_,_))
+ .Times(4)
+ .WillOnce(Invoke(GeneratePositiveHasResponse))
+ .WillRepeatedly(Invoke(&helper, &GetHelper::GenerateGetResponse));
+ BasicCodeModule module(0x0, 0x0, "test.dll", "test.pdb", "ABCD", "", "");
+ // The resolver has to think the module is loaded before it will respond
+ // to GET requests for that module.
+ EXPECT_TRUE(resolver->HasModule(&module));
+ StackFrame frame;
+ frame.module = &module;
+ frame.instruction = 0x1010;
+ resolver->FillSourceLineInfo(&frame);
+ EXPECT_EQ("test()", frame.function_name);
+ EXPECT_EQ(0x1000, frame.function_base);
+ EXPECT_EQ("test.c", frame.source_file_name);
+ EXPECT_EQ(1, frame.source_line);
+ EXPECT_EQ(0x1010, frame.source_line_base);
+
+ StackFrame frame2;
+ frame2.module = &module;
+ frame2.instruction = 0x2020;
+ resolver->FillSourceLineInfo(&frame2);
+ EXPECT_EQ("test2()", frame2.function_name);
+ EXPECT_EQ(0x2000, frame2.function_base);
+ EXPECT_EQ("test2.c", frame2.source_file_name);
+ EXPECT_EQ(2, frame2.source_line);
+ EXPECT_EQ(0x2020, frame2.source_line_base);
+
+ StackFrame frame3;
+ frame3.module = &module;
+ frame3.instruction = 0x4040;
+ resolver->FillSourceLineInfo(&frame3);
+ EXPECT_EQ("test3()", frame3.function_name);
+ EXPECT_EQ(0x4000, frame3.function_base);
+ EXPECT_EQ("", frame3.source_file_name);
+ EXPECT_EQ(0, frame3.source_line);
+ EXPECT_EQ(0, frame3.source_line_base);
+
+ // this should come from the cache and not hit the network
+ StackFrame frame4;
+ frame4.module = &module;
+ frame4.instruction = 0x1010;
+ resolver->FillSourceLineInfo(&frame4);
+ EXPECT_EQ("test()", frame4.function_name);
+ EXPECT_EQ(0x1000, frame4.function_base);
+ EXPECT_EQ("test.c", frame4.source_file_name);
+ EXPECT_EQ(1, frame4.source_line);
+ EXPECT_EQ(0x1010, frame4.source_line_base);
+
+ // this should also be cached
+ StackFrame frame5;
+ frame5.module = &module;
+ frame5.instruction = 0x4040;
+ resolver->FillSourceLineInfo(&frame5);
+ EXPECT_EQ("test3()", frame5.function_name);
+ EXPECT_EQ(0x4000, frame5.function_base);
+ EXPECT_EQ("", frame5.source_file_name);
+ EXPECT_EQ(0, frame5.source_line);
+ EXPECT_EQ(0, frame5.source_line_base);
+}
+
+class GetStackWinHelper {
+ public:
+ GetStackWinHelper() : sequence(1) {}
+ bool GenerateGetStackWinResponse(char *buffer, size_t buffer_size,
+ ssize_t &received) {
+ binarystream stream;
+ stream << sequence << OK;
+ switch(sequence) {
+ case 1:
+ // return full info including program string
+ stream << string("0 0 0 1 2 3 a ff f00 1 x y =");
+ break;
+ case 2:
+ // return full info, no program string
+ stream << string("0 0 0 1 2 3 a ff f00 0 1");
+ break;
+ case 3:
+ // return empty string
+ stream << string("");
+ break;
+ }
+ string s = stream.str();
+ received = s.length();
+ memcpy(buffer, s.c_str(), s.length());
+ ++sequence;
+ return true;
+ }
+ u_int16_t sequence;
+};
+
+TEST_F(TestNetworkSourceLineResolver, TestGetStackWinMessages) {
+ EXPECT_CALL(net, Send(_,_))
+ .Times(4)
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(net, WaitToReceive(0))
+ .Times(4)
+ .WillRepeatedly(Return(true));
+ GetStackWinHelper helper;
+ EXPECT_CALL(net, Receive(_,_,_))
+ .Times(4)
+ .WillOnce(Invoke(GeneratePositiveHasResponse))
+ .WillRepeatedly(Invoke(&helper,
+ &GetStackWinHelper::GenerateGetStackWinResponse));
+
+ BasicCodeModule module(0x0, 0x0, "test.dll", "test.pdb", "ABCD", "", "");
+ // The resolver has to think the module is loaded before it will respond
+ // to GETSTACKWIN requests for that module.
+ EXPECT_TRUE(resolver->HasModule(&module));
+ StackFrame frame;
+ frame.module = &module;
+ frame.instruction = 0x1010;
+ WindowsFrameInfo *info = resolver->FindWindowsFrameInfo(&frame);
+ ASSERT_NE((WindowsFrameInfo*)NULL, info);
+ EXPECT_EQ(0x1, info->prolog_size);
+ EXPECT_EQ(0x2, info->epilog_size);
+ EXPECT_EQ(0x3, info->parameter_size);
+ EXPECT_EQ(0xa, info->saved_register_size);
+ EXPECT_EQ(0xff, info->local_size);
+ EXPECT_EQ(0xf00, info->max_stack_size);
+ EXPECT_EQ("x y =", info->program_string);
+ delete info;
+
+ StackFrame frame2;
+ frame2.module = &module;
+ frame2.instruction = 0x2020;
+ info = resolver->FindWindowsFrameInfo(&frame2);
+ ASSERT_NE((WindowsFrameInfo*)NULL, info);
+ EXPECT_EQ(0x1, info->prolog_size);
+ EXPECT_EQ(0x2, info->epilog_size);
+ EXPECT_EQ(0x3, info->parameter_size);
+ EXPECT_EQ(0xa, info->saved_register_size);
+ EXPECT_EQ(0xff, info->local_size);
+ EXPECT_EQ(0xf00, info->max_stack_size);
+ EXPECT_EQ("", info->program_string);
+ EXPECT_EQ(true, info->allocates_base_pointer);
+ delete info;
+
+ StackFrame frame3;
+ frame3.module = &module;
+ frame3.instruction = 0x4040;
+ info = resolver->FindWindowsFrameInfo(&frame3);
+ EXPECT_EQ((WindowsFrameInfo*)NULL, info);
+
+ // this should come from the cache and not hit the network
+ StackFrame frame4;
+ frame4.module = &module;
+ frame4.instruction = 0x1010;
+ info = resolver->FindWindowsFrameInfo(&frame4);
+ ASSERT_NE((WindowsFrameInfo*)NULL, info);
+ EXPECT_EQ(0x1, info->prolog_size);
+ EXPECT_EQ(0x2, info->epilog_size);
+ EXPECT_EQ(0x3, info->parameter_size);
+ EXPECT_EQ(0xa, info->saved_register_size);
+ EXPECT_EQ(0xff, info->local_size);
+ EXPECT_EQ(0xf00, info->max_stack_size);
+ EXPECT_EQ("x y =", info->program_string);
+ delete info;
+
+ // this should also be cached
+ StackFrame frame5;
+ frame5.module = &module;
+ frame5.instruction = 0x4040;
+ info = resolver->FindWindowsFrameInfo(&frame5);
+ EXPECT_EQ((WindowsFrameInfo*)NULL, info);
+}
+
+class GetStackCFIHelper {
+ public:
+ GetStackCFIHelper() : sequence(1) {}
+ bool GenerateGetStackCFIResponse(char *buffer, size_t buffer_size,
+ ssize_t &received) {
+ binarystream stream;
+ stream << sequence << OK;
+ switch(sequence) {
+ case 1:
+ // return .cfa, .ra, registers
+ stream << string(".cfa: 1234 .ra: .cfa 5 + r0: abc xyz r2: 10 10");
+ break;
+ case 2:
+ // return just .cfa
+ stream << string(".cfa: xyz");
+ break;
+ case 3:
+ // return empty string
+ stream << string("");
+ break;
+ }
+ string s = stream.str();
+ received = s.length();
+ memcpy(buffer, s.c_str(), s.length());
+ ++sequence;
+ return true;
+ }
+ u_int16_t sequence;
+};
+
+TEST_F(TestNetworkSourceLineResolver, TestGetStackCFIMessages) {
+ EXPECT_CALL(net, Send(_,_))
+ .Times(4)
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(net, WaitToReceive(0))
+ .Times(4)
+ .WillRepeatedly(Return(true));
+ GetStackCFIHelper helper;
+ EXPECT_CALL(net, Receive(_,_,_))
+ .Times(4)
+ .WillOnce(Invoke(GeneratePositiveHasResponse))
+ .WillRepeatedly(Invoke(&helper,
+ &GetStackCFIHelper::GenerateGetStackCFIResponse));
+
+ BasicCodeModule module(0x0, 0x0, "test.dll", "test.pdb", "ABCD", "", "");
+ // The resolver has to think the module is loaded before it will respond
+ // to GETSTACKCFI requests for that module.
+ EXPECT_TRUE(resolver->HasModule(&module));
+ StackFrame frame;
+ frame.module = &module;
+ frame.instruction = 0x1010;
+ CFIFrameInfo *info = resolver->FindCFIFrameInfo(&frame);
+ ASSERT_NE((CFIFrameInfo*)NULL, info);
+ // Ostensibly we would check the internal data structure, but
+ // we'd have to either mock out some other classes or get internal access,
+ // so this is easier.
+ EXPECT_EQ(".cfa: 1234 .ra: .cfa 5 + r0: abc xyz r2: 10 10",
+ info->Serialize());
+ delete info;
+
+ StackFrame frame2;
+ frame2.module = &module;
+ frame2.instruction = 0x2020;
+ info = resolver->FindCFIFrameInfo(&frame2);
+ ASSERT_NE((CFIFrameInfo*)NULL, info);
+ EXPECT_EQ(".cfa: xyz", info->Serialize());
+ delete info;
+
+ StackFrame frame3;
+ frame3.module = &module;
+ frame3.instruction = 0x4040;
+ info = resolver->FindCFIFrameInfo(&frame3);
+ EXPECT_EQ((CFIFrameInfo*)NULL, info);
+
+ // this should come from the cache and not hit the network
+ StackFrame frame4;
+ frame4.module = &module;
+ frame4.instruction = 0x1010;
+ info = resolver->FindCFIFrameInfo(&frame4);
+ ASSERT_NE((CFIFrameInfo*)NULL, info);
+ EXPECT_EQ(".cfa: 1234 .ra: .cfa 5 + r0: abc xyz r2: 10 10",
+ info->Serialize());
+ delete info;
+
+ // this should also be cached
+ StackFrame frame5;
+ frame5.module = &module;
+ frame5.instruction = 0x4040;
+ info = resolver->FindCFIFrameInfo(&frame5);
+ EXPECT_EQ((CFIFrameInfo*)NULL, info);
+}
+
+TEST_F(TestNetworkSourceLineResolver, TestBogusData) {
+ EXPECT_FALSE(resolver->HasModule(NULL));
+
+ StackFrame frame;
+ frame.module = NULL;
+ frame.instruction = 0x1000;
+ resolver->FillSourceLineInfo(&frame);
+ EXPECT_EQ("", frame.function_name);
+ EXPECT_EQ(0x0, frame.function_base);
+ EXPECT_EQ("", frame.source_file_name);
+ EXPECT_EQ(0, frame.source_line);
+ EXPECT_EQ(0x0, frame.source_line_base);
+
+ EXPECT_EQ((WindowsFrameInfo*)NULL, resolver->FindWindowsFrameInfo(&frame));
+}
+
+} // namespace
+
+int main(int argc, char *argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}