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_.data_type = ::common::IndexedFrequencyData::BASIC_BLOCK_ENTRY;
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_entries = 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 : base::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_, "_increment_indexed_freq_data");
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 :
146 : indirect_penter_exemain_stub_ =
147 E : ::GetProcAddress(agent_module_, "_indirect_penter_exemain");
148 E : ASSERT_TRUE(indirect_penter_exemain_stub_ != NULL);
149 :
150 : get_raw_frequency_data_stub_ =
151 E : ::GetProcAddress(agent_module_, "GetRawFrequencyData");
152 E : ASSERT_TRUE(get_raw_frequency_data_stub_ != NULL);
153 E : }
154 :
155 E : void UnloadDll() {
156 E : if (agent_module_ != NULL) {
157 E : ASSERT_TRUE(::FreeLibrary(agent_module_));
158 E : agent_module_ = NULL;
159 E : basic_block_enter_stub_ = NULL;
160 E : indirect_penter_dllmain_stub_ = NULL;
161 E : indirect_penter_exemain_stub_ = NULL;
162 E : get_raw_frequency_data_stub_ = NULL;
163 : }
164 E : }
165 :
166 : protected:
167 : static BOOL WINAPI DllMain(HMODULE module, DWORD reason, LPVOID reserved);
168 : static BOOL WINAPI DllMainThunk(
169 : HMODULE module, DWORD reason, LPVOID reserved);
170 : static int __cdecl ExeMain();
171 : static int __cdecl ExeMainThunk();
172 : static int __cdecl GetFrequencyDataThunk();
173 :
174 E : void SimulateModuleEvent(DWORD reason) {
175 E : DllMainThunk(kThisModule, reason, NULL);
176 E : }
177 :
178 i : void SimulateBasicBlockEntry(uint32 basic_block_id) {
179 : __asm {
180 i : push basic_block_id
181 i : push offset module_data_
182 i : call basic_block_enter_stub_
183 : }
184 i : }
185 :
186 : // The directory where trace file output will be written.
187 : base::ScopedTempDir temp_dir_;
188 :
189 : // The handler to which the trace file parser will delegate events.
190 : StrictMockParseEventHandler handler_;
191 :
192 : // Our call trace service process instance.
193 : testing::CallTraceService service_;
194 :
195 : // The basic-block entry client module.
196 : HMODULE agent_module_;
197 :
198 : // This will be a stand-in for the (usually statically allocated) trace
199 : // data which would have been referenced by the instrumentation.
200 : static IndexedFrequencyData module_data_;
201 :
202 : // This will be a stand-in for the (usually statically allocated) fall-back
203 : // frequency to which module_data_.frequency_data will point.
204 : static uint32 default_frequency_data_[kNumBasicBlocks];
205 :
206 : // The basic-block entry entrance hook.
207 : static FARPROC basic_block_enter_stub_;
208 :
209 : // The DllMain entry stub.
210 : static FARPROC indirect_penter_dllmain_stub_;
211 :
212 : // The ExeMain entry stub.
213 : static FARPROC indirect_penter_exemain_stub_;
214 :
215 : // The entry stub to get a pointer to data frequency.
216 : static FARPROC get_raw_frequency_data_stub_;
217 : };
218 :
219 : BOOL WINAPI BasicBlockEntryTest::DllMain(
220 E : HMODULE module, DWORD reason, LPVOID reserved) {
221 E : return TRUE;
222 E : }
223 :
224 : BOOL __declspec(naked) WINAPI BasicBlockEntryTest::DllMainThunk(
225 i : HMODULE module, DWORD reason, LPVOID reserved) {
226 : __asm {
227 i : push offset module_data_
228 i : push DllMain
229 i : jmp indirect_penter_dllmain_stub_
230 : }
231 : }
232 :
233 E : int __cdecl BasicBlockEntryTest::ExeMain() {
234 E : return 0;
235 E : }
236 :
237 i : int __declspec(naked) __cdecl BasicBlockEntryTest::GetFrequencyDataThunk() {
238 : __asm {
239 i : push offset module_data_
240 i : call get_raw_frequency_data_stub_
241 i : ret
242 : }
243 : }
244 :
245 i : BOOL __declspec(naked) __cdecl BasicBlockEntryTest::ExeMainThunk() {
246 : __asm {
247 i : push offset module_data_
248 i : push ExeMain
249 i : jmp indirect_penter_exemain_stub_
250 : }
251 : }
252 :
253 : IndexedFrequencyData BasicBlockEntryTest::module_data_ = {};
254 : uint32 BasicBlockEntryTest::default_frequency_data_[] = {};
255 : FARPROC BasicBlockEntryTest::basic_block_enter_stub_ = NULL;
256 : FARPROC BasicBlockEntryTest::indirect_penter_dllmain_stub_ = NULL;
257 : FARPROC BasicBlockEntryTest::indirect_penter_exemain_stub_ = NULL;
258 : FARPROC BasicBlockEntryTest::get_raw_frequency_data_stub_ = NULL;
259 :
260 : } // namespace
261 :
262 E : TEST_F(BasicBlockEntryTest, NoServerNoCrash) {
263 : // Load the agent dll.
264 E : ASSERT_NO_FATAL_FAILURE(LoadDll());
265 :
266 : // Simulate the process attach event.
267 E : SimulateModuleEvent(DLL_PROCESS_ATTACH);
268 :
269 : // Validate that it only modified the tls_index and initialization_attempted
270 : // values.
271 E : ASSERT_EQ(::common::kBasicBlockEntryAgentId, module_data_.agent_id);
272 E : ASSERT_EQ(::common::kBasicBlockFrequencyDataVersion, module_data_.version);
273 E : ASSERT_EQ(IndexedFrequencyData::BASIC_BLOCK_ENTRY, module_data_.data_type);
274 E : ASSERT_NE(TLS_OUT_OF_INDEXES, module_data_.tls_index);
275 E : ASSERT_NE(0U, module_data_.initialization_attempted);
276 E : ASSERT_EQ(kNumBasicBlocks, module_data_.num_entries);
277 E : ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
278 :
279 : // Visiting an initial basic-block should not fail. It should initialize the
280 : // TLS index, map the frequency data to the default array, and increment the
281 : // call count in the default array.
282 E : SimulateBasicBlockEntry(0);
283 E : ASSERT_EQ(1U, default_frequency_data_[0]);
284 E : ASSERT_EQ(0U, default_frequency_data_[1]);
285 :
286 : // Re-visiting the same basic-block should only update the frequency array.
287 E : DWORD new_tls_index = module_data_.tls_index;
288 E : SimulateBasicBlockEntry(0);
289 E : ASSERT_EQ(new_tls_index, module_data_.tls_index);
290 E : ASSERT_EQ(2U, default_frequency_data_[0]);
291 E : ASSERT_EQ(0U, default_frequency_data_[1]);
292 :
293 : // Visiting a different basic-block should only update the frequency array.
294 E : SimulateBasicBlockEntry(1);
295 E : ASSERT_EQ(new_tls_index, module_data_.tls_index);
296 E : ASSERT_EQ(2U, default_frequency_data_[0]);
297 E : ASSERT_EQ(1U, default_frequency_data_[1]);
298 :
299 : // Simulate the process detach event.
300 E : SimulateModuleEvent(DLL_PROCESS_DETACH);
301 :
302 : // Unload the DLL.
303 E : ASSERT_NO_FATAL_FAILURE(UnloadDll());
304 :
305 : // Replay the log. There should be none as we didn't start the service.
306 E : ASSERT_NO_FATAL_FAILURE(ReplayLogs(0));
307 E : }
308 :
309 E : TEST_F(BasicBlockEntryTest, SingleThreadedDllBasicBlockEvents) {
310 E : ASSERT_NO_FATAL_FAILURE(StartService());
311 E : ASSERT_NO_FATAL_FAILURE(LoadDll());
312 :
313 : // Simulate the process attach event.
314 E : SimulateModuleEvent(DLL_PROCESS_ATTACH);
315 :
316 : // Validate that it does not modify any of our initialization values.
317 E : ASSERT_EQ(::common::kBasicBlockEntryAgentId, module_data_.agent_id);
318 E : ASSERT_EQ(::common::kBasicBlockFrequencyDataVersion, module_data_.version);
319 E : ASSERT_EQ(IndexedFrequencyData::BASIC_BLOCK_ENTRY, module_data_.data_type);
320 E : ASSERT_NE(TLS_OUT_OF_INDEXES, module_data_.tls_index);
321 E : ASSERT_NE(0U, module_data_.initialization_attempted);
322 E : ASSERT_EQ(kNumBasicBlocks, module_data_.num_entries);
323 E : ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
324 :
325 : // Visiting an initial basic-block should not fail. It should initialize the
326 : // TLS index, allocate a frequency map for this thread, and increment the
327 : // call count in the allocated frequency map. The default frequency data
328 : // should be left unchanged.
329 E : SimulateBasicBlockEntry(0);
330 E : ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
331 E : ASSERT_EQ(0U, default_frequency_data_[0]);
332 :
333 : // Make a few more calls, just to keep things interesting.
334 E : SimulateBasicBlockEntry(0);
335 E : SimulateBasicBlockEntry(1);
336 E : SimulateBasicBlockEntry(0);
337 :
338 : // Simulate the process attach event.
339 E : SimulateModuleEvent(DLL_PROCESS_DETACH);
340 :
341 : // Unload the DLL and stop the service.
342 E : ASSERT_NO_FATAL_FAILURE(UnloadDll());
343 :
344 E : HMODULE self = ::GetModuleHandle(NULL);
345 E : DWORD process_id = ::GetCurrentProcessId();
346 E : DWORD thread_id = ::GetCurrentThreadId();
347 :
348 : static const uint32 kExpectedFrequencyData[kNumBasicBlocks] = { 3, 1 };
349 :
350 : // Set up expectations for what should be in the trace.
351 E : EXPECT_CALL(handler_, OnProcessStarted(_, process_id, _));
352 : EXPECT_CALL(handler_, OnProcessAttach(_,
353 : process_id,
354 : thread_id,
355 E : ModuleAtAddress(self)));;
356 : EXPECT_CALL(handler_, OnIndexedFrequency(
357 : _,
358 : process_id,
359 : thread_id,
360 E : FrequencyDataMatches(self, kNumBasicBlocks, kExpectedFrequencyData)));
361 E : EXPECT_CALL(handler_, OnProcessEnded(_, process_id));
362 :
363 : // Replay the log.
364 E : ASSERT_NO_FATAL_FAILURE(ReplayLogs(1));
365 E : }
366 :
367 E : TEST_F(BasicBlockEntryTest, SingleThreadedExeBasicBlockEvents) {
368 E : ASSERT_NO_FATAL_FAILURE(StartService());
369 E : ASSERT_NO_FATAL_FAILURE(LoadDll());
370 :
371 : // Simulate the process attach event.
372 E : ExeMainThunk();
373 :
374 : // Validate that it does not modify any of our initialization values.
375 E : ASSERT_EQ(::common::kBasicBlockEntryAgentId, module_data_.agent_id);
376 E : ASSERT_EQ(::common::kBasicBlockFrequencyDataVersion, module_data_.version);
377 E : ASSERT_EQ(IndexedFrequencyData::BASIC_BLOCK_ENTRY, module_data_.data_type);
378 E : ASSERT_NE(TLS_OUT_OF_INDEXES, module_data_.tls_index);
379 E : ASSERT_NE(0U, module_data_.initialization_attempted);
380 E : ASSERT_EQ(kNumBasicBlocks, module_data_.num_entries);
381 E : ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
382 :
383 : // Visiting an initial basic-block should not fail. It should initialize the
384 : // TLS index, allocate a frequency map for this thread, and increment the
385 : // call count in the allocated frequency map. The default frequency data
386 : // should be left unchanged.
387 E : SimulateBasicBlockEntry(0);
388 E : ASSERT_EQ(default_frequency_data_, module_data_.frequency_data);
389 E : ASSERT_EQ(0U, default_frequency_data_[0]);
390 :
391 : // Make a few more calls, just to keep things interesting.
392 E : SimulateBasicBlockEntry(0);
393 E : SimulateBasicBlockEntry(1);
394 E : SimulateBasicBlockEntry(0);
395 :
396 : // Unload the DLL and stop the service.
397 E : ASSERT_NO_FATAL_FAILURE(UnloadDll());
398 :
399 E : HMODULE self = ::GetModuleHandle(NULL);
400 E : DWORD process_id = ::GetCurrentProcessId();
401 E : DWORD thread_id = ::GetCurrentThreadId();
402 :
403 : static const uint32 kExpectedFrequencyData[kNumBasicBlocks] = { 3, 1 };
404 :
405 : // Set up expectations for what should be in the trace.
406 E : EXPECT_CALL(handler_, OnProcessStarted(_, process_id, _));
407 : EXPECT_CALL(handler_, OnProcessAttach(_,
408 : process_id,
409 : thread_id,
410 E : ModuleAtAddress(self)));;
411 : EXPECT_CALL(handler_, OnIndexedFrequency(
412 : _,
413 : process_id,
414 : thread_id,
415 E : FrequencyDataMatches(self, kNumBasicBlocks, kExpectedFrequencyData)));
416 E : EXPECT_CALL(handler_, OnProcessEnded(_, process_id));
417 :
418 : // Replay the log.
419 E : ASSERT_NO_FATAL_FAILURE(ReplayLogs(1));
420 E : }
421 :
422 E : TEST_F(BasicBlockEntryTest, InvokeGetFrequencyData) {
423 E : ASSERT_NO_FATAL_FAILURE(StartService());
424 E : ASSERT_NO_FATAL_FAILURE(LoadDll());
425 :
426 : // Simulate the process attach event.
427 E : ExeMainThunk();
428 :
429 : // Check creation of a buffer on first call.
430 E : EXPECT_TRUE(::TlsGetValue(module_data_.tls_index) == NULL);
431 E : uint32* data1 = reinterpret_cast<uint32*>(GetFrequencyDataThunk());
432 E : EXPECT_TRUE(data1 != NULL);
433 E : EXPECT_TRUE(::TlsGetValue(module_data_.tls_index) != NULL);
434 :
435 : // Next calls should return the same buffer.
436 E : uint32* data2 = reinterpret_cast<uint32*>(GetFrequencyDataThunk());
437 E : EXPECT_EQ(data1, data2);
438 :
439 : // Unload the DLL and stop the service.
440 E : ASSERT_NO_FATAL_FAILURE(UnloadDll());
441 E : }
442 :
443 : // TODO(rogerm): Add a decent multi-thread test case.
444 :
445 : } // namespace basic_block_entry
446 : } // namespace agent
|