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 : // Implementation of the basic-block entry counting agent library.
16 :
17 : #include "syzygy/agent/basic_block_entry/basic_block_entry.h"
18 :
19 : #include "base/at_exit.h"
20 : #include "base/command_line.h"
21 : #include "base/environment.h"
22 : #include "base/file_path.h"
23 : #include "base/lazy_instance.h"
24 : #include "base/stringprintf.h"
25 : #include "base/utf_string_conversions.h"
26 : #include "base/memory/scoped_ptr.h"
27 : #include "sawbuck/common/com_utils.h"
28 : #include "syzygy/agent/common/process_utils.h"
29 : #include "syzygy/agent/common/scoped_last_error_keeper.h"
30 : #include "syzygy/common/logging.h"
31 : #include "syzygy/trace/protocol/call_trace_defs.h"
32 :
33 i : extern "C" void __declspec(naked) _basic_block_enter() {
34 : __asm {
35 : // This is expected to be called via instrumentation that looks like:
36 : // push bb_id
37 : // push module_data
38 : // call [_basic_block_enter]
39 : //
40 : // Stack: ... bb_id, module_data, ret_addr.
41 :
42 : // Stash volatile registers.
43 i : push eax
44 i : push ecx
45 i : push edx
46 i : pushfd
47 :
48 : // Stack: ... bb_id, module_data, ret_addr, eax, ecx, edx, fd.
49 :
50 : // Push the original esp value onto the stack as the entry-hook data.
51 : // This gives the entry-hook a pointer to ret_addr, module_data and bb_id.
52 i : lea eax, DWORD PTR[esp + 0x10]
53 i : push eax
54 :
55 : // Stack: ..., bb_id, module_data, ret_addr, eax, ecx, edx, fd, &ret_addr.
56 i : call agent::basic_block_entry::BasicBlockEntry::BasicBlockEntryHook
57 :
58 : // Stack: ... bb_id, module_data, ret_addr, eax, ecx, edx, fd.
59 :
60 : // Restore volatile registers.
61 i : popfd
62 i : pop edx
63 i : pop ecx
64 i : pop eax
65 :
66 : // Stack: ... bb_id, module_data, ret_addr.
67 :
68 : // Return to the address pushed by our caller, popping off the bb_id and
69 : // module_data values from the stack.
70 i : ret 8
71 :
72 : // Stack: ...
73 : }
74 : }
75 :
76 i : extern "C" void __declspec(naked) _indirect_penter_dllmain() {
77 : __asm {
78 : // This is expected to be called via a thunk that looks like:
79 : // push module_data
80 : // push function
81 : // jmp [_indirect_penter_dllmain]
82 : //
83 : // Stack: ... reserved, reason, module, ret_addr, module_data, function.
84 :
85 : // Stash volatile registers.
86 i : push eax
87 i : push ecx
88 i : push edx
89 i : pushfd
90 :
91 : // Stack: ... reserved, reason, module, ret_addr, module_data, function,
92 : // eax, ecx, edx, fd.
93 :
94 : // Push the original esp value onto the stack as the entry-hook data.
95 : // This gives the dll entry-hook a pointer to function, module_data,
96 : // ret_addr, module, reason and reserved.
97 i : lea eax, DWORD PTR[esp + 0x10]
98 i : push eax
99 :
100 : // Stack: ... reserved, reason, module, ret_addr, module_data, function,
101 : // eax, ecx, edx, fd, &function.
102 :
103 i : call agent::basic_block_entry::BasicBlockEntry::DllMainEntryHook
104 :
105 : // Stack: ... reserved, reason, module, ret_addr, module_data, function,
106 : // eax, ecx, edx, fd.
107 :
108 : // Restore volatile registers.
109 i : popfd
110 i : pop edx
111 i : pop ecx
112 i : pop eax
113 :
114 : // Stack: ... reserved, reason, module, ret_addr, module_data, function.
115 :
116 : // Return to the thunked function, popping module_data off the stack as
117 : // we go.
118 i : ret 4
119 :
120 : // Stack: ... reserved, reason, module, ret_addr.
121 : }
122 : }
123 :
124 i : extern "C" void __declspec(naked) _indirect_penter_exemain() {
125 : __asm {
126 : // This is expected to be called via a thunk that looks like:
127 : // push module_data
128 : // push function
129 : // jmp [_indirect_penter_exe_main]
130 : //
131 : // Stack: ... ret_addr, module_data, function.
132 :
133 : // Stash volatile registers.
134 i : push eax
135 i : push ecx
136 i : push edx
137 i : pushfd
138 :
139 : // Stack: ... ret_addr, module_data, function, eax, ecx, edx, fd.
140 :
141 : // Push the original esp value onto the stack as the entry-hook data.
142 : // This gives the exe entry-hook a pointer to function, module_data,
143 : // and ret_addr.
144 i : lea eax, DWORD PTR[esp + 0x10]
145 i : push eax
146 :
147 : // Stack: ... ret_addr, module_data, function, eax, ecx, edx, fd, frame.
148 :
149 i : call agent::basic_block_entry::BasicBlockEntry::ExeMainEntryHook
150 :
151 : // Stack: ... ret_addr, module_data, function, eax, ecx, edx, fd.
152 :
153 : // Restore volatile registers.
154 i : popfd
155 i : pop edx
156 i : pop ecx
157 i : pop eax
158 :
159 : // Stack: ... ret_addr, module_data, function.
160 :
161 : // Return to the thunked function, popping module_data off the stack as
162 : // we go.
163 i : ret 4
164 :
165 : // Stack: ... reserved, reason, module, ret_addr.
166 : }
167 : }
168 :
169 E : BOOL WINAPI DllMain(HMODULE instance, DWORD reason, LPVOID reserved) {
170 : // Our AtExit manager required by base.
171 : static base::AtExitManager* at_exit = NULL;
172 :
173 E : switch (reason) {
174 : case DLL_PROCESS_ATTACH:
175 E : DCHECK(at_exit == NULL);
176 E : at_exit = new base::AtExitManager();
177 :
178 E : CommandLine::Init(0, NULL);
179 E : common::InitLoggingForDll(L"basic_block_entry");
180 E : LOG(INFO) << "Initialized basic-block entry counting agent library.";
181 E : break;
182 :
183 : case DLL_THREAD_ATTACH:
184 i : break;
185 :
186 : case DLL_THREAD_DETACH:
187 i : break;
188 :
189 : case DLL_PROCESS_DETACH:
190 E : DCHECK(at_exit != NULL);
191 E : delete at_exit;
192 E : at_exit = NULL;
193 E : break;
194 :
195 : default:
196 i : NOTREACHED();
197 : break;
198 : }
199 :
200 E : return TRUE;
201 E : }
202 :
203 : namespace agent {
204 : namespace basic_block_entry {
205 :
206 : namespace {
207 :
208 : using ::common::IndexedFrequencyData;
209 : using ::common::kBasicBlockEntryAgentId;
210 : using ::common::kBasicBlockFrequencyDataVersion;
211 : using agent::common::ScopedLastErrorKeeper;
212 : using trace::client::TraceFileSegment;
213 :
214 : // All tracing runs through this object.
215 : base::LazyInstance<BasicBlockEntry> static_coverage_instance =
216 : LAZY_INSTANCE_INITIALIZER;
217 :
218 : // Get the address of the module containing @p addr. We do this by querying
219 : // for the allocation that contains @p addr. This must lie within the
220 : // instrumented module, and be part of the single allocation in which the
221 : // image of the module lies. The base of the module will be the base address
222 : // of the allocation.
223 : // TODO(rogerm): Move to agent::common.
224 E : HMODULE GetModuleForAddr(const void* addr) {
225 E : MEMORY_BASIC_INFORMATION mem_info = {};
226 :
227 : // Lookup up the allocation in which addr is located.
228 E : if (::VirtualQuery(addr, &mem_info, sizeof(mem_info)) == 0) {
229 i : DWORD error = ::GetLastError();
230 i : LOG(ERROR) << "VirtualQuery failed: " << com::LogWe(error) << ".";
231 i : return NULL;
232 : }
233 :
234 : // Check that the allocation base has a valid PE header magic number.
235 E : base::win::PEImage image(reinterpret_cast<HMODULE>(mem_info.AllocationBase));
236 E : if (!image.VerifyMagic()) {
237 i : LOG(ERROR) << "Invalid module found for "
238 : << base::StringPrintf("0x%08X", addr) << ".";
239 i : return NULL;
240 : }
241 :
242 : // Then it's a module.
243 E : return image.module();
244 E : }
245 :
246 : } // namespace
247 :
248 : // The BasicBlockEntryHook parameters.
249 : struct BasicBlockEntry::BasicBlockEntryFrame {
250 : const void* ret_addr;
251 : IndexedFrequencyData* module_data;
252 : uint32 basic_block_id;
253 : };
254 :
255 : // The DllMainEntryHook parameters.
256 : struct BasicBlockEntry::DllMainEntryFrame {
257 : FuncAddr function;
258 : IndexedFrequencyData* module_data;
259 : const void* ret_addr;
260 : HMODULE module;
261 : DWORD reason;
262 : DWORD reserved;
263 : };
264 :
265 : // The ExeMainEntryHook parameters.
266 : struct BasicBlockEntry::ExeMainEntryFrame {
267 : FuncAddr function;
268 : IndexedFrequencyData* module_data;
269 : const void* ret_addr;
270 : };
271 :
272 : namespace {
273 :
274 : COMPILE_ASSERT(sizeof(BasicBlockEntry::BasicBlockEntryFrame) == 12,
275 : BasicBlockEntry_BasicBlockEntryFrame_is_not_the_right_size);
276 :
277 : COMPILE_ASSERT(sizeof(BasicBlockEntry::DllMainEntryFrame) == 24,
278 : BasicBlockEntry_DllMainEntryFrame_is_not_the_right_size);
279 :
280 : COMPILE_ASSERT(sizeof(BasicBlockEntry::ExeMainEntryFrame) == 12,
281 : BasicBlockEntry_ExeMainEntryFrame_is_not_the_right_size);
282 : }
283 :
284 : // The per-thread-per-instrumented-module state managed by this agent.
285 : class BasicBlockEntry::ThreadState : public agent::common::ThreadStateBase {
286 : public:
287 : // Initialize a ThreadState instance.
288 : ThreadState(BasicBlockEntry* agent, void* buffer);
289 :
290 : // Destroy a ThreadState instance.
291 : ~ThreadState();
292 :
293 : // @name Accessors.
294 : // @{
295 : uint32* frequency_data() { return frequency_data_; }
296 E : TraceFileSegment* segment() { return &segment_; }
297 : TraceIndexedFrequencyData* trace_data() { return trace_data_; }
298 : // @}
299 :
300 : // @name Mutators.
301 : // @{
302 : void set_frequency_data(void* buffer);
303 : void set_trace_data(TraceIndexedFrequencyData* trace_data);
304 : // @}
305 :
306 : // A helper to return a ThreadState pointer given a TLS index.
307 : static ThreadState* Get(DWORD tls_index);
308 :
309 : // A helper to assign a ThreadState pointer to a TLS index.
310 : void Assign(DWORD tls_index);
311 :
312 : // Saturation increment the frequency record for @p basic_block_id. Note
313 : // that in Release mode, no range checking is performed on basic_block_id.
314 : void Increment(uint32 basic_block_id);
315 :
316 : protected:
317 : // As a shortcut, this points to the beginning of the array of basic-block
318 : // entry frequency values. With tracing enabled, this is equivalent to:
319 : // reinterpret_cast<uint32*>(this->trace_data->frequency_data)
320 : // If tracing is not enabled, this will be set to point to a static
321 : // allocation of IndexedFrequencyData::frequency_data.
322 : uint32* frequency_data_;
323 :
324 : // The basic-block entry agent this tread state belongs to.
325 : BasicBlockEntry* agent_;
326 :
327 : // The thread's current trace-file segment, if any.
328 : trace::client::TraceFileSegment segment_;
329 :
330 : // The basic-block frequency record we're populating. This will point into
331 : // the associated trace file segment's buffer.
332 : TraceIndexedFrequencyData* trace_data_;
333 :
334 : private:
335 : DISALLOW_COPY_AND_ASSIGN(ThreadState);
336 : };
337 :
338 : BasicBlockEntry::ThreadState::ThreadState(BasicBlockEntry* agent, void* buffer)
339 : : agent_(agent),
340 : frequency_data_(static_cast<uint32*>(buffer)),
341 E : trace_data_(NULL) {
342 E : DCHECK(agent != NULL);
343 E : DCHECK(buffer != NULL);
344 E : }
345 :
346 i : BasicBlockEntry::ThreadState::~ThreadState() {
347 : // If we have an outstanding buffer, let's deallocate it now.
348 i : if (segment_.write_ptr != NULL && !agent_->session_.IsDisabled())
349 i : agent_->session_.ReturnBuffer(&segment_);
350 i : }
351 :
352 E : void BasicBlockEntry::ThreadState::set_frequency_data(void* buffer) {
353 E : DCHECK(buffer != NULL);
354 E : frequency_data_ = static_cast<uint32*>(buffer);
355 E : }
356 :
357 : void BasicBlockEntry::ThreadState::set_trace_data(
358 : TraceIndexedFrequencyData* trace_data) {
359 : DCHECK(trace_data != NULL);
360 : trace_data_ = trace_data;
361 : }
362 :
363 : BasicBlockEntry::ThreadState* BasicBlockEntry::ThreadState::Get(
364 E : DWORD tls_index) {
365 E : DCHECK_NE(TLS_OUT_OF_INDEXES, tls_index);
366 E : return static_cast<ThreadState*>(::TlsGetValue(tls_index));
367 E : }
368 :
369 E : void BasicBlockEntry::ThreadState::Assign(DWORD tls_index) {
370 E : DCHECK_NE(TLS_OUT_OF_INDEXES, tls_index);
371 E : ::TlsSetValue(tls_index, this);
372 E : }
373 :
374 E : inline void BasicBlockEntry::ThreadState::Increment(uint32 basic_block_id) {
375 E : DCHECK(frequency_data_ != NULL);
376 E : DCHECK(trace_data_ == NULL || basic_block_id < trace_data_->num_entries);
377 E : uint32& element = frequency_data_[basic_block_id];
378 E : if (element != ~0U)
379 E : ++element;
380 E : }
381 :
382 E : BasicBlockEntry* BasicBlockEntry::Instance() {
383 E : return static_coverage_instance.Pointer();
384 E : }
385 :
386 E : BasicBlockEntry::BasicBlockEntry() {
387 : // Create a session. We immediately return the buffer that gets allocated
388 : // to us. The client will perform thread-local buffer management on an as-
389 : // needed basis.
390 E : trace::client::TraceFileSegment dummy_segment;
391 E : if (trace::client::InitializeRpcSession(&session_, &dummy_segment))
392 E : CHECK(session_.ReturnBuffer(&dummy_segment));
393 E : }
394 :
395 E : BasicBlockEntry::~BasicBlockEntry() {
396 E : }
397 :
398 E : void BasicBlockEntry::BasicBlockEntryHook(BasicBlockEntryFrame* entry_frame) {
399 E : ScopedLastErrorKeeper scoped_last_error_keeper;
400 E : DCHECK(entry_frame != NULL);
401 E : DCHECK(entry_frame->module_data != NULL);
402 : DCHECK_GT(entry_frame->module_data->num_entries,
403 E : entry_frame->basic_block_id);
404 :
405 : // TODO(rogerm): Consider extracting a fast path for state != NULL? Inline it
406 : // during instrumentation? Move it into the _basic_block_enter function?
407 E : ThreadState* state = ThreadState::Get(entry_frame->module_data->tls_index);
408 E : if (state == NULL)
409 E : state = Instance()->CreateThreadState(entry_frame->module_data);
410 E : state->Increment(entry_frame->basic_block_id);
411 E : }
412 :
413 E : void BasicBlockEntry::DllMainEntryHook(DllMainEntryFrame* entry_frame) {
414 E : ScopedLastErrorKeeper scoped_last_error_keeper;
415 E : DCHECK(entry_frame != NULL);
416 E : switch (entry_frame->reason) {
417 : case DLL_PROCESS_ATTACH:
418 E : Instance()->OnProcessAttach(entry_frame->module_data);
419 E : break;
420 :
421 : case DLL_THREAD_ATTACH:
422 : // We don't handle this event because the thread may never actually
423 : // call into an instrumented module, so we don't want to allocate
424 : // resources needlessly. Further, we won't get this event for thread
425 : // that were created before the agent was loaded. On first use of
426 : // an instrumented basic-block in a given thread, any thread specific
427 : // resources will be allocated.
428 i : break;
429 :
430 : case DLL_PROCESS_DETACH:
431 : case DLL_THREAD_DETACH:
432 E : Instance()->OnThreadDetach(entry_frame->module_data);
433 E : break;
434 :
435 : default:
436 i : NOTREACHED();
437 : }
438 E : }
439 :
440 E : void BasicBlockEntry::ExeMainEntryHook(ExeMainEntryFrame* entry_frame) {
441 E : ScopedLastErrorKeeper scoped_last_error_keeper;
442 E : DCHECK(entry_frame != NULL);
443 E : Instance()->OnProcessAttach(entry_frame->module_data);
444 E : }
445 :
446 E : void BasicBlockEntry::RegisterModule(const void* addr) {
447 E : DCHECK(addr != NULL);
448 :
449 : // Allocate a segment for the module information.
450 E : trace::client::TraceFileSegment module_info_segment;
451 E : CHECK(session_.AllocateBuffer(&module_info_segment));
452 :
453 : // Log the module. This is required in order to associate basic-block
454 : // frequency with a module and PDB file during post-processing.
455 E : HMODULE module = GetModuleForAddr(addr);
456 E : CHECK(module != NULL);
457 E : CHECK(agent::common::LogModule(module, &session_, &module_info_segment));
458 :
459 : // Commit the module information.
460 E : CHECK(session_.ReturnBuffer(&module_info_segment));
461 E : }
462 :
463 E : void BasicBlockEntry::OnProcessAttach(IndexedFrequencyData* module_data) {
464 E : DCHECK(module_data != NULL);
465 :
466 : // Exit if the magic number does not match.
467 E : CHECK_EQ(kBasicBlockEntryAgentId, module_data->agent_id);
468 :
469 : // Exit if the version does not match.
470 E : CHECK_EQ(kBasicBlockFrequencyDataVersion, module_data->version);
471 :
472 : // We allow for this hook to be called multiple times. We expect the first
473 : // time to occur under the loader lock, so we don't need to worry about
474 : // concurrency for this check.
475 E : if (module_data->initialization_attempted)
476 E : return;
477 :
478 : // Flag the module as initialized.
479 E : module_data->initialization_attempted = 1U;
480 :
481 : // We expect this to be executed exactly once for each module.
482 E : CHECK_EQ(TLS_OUT_OF_INDEXES, module_data->tls_index);
483 E : module_data->tls_index = ::TlsAlloc();
484 E : CHECK_NE(TLS_OUT_OF_INDEXES, module_data->tls_index);
485 :
486 : // Register this module with the call_trace if the session is not disabled.
487 : // Note that we expect module_data to be statically defined within the
488 : // module of interest, so we can use its address to lookup the module.
489 E : if (!session_.IsDisabled())
490 E : RegisterModule(module_data);
491 E : }
492 :
493 E : void BasicBlockEntry::OnThreadDetach(IndexedFrequencyData* module_data) {
494 E : DCHECK(module_data != NULL);
495 E : DCHECK_EQ(1U, module_data->initialization_attempted);
496 E : DCHECK_NE(TLS_OUT_OF_INDEXES, module_data->tls_index);
497 :
498 E : ThreadState* state = ThreadState::Get(module_data->tls_index);
499 E : if (state != NULL)
500 E : thread_state_manager_.MarkForDeath(state);
501 E : }
502 :
503 : BasicBlockEntry::ThreadState* BasicBlockEntry::CreateThreadState(
504 E : IndexedFrequencyData* module_data) {
505 E : DCHECK(module_data != NULL);
506 :
507 : // Create the thread-local state for this thread. By default, just point the
508 : // counter array to the statically allocated fall-back area.
509 E : ThreadState* state = new ThreadState(this, module_data->frequency_data);
510 E : CHECK(state != NULL);
511 :
512 : // Associate the thread_state with the current thread.
513 E : state->Assign(module_data->tls_index);
514 :
515 : // Register the thread state with the thread state manager.
516 E : thread_state_manager_.Register(state);
517 :
518 : // If we're not actually tracing, then we're done.
519 E : if (session_.IsDisabled())
520 E : return state;
521 :
522 : // Nothing to allocate? We're done!
523 E : if (module_data->num_entries == 0) {
524 i : LOG(WARNING) << "Module contains no instrumented basic blocks, not "
525 : << "allocating basic-block trace data segment.";
526 i : return state;
527 : }
528 :
529 : // Determine the size of the basic block frequency table.
530 E : size_t data_size = module_data->num_entries * sizeof(uint32);
531 :
532 : // Determine the size of the basic block frequency record.
533 E : size_t record_size = sizeof(TraceIndexedFrequencyData) + data_size - 1;
534 :
535 : // Determine the size of the buffer we need. We need room for the basic block
536 : // frequency struct plus a single RecordPrefix header.
537 E : size_t segment_size = sizeof(RecordPrefix) + record_size;
538 :
539 : // Allocate the actual segment for the coverage data.
540 E : CHECK(session_.AllocateBuffer(segment_size, state->segment()));
541 :
542 : // Ensure it's big enough to allocate the basic-block frequency data
543 : // we want. This automatically accounts for the RecordPrefix overhead.
544 E : CHECK(state->segment()->CanAllocate(record_size));
545 :
546 : // Allocate the basic-block frequency data. We will leave this allocated and
547 : // let it get flushed during tear-down of the call-trace client.
548 : TraceIndexedFrequencyData* trace_data =
549 : reinterpret_cast<TraceIndexedFrequencyData*>(
550 : state->segment()->AllocateTraceRecordImpl(TRACE_INDEXED_FREQUENCY,
551 E : record_size));
552 E : DCHECK(trace_data != NULL);
553 :
554 : // Initialize the basic block frequency data struct.
555 E : HMODULE module = GetModuleForAddr(module_data);
556 E : CHECK(module != NULL);
557 E : const base::win::PEImage image(module);
558 E : const IMAGE_NT_HEADERS* nt_headers = image.GetNTHeaders();
559 E : trace_data->data_type = TraceIndexedFrequencyData::BASIC_BLOCK;
560 E : trace_data->module_base_addr = reinterpret_cast<ModuleAddr>(image.module());
561 E : trace_data->module_base_size = nt_headers->OptionalHeader.SizeOfImage;
562 E : trace_data->module_checksum = nt_headers->OptionalHeader.CheckSum;
563 E : trace_data->module_time_date_stamp = nt_headers->FileHeader.TimeDateStamp;
564 E : trace_data->frequency_size = sizeof(uint32);
565 E : trace_data->num_entries = module_data->num_entries;
566 :
567 : // Hook up the newly allocated buffer to the call-trace instrumentation.
568 E : state->set_frequency_data(trace_data->frequency_data);
569 :
570 E : return state;
571 E : }
572 :
573 : } // namespace coverage
574 : } // namespace agent
|