1 : // Copyright 2012 Google Inc.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 : //
15 : // BasicBlockEntry trace agent unit-tests.
16 :
17 : #include "syzygy/agent/basic_block_entry/basic_block_entry.h"
18 :
19 : #include "base/file_util.h"
20 : #include "base/scoped_temp_dir.h"
21 : #include "gmock/gmock.h"
22 : #include "gtest/gtest.h"
23 : #include "syzygy/common/basic_block_frequency_data.h"
24 : #include "syzygy/trace/common/unittest_util.h"
25 : #include "syzygy/trace/parse/unittest_util.h"
26 :
27 : // There's a quick a dirty way to get the HMODULE of this module for MS linkers.
28 : // HINSTANCE == HMODULE == &__ImageBase;
29 : // See http://blogs.msdn.com/b/oldnewthing/archive/2004/10/25/247180.aspx
30 : EXTERN_C IMAGE_DOS_HEADER __ImageBase;
31 :
32 : namespace agent {
33 : namespace basic_block_entry {
34 :
35 : namespace {
36 :
37 : using ::common::BasicBlockFrequencyData;
38 : using testing::_;
39 : using testing::StrictMockParseEventHandler;
40 : using trace::parser::Parser;
41 :
42 : // This is the static basic-block frequency array that our coverage
43 : // instrumentation will point to.
44 : const wchar_t kBasicBlockEntryClientDll[] = L"basic_block_entry_client.dll";
45 :
46 : // The number of basic blocks we'll work with for these tests.
47 : const uint32 kNumBasicBlocks = 2;
48 :
49 : // The module defining this lib/executable.
50 : const HMODULE kThisModule = reinterpret_cast<HMODULE>(&__ImageBase);
51 :
52 : // A helper to match modules by base address.
53 E : MATCHER_P(ModuleAtAddress, module, "") {
54 E : return arg->module_base_addr == module;
55 E : }
56 :
57 : // A helper to match basic-block frequency results to expectations.
58 E : MATCHER_P3(FrequencyDataMatches, module, bb_count, bb_freqs, "") {
59 E : if (arg->module_base_addr != module)
60 i : return false;
61 :
62 E : if (arg->frequency_size != sizeof(uint32))
63 i : return false;
64 :
65 E : if (arg->num_basic_blocks != bb_count)
66 i : return false;
67 :
68 E : return ::memcmp(bb_freqs, arg->frequency_data, bb_count) == 0;
69 E : }
70 :
71 : // The test fixture for the basic-block entry agent.
72 : class BasicBlockEntryTest : public testing::Test {
73 : public:
74 E : BasicBlockEntryTest()
75 : : agent_module_(NULL) {
76 E : module_data_.agent_id = ::common::kBasicBlockEntryAgentId;
77 E : module_data_.version = ::common::kBasicBlockFrequencyDataVersion;
78 E : module_data_.tls_index = TLS_OUT_OF_INDEXES;
79 E : module_data_.initialization_attempted = 0U;
80 E : module_data_.num_basic_blocks = kNumBasicBlocks;
81 E : module_data_.frequency_data = default_frequency_data_;
82 E : ::memset(&default_frequency_data_, 0, sizeof(default_frequency_data_));
83 E : }
84 :
85 E : virtual void SetUp() OVERRIDE {
86 E : testing::Test::SetUp();
87 E : ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
88 E : service_.SetEnvironment();
89 E : }
90 :
91 E : virtual void TearDown() OVERRIDE {
92 E : UnloadDll();
93 E : service_.Stop();
94 E : }
95 :
96 E : void StartService() {
97 E : service_.Start(temp_dir_.path());
98 E : }
99 :
100 E : void StopService() {
101 E : service_.Stop();
102 E : }
103 :
104 E : void ReplayLogs(size_t files_expected) {
105 : // Stop the service if it's running.
106 E : ASSERT_NO_FATAL_FAILURE(StopService());
107 :
108 E : Parser parser;
109 E : ASSERT_TRUE(parser.Init(&handler_));
110 :
111 : // Queue up the trace file(s) we engendered.
112 : file_util::FileEnumerator enumerator(temp_dir_.path(),
113 : false,
114 E : file_util::FileEnumerator::FILES);
115 E : size_t num_files = 0;
116 E : while (true) {
117 E : FilePath trace_file = enumerator.Next();
118 E : if (trace_file.empty())
119 E : break;
120 E : ASSERT_TRUE(parser.OpenTraceFile(trace_file));
121 E : ++num_files;
122 E : }
123 :
124 E : EXPECT_EQ(files_expected, num_files);
125 :
126 E : if (num_files > 0)
127 E : ASSERT_TRUE(parser.Consume());
128 E : }
129 :
130 E : void LoadDll() {
131 E : ASSERT_EQ(NULL, agent_module_);
132 E : ASSERT_EQ(NULL, basic_block_enter_stub_);
133 E : ASSERT_EQ(NULL, ::GetModuleHandle(kBasicBlockEntryClientDll));
134 :
135 E : agent_module_ = ::LoadLibrary(kBasicBlockEntryClientDll);
136 E : ASSERT_TRUE(agent_module_ != NULL);
137 :
138 : basic_block_enter_stub_ =
139 E : ::GetProcAddress(agent_module_, "_basic_block_enter");
140 E : ASSERT_TRUE(basic_block_enter_stub_ != NULL);
141 :
142 : indirect_penter_dllmain_stub_ =
143 E : ::GetProcAddress(agent_module_, "_indirect_penter_dllmain");
144 E : ASSERT_TRUE(indirect_penter_dllmain_stub_ != NULL);
145 E : }
146 :
147 E : void UnloadDll() {
148 E : if (agent_module_ != NULL) {
149 E : ASSERT_TRUE(::FreeLibrary(agent_module_));
150 E : agent_module_ = NULL;
151 E : basic_block_enter_stub_ = NULL;
152 E : indirect_penter_dllmain_stub_ = NULL;
153 : }
154 E : }
155 :
156 : protected:
157 : static BOOL WINAPI DllMain(HMODULE module, DWORD reason, LPVOID reserved);
158 : static BOOL WINAPI DllMainThunk(
159 : HMODULE module, DWORD reason, LPVOID reserved);
160 :
161 E : void SimulateModuleEvent(DWORD reason) {
162 E : DllMainThunk(kThisModule, reason, NULL);
163 E : }
164 :
165 E : void SimulateBasicBlockEntry(uint32 basic_block_id) {
166 : __asm {
167 E : push basic_block_id
168 E : push offset module_data_
169 E : call basic_block_enter_stub_
170 : }
171 E : }
172 :
173 : // The directory where trace file output will be written.
174 : ScopedTempDir temp_dir_;
175 :
176 : // The handler to which the trace file parser will delegate events.
177 : StrictMockParseEventHandler handler_;
178 :
179 : // Our call trace service process instance.
180 : testing::CallTraceService service_;
181 :
182 : // The basic-block entry client module.
183 : HMODULE agent_module_;
184 :
185 : // This will be a stand-in for the (usually statically allocated) trace
186 : // data which would have been referenced by the instrumentation.
187 : static BasicBlockFrequencyData module_data_;
188 :
189 : // This will be a stand-in for the (usually statically allocated) fall-back
190 : // frequency to which module_data_.frequency_data will point.
191 : static uint32 default_frequency_data_[kNumBasicBlocks];
192 :
193 : // The basic-block entry entrance hook.
194 : static FARPROC basic_block_enter_stub_;
195 :
196 : // The DllMain entry hook.
197 : static FARPROC indirect_penter_dllmain_stub_;
198 : };
199 :
200 : BOOL WINAPI BasicBlockEntryTest::DllMain(
201 E : HMODULE module, DWORD reason, LPVOID reserved) {
202 E : return TRUE;
203 E : }
204 :
205 : BOOL __declspec(naked) WINAPI BasicBlockEntryTest::DllMainThunk(
206 E : HMODULE module, DWORD reason, LPVOID reserved) {
207 : __asm {
208 E : push offset module_data_
209 E : push DllMain
210 E : jmp indirect_penter_dllmain_stub_
211 : }
212 : }
213 :
214 : BasicBlockFrequencyData BasicBlockEntryTest::module_data_ = {};
215 : uint32 BasicBlockEntryTest::default_frequency_data_[] = {};
216 : FARPROC BasicBlockEntryTest::basic_block_enter_stub_ = NULL;
217 : FARPROC BasicBlockEntryTest::indirect_penter_dllmain_stub_ = NULL;
218 :
219 : } // namespace
220 :
221 E : TEST_F(BasicBlockEntryTest, NoServerNoCrash) {
222 : // Load the agent dll.
223 E : ASSERT_NO_FATAL_FAILURE(LoadDll());
224 :
225 : // Simulate the process attach event.
226 E : SimulateModuleEvent(DLL_PROCESS_ATTACH);
227 :
228 : // Validate that it only modified the tls_index and initialization_attempted
229 : // values.
230 E : ASSERT_EQ(::common::kBasicBlockEntryAgentId, module_data_.agent_id);
231 E : ASSERT_EQ(::common::kBasicBlockFrequencyDataVersion, module_data_.version);
232 E : ASSERT_NE(TLS_OUT_OF_INDEXES, module_data_.tls_index);
233 E : ASSERT_NE(0U, module_data_.initialization_attempted);
234 E : ASSERT_EQ(kNumBasicBlocks, module_data_.num_basic_blocks);
235 E : ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
236 :
237 : // Visiting an initial basic-block should not fail. It should initialize the
238 : // TLS index, map the frequency data to the default array, and increment the
239 : // call count in the default array.
240 E : SimulateBasicBlockEntry(0);
241 E : ASSERT_EQ(1U, default_frequency_data_[0]);
242 E : ASSERT_EQ(0U, default_frequency_data_[1]);
243 :
244 : // Re-visiting the same basic-block should only update the frequency array.
245 E : DWORD new_tls_index = module_data_.tls_index;
246 E : SimulateBasicBlockEntry(0);
247 E : ASSERT_EQ(new_tls_index, module_data_.tls_index);
248 E : ASSERT_EQ(2U, default_frequency_data_[0]);
249 E : ASSERT_EQ(0U, default_frequency_data_[1]);
250 :
251 : // Visiting a different basic-block should only update the frequency array.
252 E : SimulateBasicBlockEntry(1);
253 E : ASSERT_EQ(new_tls_index, module_data_.tls_index);
254 E : ASSERT_EQ(2U, default_frequency_data_[0]);
255 E : ASSERT_EQ(1U, default_frequency_data_[1]);
256 :
257 : // Simulate the process detach event.
258 E : SimulateModuleEvent(DLL_PROCESS_DETACH);
259 :
260 : // Unload the DLL.
261 E : ASSERT_NO_FATAL_FAILURE(UnloadDll());
262 :
263 : // Replay the log. There should be none as we didn't start the service.
264 E : ASSERT_NO_FATAL_FAILURE(ReplayLogs(0));
265 E : }
266 :
267 E : TEST_F(BasicBlockEntryTest, SingleThreadedBasicBlockEvents) {
268 E : ASSERT_NO_FATAL_FAILURE(StartService());
269 E : ASSERT_NO_FATAL_FAILURE(LoadDll());
270 :
271 : // Simulate the process attach event.
272 E : SimulateModuleEvent(DLL_PROCESS_ATTACH);
273 :
274 : // Validate that it does not modify any of our initialization values.
275 E : ASSERT_EQ(::common::kBasicBlockEntryAgentId, module_data_.agent_id);
276 E : ASSERT_EQ(::common::kBasicBlockFrequencyDataVersion, module_data_.version);
277 E : ASSERT_NE(TLS_OUT_OF_INDEXES, module_data_.tls_index);
278 E : ASSERT_NE(0U, module_data_.initialization_attempted);
279 E : ASSERT_EQ(kNumBasicBlocks, module_data_.num_basic_blocks);
280 E : ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
281 :
282 : // Visiting an initial basic-block should not fail. It should initialize the
283 : // TLS index, allocate a frequency map for this thread, and increment the
284 : // call count in the allocated frequency map. The default frequency data
285 : // should be left unchanged.
286 E : SimulateBasicBlockEntry(0);
287 E : ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
288 E : ASSERT_EQ(0U, default_frequency_data_[0]);
289 :
290 : // Make a few more calls, just to keep things interesting.
291 E : SimulateBasicBlockEntry(0);
292 E : SimulateBasicBlockEntry(1);
293 E : SimulateBasicBlockEntry(0);
294 :
295 : // Simulate the process attach event.
296 E : SimulateModuleEvent(DLL_PROCESS_DETACH);
297 :
298 : // Unload the DLL and stop the service.
299 E : ASSERT_NO_FATAL_FAILURE(UnloadDll());
300 :
301 E : HMODULE self = ::GetModuleHandle(NULL);
302 E : DWORD process_id = ::GetCurrentProcessId();
303 E : DWORD thread_id = ::GetCurrentThreadId();
304 :
305 : static const uint32 kExpectedFrequencyData[kNumBasicBlocks] = { 3, 1 };
306 :
307 : // Set up expectations for what should be in the trace.
308 E : EXPECT_CALL(handler_, OnProcessStarted(_, process_id, _));
309 : EXPECT_CALL(handler_, OnProcessAttach(_,
310 : process_id,
311 : thread_id,
312 E : ModuleAtAddress(self)));;
313 : EXPECT_CALL(handler_, OnBasicBlockFrequency(
314 : _,
315 : process_id,
316 : thread_id,
317 E : FrequencyDataMatches(self, kNumBasicBlocks, kExpectedFrequencyData)));
318 E : EXPECT_CALL(handler_, OnProcessEnded(_, process_id));
319 :
320 : // Replay the log.
321 E : ASSERT_NO_FATAL_FAILURE(ReplayLogs(1));
322 E : }
323 :
324 : // TODO(rogerm): Add a decent multi-thread test case.
325 :
326 : } // namespace basic_block_entry
327 : } // namespace agent
|