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