1 : // Copyright 2012 Google Inc. All Rights Reserved.
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/files/scoped_temp_dir.h"
21 : #include "gmock/gmock.h"
22 : #include "gtest/gtest.h"
23 : #include "syzygy/common/indexed_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 and dirty way to get the HMODULE of this module for MS
28 : // linkers. 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::IndexedFrequencyData;
38 : using testing::_;
39 : using testing::StrictMockParseEventHandler;
40 : using trace::parser::Parser;
41 :
42 : // This is the name of the agent DLL.
43 : const wchar_t kBasicBlockEntryClientDll[] = L"basic_block_entry_client.dll";
44 :
45 : // The number of basic blocks we'll work with for these tests.
46 : const uint32 kNumBasicBlocks = 2;
47 :
48 : // The module defining this lib/executable.
49 : const HMODULE kThisModule = reinterpret_cast<HMODULE>(&__ImageBase);
50 :
51 : // A helper to match modules by base address.
52 E : MATCHER_P(ModuleAtAddress, module, "") {
53 E : return arg->module_base_addr == module;
54 E : }
55 :
56 : // A helper to match basic-block frequency results to expectations.
57 E : MATCHER_P3(FrequencyDataMatches, module, bb_count, bb_freqs, "") {
58 E : if (arg->module_base_addr != module)
59 i : return false;
60 :
61 E : if (arg->frequency_size != sizeof(uint32))
62 i : return false;
63 :
64 E : if (arg->num_entries != bb_count)
65 i : return false;
66 :
67 E : return ::memcmp(bb_freqs, arg->frequency_data, bb_count) == 0;
68 E : }
69 :
70 : // The test fixture for the basic-block entry agent.
71 : class BasicBlockEntryTest : public testing::Test {
72 : public:
73 E : BasicBlockEntryTest()
74 : : agent_module_(NULL) {
75 E : module_data_.agent_id = ::common::kBasicBlockEntryAgentId;
76 E : module_data_.version = ::common::kBasicBlockFrequencyDataVersion;
77 E : module_data_.tls_index = TLS_OUT_OF_INDEXES;
78 E : module_data_.initialization_attempted = 0U;
79 E : module_data_.num_entries = kNumBasicBlocks;
80 E : module_data_.frequency_data = default_frequency_data_;
81 E : ::memset(&default_frequency_data_, 0, sizeof(default_frequency_data_));
82 E : }
83 :
84 E : virtual void SetUp() OVERRIDE {
85 E : testing::Test::SetUp();
86 E : ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
87 E : service_.SetEnvironment();
88 E : }
89 :
90 E : virtual void TearDown() OVERRIDE {
91 E : UnloadDll();
92 E : service_.Stop();
93 E : }
94 :
95 E : void StartService() {
96 E : service_.Start(temp_dir_.path());
97 E : }
98 :
99 E : void StopService() {
100 E : service_.Stop();
101 E : }
102 :
103 E : void ReplayLogs(size_t files_expected) {
104 : // Stop the service if it's running.
105 E : ASSERT_NO_FATAL_FAILURE(StopService());
106 :
107 E : Parser parser;
108 E : ASSERT_TRUE(parser.Init(&handler_));
109 :
110 : // Queue up the trace file(s) we engendered.
111 : file_util::FileEnumerator enumerator(temp_dir_.path(),
112 : false,
113 E : file_util::FileEnumerator::FILES);
114 E : size_t num_files = 0;
115 E : while (true) {
116 E : base::FilePath trace_file = enumerator.Next();
117 E : if (trace_file.empty())
118 E : break;
119 E : ASSERT_TRUE(parser.OpenTraceFile(trace_file));
120 E : ++num_files;
121 E : }
122 :
123 E : EXPECT_EQ(files_expected, num_files);
124 :
125 E : if (num_files > 0)
126 E : ASSERT_TRUE(parser.Consume());
127 E : }
128 :
129 E : void LoadDll() {
130 E : ASSERT_EQ(NULL, agent_module_);
131 E : ASSERT_EQ(NULL, basic_block_enter_stub_);
132 E : ASSERT_EQ(NULL, ::GetModuleHandle(kBasicBlockEntryClientDll));
133 :
134 E : agent_module_ = ::LoadLibrary(kBasicBlockEntryClientDll);
135 E : ASSERT_TRUE(agent_module_ != NULL);
136 :
137 : basic_block_enter_stub_ =
138 E : ::GetProcAddress(agent_module_, "_basic_block_enter");
139 E : ASSERT_TRUE(basic_block_enter_stub_ != NULL);
140 :
141 : indirect_penter_dllmain_stub_ =
142 E : ::GetProcAddress(agent_module_, "_indirect_penter_dllmain");
143 E : ASSERT_TRUE(indirect_penter_dllmain_stub_ != NULL);
144 :
145 : indirect_penter_exemain_stub_ =
146 E : ::GetProcAddress(agent_module_, "_indirect_penter_exemain");
147 E : ASSERT_TRUE(indirect_penter_exemain_stub_ != NULL);
148 :
149 : get_raw_frequency_data_stub_ =
150 E : ::GetProcAddress(agent_module_, "GetRawFrequencyData");
151 E : ASSERT_TRUE(get_raw_frequency_data_stub_ != NULL);
152 E : }
153 :
154 E : void UnloadDll() {
155 E : if (agent_module_ != NULL) {
156 E : ASSERT_TRUE(::FreeLibrary(agent_module_));
157 E : agent_module_ = NULL;
158 E : basic_block_enter_stub_ = NULL;
159 E : indirect_penter_dllmain_stub_ = NULL;
160 E : indirect_penter_exemain_stub_ = NULL;
161 E : get_raw_frequency_data_stub_ = NULL;
162 : }
163 E : }
164 :
165 : protected:
166 : static BOOL WINAPI DllMain(HMODULE module, DWORD reason, LPVOID reserved);
167 : static BOOL WINAPI DllMainThunk(
168 : HMODULE module, DWORD reason, LPVOID reserved);
169 : static int __cdecl ExeMain();
170 : static int __cdecl ExeMainThunk();
171 : static int __cdecl GetFrequencyDataThunk();
172 :
173 E : void SimulateModuleEvent(DWORD reason) {
174 E : DllMainThunk(kThisModule, reason, NULL);
175 E : }
176 :
177 i : void SimulateBasicBlockEntry(uint32 basic_block_id) {
178 : __asm {
179 i : push basic_block_id
180 i : push offset module_data_
181 i : call basic_block_enter_stub_
182 : }
183 i : }
184 :
185 : // The directory where trace file output will be written.
186 : base::ScopedTempDir temp_dir_;
187 :
188 : // The handler to which the trace file parser will delegate events.
189 : StrictMockParseEventHandler handler_;
190 :
191 : // Our call trace service process instance.
192 : testing::CallTraceService service_;
193 :
194 : // The basic-block entry client module.
195 : HMODULE agent_module_;
196 :
197 : // This will be a stand-in for the (usually statically allocated) trace
198 : // data which would have been referenced by the instrumentation.
199 : static IndexedFrequencyData module_data_;
200 :
201 : // This will be a stand-in for the (usually statically allocated) fall-back
202 : // frequency to which module_data_.frequency_data will point.
203 : static uint32 default_frequency_data_[kNumBasicBlocks];
204 :
205 : // The basic-block entry entrance hook.
206 : static FARPROC basic_block_enter_stub_;
207 :
208 : // The DllMain entry stub.
209 : static FARPROC indirect_penter_dllmain_stub_;
210 :
211 : // The ExeMain entry stub.
212 : static FARPROC indirect_penter_exemain_stub_;
213 :
214 : // The entry stub to get a pointer to data frequency.
215 : static FARPROC get_raw_frequency_data_stub_;
216 : };
217 :
218 : BOOL WINAPI BasicBlockEntryTest::DllMain(
219 E : HMODULE module, DWORD reason, LPVOID reserved) {
220 E : return TRUE;
221 E : }
222 :
223 : BOOL __declspec(naked) WINAPI BasicBlockEntryTest::DllMainThunk(
224 i : HMODULE module, DWORD reason, LPVOID reserved) {
225 : __asm {
226 i : push offset module_data_
227 i : push DllMain
228 i : jmp indirect_penter_dllmain_stub_
229 : }
230 : }
231 :
232 E : int __cdecl BasicBlockEntryTest::ExeMain() {
233 E : return 0;
234 E : }
235 :
236 i : int __declspec(naked) __cdecl BasicBlockEntryTest::GetFrequencyDataThunk() {
237 : __asm {
238 i : push offset module_data_
239 i : call get_raw_frequency_data_stub_
240 i : ret
241 : }
242 : }
243 :
244 i : BOOL __declspec(naked) __cdecl BasicBlockEntryTest::ExeMainThunk() {
245 : __asm {
246 i : push offset module_data_
247 i : push ExeMain
248 i : jmp indirect_penter_exemain_stub_
249 : }
250 : }
251 :
252 : IndexedFrequencyData BasicBlockEntryTest::module_data_ = {};
253 : uint32 BasicBlockEntryTest::default_frequency_data_[] = {};
254 : FARPROC BasicBlockEntryTest::basic_block_enter_stub_ = NULL;
255 : FARPROC BasicBlockEntryTest::indirect_penter_dllmain_stub_ = NULL;
256 : FARPROC BasicBlockEntryTest::indirect_penter_exemain_stub_ = NULL;
257 : FARPROC BasicBlockEntryTest::get_raw_frequency_data_stub_ = NULL;
258 :
259 : } // namespace
260 :
261 E : TEST_F(BasicBlockEntryTest, NoServerNoCrash) {
262 : // Load the agent dll.
263 E : ASSERT_NO_FATAL_FAILURE(LoadDll());
264 :
265 : // Simulate the process attach event.
266 E : SimulateModuleEvent(DLL_PROCESS_ATTACH);
267 :
268 : // Validate that it only modified the tls_index and initialization_attempted
269 : // values.
270 E : ASSERT_EQ(::common::kBasicBlockEntryAgentId, module_data_.agent_id);
271 E : ASSERT_EQ(::common::kBasicBlockFrequencyDataVersion, module_data_.version);
272 E : ASSERT_NE(TLS_OUT_OF_INDEXES, module_data_.tls_index);
273 E : ASSERT_NE(0U, module_data_.initialization_attempted);
274 E : ASSERT_EQ(kNumBasicBlocks, module_data_.num_entries);
275 E : ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
276 :
277 : // Visiting an initial basic-block should not fail. It should initialize the
278 : // TLS index, map the frequency data to the default array, and increment the
279 : // call count in the default array.
280 E : SimulateBasicBlockEntry(0);
281 E : ASSERT_EQ(1U, default_frequency_data_[0]);
282 E : ASSERT_EQ(0U, default_frequency_data_[1]);
283 :
284 : // Re-visiting the same basic-block should only update the frequency array.
285 E : DWORD new_tls_index = module_data_.tls_index;
286 E : SimulateBasicBlockEntry(0);
287 E : ASSERT_EQ(new_tls_index, module_data_.tls_index);
288 E : ASSERT_EQ(2U, default_frequency_data_[0]);
289 E : ASSERT_EQ(0U, default_frequency_data_[1]);
290 :
291 : // Visiting a different basic-block should only update the frequency array.
292 E : SimulateBasicBlockEntry(1);
293 E : ASSERT_EQ(new_tls_index, module_data_.tls_index);
294 E : ASSERT_EQ(2U, default_frequency_data_[0]);
295 E : ASSERT_EQ(1U, default_frequency_data_[1]);
296 :
297 : // Simulate the process detach event.
298 E : SimulateModuleEvent(DLL_PROCESS_DETACH);
299 :
300 : // Unload the DLL.
301 E : ASSERT_NO_FATAL_FAILURE(UnloadDll());
302 :
303 : // Replay the log. There should be none as we didn't start the service.
304 E : ASSERT_NO_FATAL_FAILURE(ReplayLogs(0));
305 E : }
306 :
307 E : TEST_F(BasicBlockEntryTest, SingleThreadedDllBasicBlockEvents) {
308 E : ASSERT_NO_FATAL_FAILURE(StartService());
309 E : ASSERT_NO_FATAL_FAILURE(LoadDll());
310 :
311 : // Simulate the process attach event.
312 E : SimulateModuleEvent(DLL_PROCESS_ATTACH);
313 :
314 : // Validate that it does not modify any of our initialization values.
315 E : ASSERT_EQ(::common::kBasicBlockEntryAgentId, module_data_.agent_id);
316 E : ASSERT_EQ(::common::kBasicBlockFrequencyDataVersion, module_data_.version);
317 E : ASSERT_NE(TLS_OUT_OF_INDEXES, module_data_.tls_index);
318 E : ASSERT_NE(0U, module_data_.initialization_attempted);
319 E : ASSERT_EQ(kNumBasicBlocks, module_data_.num_entries);
320 E : ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
321 :
322 : // Visiting an initial basic-block should not fail. It should initialize the
323 : // TLS index, allocate a frequency map for this thread, and increment the
324 : // call count in the allocated frequency map. The default frequency data
325 : // should be left unchanged.
326 E : SimulateBasicBlockEntry(0);
327 E : ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
328 E : ASSERT_EQ(0U, default_frequency_data_[0]);
329 :
330 : // Make a few more calls, just to keep things interesting.
331 E : SimulateBasicBlockEntry(0);
332 E : SimulateBasicBlockEntry(1);
333 E : SimulateBasicBlockEntry(0);
334 :
335 : // Simulate the process attach event.
336 E : SimulateModuleEvent(DLL_PROCESS_DETACH);
337 :
338 : // Unload the DLL and stop the service.
339 E : ASSERT_NO_FATAL_FAILURE(UnloadDll());
340 :
341 E : HMODULE self = ::GetModuleHandle(NULL);
342 E : DWORD process_id = ::GetCurrentProcessId();
343 E : DWORD thread_id = ::GetCurrentThreadId();
344 :
345 : static const uint32 kExpectedFrequencyData[kNumBasicBlocks] = { 3, 1 };
346 :
347 : // Set up expectations for what should be in the trace.
348 E : EXPECT_CALL(handler_, OnProcessStarted(_, process_id, _));
349 : EXPECT_CALL(handler_, OnProcessAttach(_,
350 : process_id,
351 : thread_id,
352 E : ModuleAtAddress(self)));;
353 : EXPECT_CALL(handler_, OnIndexedFrequency(
354 : _,
355 : process_id,
356 : thread_id,
357 E : FrequencyDataMatches(self, kNumBasicBlocks, kExpectedFrequencyData)));
358 E : EXPECT_CALL(handler_, OnProcessEnded(_, process_id));
359 :
360 : // Replay the log.
361 E : ASSERT_NO_FATAL_FAILURE(ReplayLogs(1));
362 E : }
363 :
364 E : TEST_F(BasicBlockEntryTest, SingleThreadedExeBasicBlockEvents) {
365 E : ASSERT_NO_FATAL_FAILURE(StartService());
366 E : ASSERT_NO_FATAL_FAILURE(LoadDll());
367 :
368 : // Simulate the process attach event.
369 E : ExeMainThunk();
370 :
371 : // Validate that it does not modify any of our initialization values.
372 E : ASSERT_EQ(::common::kBasicBlockEntryAgentId, module_data_.agent_id);
373 E : ASSERT_EQ(::common::kBasicBlockFrequencyDataVersion, module_data_.version);
374 E : ASSERT_NE(TLS_OUT_OF_INDEXES, module_data_.tls_index);
375 E : ASSERT_NE(0U, module_data_.initialization_attempted);
376 E : ASSERT_EQ(kNumBasicBlocks, module_data_.num_entries);
377 E : ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
378 :
379 : // Visiting an initial basic-block should not fail. It should initialize the
380 : // TLS index, allocate a frequency map for this thread, and increment the
381 : // call count in the allocated frequency map. The default frequency data
382 : // should be left unchanged.
383 E : SimulateBasicBlockEntry(0);
384 E : ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
385 E : ASSERT_EQ(0U, default_frequency_data_[0]);
386 :
387 : // Make a few more calls, just to keep things interesting.
388 E : SimulateBasicBlockEntry(0);
389 E : SimulateBasicBlockEntry(1);
390 E : SimulateBasicBlockEntry(0);
391 :
392 : // Unload the DLL and stop the service.
393 E : ASSERT_NO_FATAL_FAILURE(UnloadDll());
394 :
395 E : HMODULE self = ::GetModuleHandle(NULL);
396 E : DWORD process_id = ::GetCurrentProcessId();
397 E : DWORD thread_id = ::GetCurrentThreadId();
398 :
399 : static const uint32 kExpectedFrequencyData[kNumBasicBlocks] = { 3, 1 };
400 :
401 : // Set up expectations for what should be in the trace.
402 E : EXPECT_CALL(handler_, OnProcessStarted(_, process_id, _));
403 : EXPECT_CALL(handler_, OnProcessAttach(_,
404 : process_id,
405 : thread_id,
406 E : ModuleAtAddress(self)));;
407 : EXPECT_CALL(handler_, OnIndexedFrequency(
408 : _,
409 : process_id,
410 : thread_id,
411 E : FrequencyDataMatches(self, kNumBasicBlocks, kExpectedFrequencyData)));
412 E : EXPECT_CALL(handler_, OnProcessEnded(_, process_id));
413 :
414 : // Replay the log.
415 E : ASSERT_NO_FATAL_FAILURE(ReplayLogs(1));
416 E : }
417 :
418 E : TEST_F(BasicBlockEntryTest, InvokeGetFrequencyData) {
419 E : ASSERT_NO_FATAL_FAILURE(StartService());
420 E : ASSERT_NO_FATAL_FAILURE(LoadDll());
421 :
422 : // Simulate the process attach event.
423 E : ExeMainThunk();
424 :
425 : // Check creation of a buffer on first call.
426 E : EXPECT_TRUE(::TlsGetValue(module_data_.tls_index) == NULL);
427 E : uint32* data1 = reinterpret_cast<uint32*>(GetFrequencyDataThunk());
428 E : EXPECT_TRUE(data1 != NULL);
429 E : EXPECT_TRUE(::TlsGetValue(module_data_.tls_index) != NULL);
430 :
431 : // Next calls should return the same buffer.
432 E : uint32* data2 = reinterpret_cast<uint32*>(GetFrequencyDataThunk());
433 E : EXPECT_EQ(data1, data2);
434 :
435 : // Unload the DLL and stop the service.
436 E : ASSERT_NO_FATAL_FAILURE(UnloadDll());
437 E : }
438 :
439 : // TODO(rogerm): Add a decent multi-thread test case.
440 :
441 : } // namespace basic_block_entry
442 : } // namespace agent
|