blob: 05f1ccc26188aaebd10a90b894bf5c2460e592f7 [file] [log] [blame]
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "syzygy/playback/playback.h"
#include <string>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "syzygy/common/syzygy_version.h"
#include "syzygy/core/unittest_util.h"
#include "syzygy/pdb/omap.h"
#include "syzygy/pe/pe_file.h"
#include "syzygy/pe/unittest_util.h"
#include "syzygy/trace/parse/parse_engine.h"
#include "syzygy/trace/parse/unittest_util.h"
namespace playback {
namespace {
using testing::_;
using testing::GetSrcRelativePath;
using testing::GetExeRelativePath;
using testing::GetExeTestDataRelativePath;
using testing::MockParseEventHandler;
using testing::Return;
// A test parse engine that exposes some internals so we can simulate some
// events.
class TestParseEngine : public trace::parser::ParseEngine {
public:
using trace::parser::ParseEngine::AddModuleInformation;
using trace::parser::ParseEngine::RemoveModuleInformation;
};
// A test parser that exposes the parse engine, so we can feed it simulated
// events.
class TestParser : public trace::parser::Parser {
public:
TestParseEngine* active_parse_engine() const {
return reinterpret_cast<TestParseEngine*>(active_parse_engine_);
}
};
class PlaybackTest : public testing::PELibUnitTest {
public:
PlaybackTest() : image_layout_(&block_graph_) {
}
void SetUp() {
module_path_ =
GetExeTestDataRelativePath(testing::kTestDllName);
instrumented_path_ =
GetExeTestDataRelativePath(testing::kCallTraceInstrumentedTestDllName);
const base::FilePath kTraceFiles[] = {
GetExeTestDataRelativePath(testing::kCallTraceTraceFiles[0]),
GetExeTestDataRelativePath(testing::kCallTraceTraceFiles[1]),
GetExeTestDataRelativePath(testing::kCallTraceTraceFiles[2]),
GetExeTestDataRelativePath(testing::kCallTraceTraceFiles[3]),
};
trace_files_ = Playback::TraceFileList(kTraceFiles,
kTraceFiles + arraysize(kTraceFiles));
}
bool Init() {
playback_.reset(
new Playback(module_path_, instrumented_path_, trace_files_));
parse_event_handler_.reset(new MockParseEventHandler);
parser_.reset(new TestParser);
return parser_->Init(parse_event_handler_.get());
}
scoped_ptr<Playback> playback_;
base::FilePath module_path_;
base::FilePath instrumented_path_;
Playback::TraceFileList trace_files_;
pe::PEFile input_dll_;
block_graph::BlockGraph block_graph_;
pe::ImageLayout image_layout_;
scoped_ptr<MockParseEventHandler> parse_event_handler_;
scoped_ptr<TestParser> parser_;
};
} // namespace
TEST_F(PlaybackTest, MismatchedDLLTest) {
module_path_ = GetExeTestDataRelativePath(L"randomized_test_dll.dll");
EXPECT_TRUE(Init());
EXPECT_FALSE(playback_->Init(&input_dll_, &image_layout_, parser_.get()));
}
TEST_F(PlaybackTest, BadTraceFile) {
const base::FilePath kTraceFile = GetSrcRelativePath(
L"syzygy/playback/test_data/bad-trace.bin");
trace_files_ = Playback::TraceFileList(&kTraceFile, &kTraceFile + 1);
EXPECT_TRUE(Init());
EXPECT_FALSE(playback_->Init(&input_dll_, &image_layout_, parser_.get()));
}
TEST_F(PlaybackTest, SuccessfulInit) {
EXPECT_TRUE(Init());
EXPECT_TRUE(playback_->Init(&input_dll_, &image_layout_, parser_.get()));
}
TEST_F(PlaybackTest, ConsumeCallTraceEvents) {
EXPECT_TRUE(Init());
EXPECT_TRUE(playback_->Init(&input_dll_, &image_layout_, parser_.get()));
#ifdef OFFICIAL_BUILD
static const size_t kProcessAttachCount = 4;
static const size_t kBatchFunctionEntryCount = 4;
#else
static const size_t kProcessAttachCount = 12;
static const size_t kBatchFunctionEntryCount = 12;
#endif
EXPECT_CALL(*parse_event_handler_, OnProcessStarted(_, _, _)).Times(4);
EXPECT_CALL(*parse_event_handler_, OnProcessEnded(_, _)).Times(4);
EXPECT_CALL(*parse_event_handler_, OnFunctionEntry(_, _, _, _)).Times(0);
EXPECT_CALL(*parse_event_handler_, OnFunctionExit(_, _, _, _)).Times(0);
EXPECT_CALL(*parse_event_handler_, OnBatchFunctionEntry(_, _, _, _)).
Times(kBatchFunctionEntryCount);
EXPECT_CALL(*parse_event_handler_, OnProcessAttach(_, _, _, _)).
Times(kProcessAttachCount);
EXPECT_CALL(*parse_event_handler_, OnThreadAttach(_, _, _, _)).Times(0);
EXPECT_CALL(*parse_event_handler_, OnThreadDetach(_, _, _, _)).Times(0);
EXPECT_CALL(*parse_event_handler_,
OnInvocationBatch(_, _, _, _, _)).Times(0);
EXPECT_TRUE(parser_->Consume());
}
TEST_F(PlaybackTest, FindFunctionBlock) {
EXPECT_TRUE(Init());
EXPECT_TRUE(playback_->Init(&input_dll_, &image_layout_, parser_.get()));
// Get the instrumented module's signature. We need this so we can inject
// modules into the parse engine.
pe::PEFile pe_file;
trace::parser::ModuleInformation module_info;
ASSERT_TRUE(pe_file.Init(instrumented_path_));
pe_file.GetSignature(&module_info);
const DWORD kPid = 0x1234;
// Get pointers to text and data.
const IMAGE_SECTION_HEADER* text = input_dll_.GetSectionHeader(".text");
const IMAGE_SECTION_HEADER* data = input_dll_.GetSectionHeader(".data");
ASSERT_TRUE(text != NULL);
ASSERT_TRUE(data != NULL);
FuncAddr text_addr = reinterpret_cast<FuncAddr>(
module_info.base_address.value() + text->VirtualAddress);
FuncAddr data_addr = reinterpret_cast<FuncAddr>(
module_info.base_address.value() + data->VirtualAddress);
trace::parser::ModuleInformation other_module_info;
other_module_info.base_address.set_value(0x3F000000);
other_module_info.module_size = 0x00010000;
other_module_info.module_checksum = 0xF000BA55;
other_module_info.module_time_date_stamp = 0xDEADBEEF;
other_module_info.path = L"other_module.dll";
FuncAddr other_text_addr = reinterpret_cast<FuncAddr>(
other_module_info.base_address.value() + 0x1000);
ASSERT_TRUE(parser_->active_parse_engine()->AddModuleInformation(
kPid, module_info));
// We should be able to find text.
bool error = false;
EXPECT_TRUE(playback_->FindFunctionBlock(kPid, text_addr, &error) != NULL);
EXPECT_FALSE(error);
// We should get an error looking up data.
error = false;
EXPECT_TRUE(playback_->FindFunctionBlock(kPid, data_addr, &error) == NULL);
EXPECT_TRUE(error);
// We should get an error looking up an address outside of the module.
error = false;
EXPECT_TRUE(
playback_->FindFunctionBlock(kPid, other_text_addr, &error) == NULL);
EXPECT_TRUE(error);
// Now add the dummy module. Another lookup should succeed but return NULL.
ASSERT_TRUE(parser_->active_parse_engine()->AddModuleInformation(
kPid, other_module_info));
error = false;
EXPECT_TRUE(
playback_->FindFunctionBlock(kPid, other_text_addr, &error) == NULL);
EXPECT_FALSE(error);
}
} // namespace playback