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 : // Implementation of the profiler DLL.
16 : #include "syzygy/agent/profiler/profiler.h"
17 :
18 : #include <windows.h>
19 : #include <algorithm>
20 :
21 : #include "base/at_exit.h"
22 : #include "base/command_line.h"
23 : #include "base/environment.h"
24 : #include "base/file_path.h"
25 : #include "base/lazy_instance.h"
26 : #include "base/logging.h"
27 : #include "base/string_util.h"
28 : #include "base/utf_string_conversions.h"
29 : #include "base/memory/scoped_ptr.h"
30 : #include "base/win/pe_image.h"
31 : #include "base/win/scoped_handle.h"
32 : #include "syzygy/agent/common/dlist.h"
33 : #include "syzygy/agent/common/process_utils.h"
34 : #include "syzygy/agent/common/scoped_last_error_keeper.h"
35 : #include "syzygy/agent/profiler/return_thunk_factory.h"
36 : #include "syzygy/common/logging.h"
37 : #include "syzygy/trace/client/client_utils.h"
38 : #include "syzygy/trace/protocol/call_trace_defs.h"
39 :
40 : namespace {
41 :
42 : using agent::common::ScopedLastErrorKeeper;
43 :
44 : // All tracing runs through this object.
45 : base::LazyInstance<agent::profiler::Profiler> static_profiler_instance =
46 : LAZY_INSTANCE_INITIALIZER;
47 :
48 : typedef std::pair<RetAddr, FuncAddr> InvocationKey;
49 :
50 : class HashInvocationKey {
51 : public:
52 : static const size_t bucket_size = 4;
53 : static const size_t min_buckets = 8;
54 :
55 E : size_t operator()(const InvocationKey& key) const {
56 : return reinterpret_cast<size_t>(key.first) ^
57 E : reinterpret_cast<size_t>(key.second);
58 E : }
59 :
60 E : bool operator()(const InvocationKey& a, const InvocationKey& b) const {
61 E : return a < b;
62 E : }
63 : };
64 : typedef base::hash_map<
65 : InvocationKey, InvocationInfo*, HashInvocationKey> InvocationMap;
66 :
67 : // Accessing a module acquired from process iteration calls is inherently racy,
68 : // as we don't hold any kind of reference to the module, and so the module
69 : // could be unloaded while we're accessing it. In practice this shouldn't
70 : // happen to us, as we'll be running under the loader's lock in all cases.
71 : bool CaptureModuleInformation(const base::win::PEImage& image,
72 : TraceModuleData* module_event) {
73 : __try {
74 : // Populate the log record.
75 : module_event->module_base_size =
76 : image.GetNTHeaders()->OptionalHeader.SizeOfImage;
77 : module_event->module_checksum =
78 : image.GetNTHeaders()->OptionalHeader.CheckSum;
79 : module_event->module_time_date_stamp =
80 : image.GetNTHeaders()->FileHeader.TimeDateStamp;
81 : } __except(EXCEPTION_EXECUTE_HANDLER) {
82 : return false;
83 : }
84 :
85 : return true;
86 : }
87 :
88 : // The information on how to set the thread name comes from
89 : // a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
90 : const DWORD kVCThreadNameException = 0x406D1388;
91 :
92 : typedef struct tagTHREADNAME_INFO {
93 : DWORD dwType; // Must be 0x1000.
94 : LPCSTR szName; // Pointer to name (in user addr space).
95 : DWORD dwThreadID; // Thread ID (-1=caller thread).
96 : DWORD dwFlags; // Reserved for future use, must be zero.
97 : } THREADNAME_INFO;
98 :
99 : } // namespace
100 :
101 : // See client.cc for a description of the unconventional
102 : // calling conventions for this function.
103 E : extern "C" void __declspec(naked) _indirect_penter() {
104 : __asm {
105 : // Stash volatile registers.
106 E : push eax
107 E : push edx
108 :
109 : // Get the current cycle time ASAP.
110 E : rdtsc
111 :
112 E : push ecx
113 E : pushfd
114 :
115 : // Push the cycle time arg.
116 E : push edx
117 E : push eax
118 :
119 : // Retrieve the original function address, pushed by our caller.
120 E : mov eax, DWORD PTR[esp + 0x18]
121 E : push eax
122 :
123 : // Calculate the position of the return address on stack, and
124 : // push it. This becomes the EntryFrame argument.
125 E : lea eax, DWORD PTR[esp + 0x20]
126 E : push eax
127 E : call agent::profiler::Profiler::FunctionEntryHook
128 :
129 : // Restore volatile registers.
130 E : popfd
131 E : pop ecx
132 E : pop edx
133 E : pop eax
134 :
135 : // Return to the address pushed by our caller.
136 E : ret
137 : }
138 : }
139 :
140 E : extern "C" void __declspec(naked) _indirect_penter_dllmain() {
141 : __asm {
142 : // Stash volatile registers.
143 E : push eax
144 E : push edx
145 :
146 : // Get the current cycle time ASAP.
147 E : rdtsc
148 :
149 E : push ecx
150 E : pushfd
151 :
152 : // Push the cycle time arg.
153 E : push edx
154 E : push eax
155 :
156 : // Retrieve the address pushed by our caller.
157 E : mov eax, DWORD PTR[esp + 0x18]
158 E : push eax
159 :
160 : // Calculate the position of the return address on stack, and
161 : // push it. This becomes the EntryFrame argument.
162 E : lea eax, DWORD PTR[esp + 0x20]
163 E : push eax
164 E : call agent::profiler::Profiler::DllMainEntryHook
165 :
166 : // Restore volatile registers.
167 E : popfd
168 E : pop ecx
169 E : pop edx
170 E : pop eax
171 :
172 : // Return to the address pushed by our caller.
173 E : ret
174 : }
175 : }
176 :
177 : // On entry, pc_location should point to a location on our own stack.
178 : extern "C" uintptr_t __cdecl ResolveReturnAddressLocation(
179 E : uintptr_t pc_location) {
180 : using agent::profiler::Profiler;
181 E : Profiler* profiler = Profiler::Instance();
182 : return reinterpret_cast<uintptr_t>(
183 : profiler->ResolveReturnAddressLocation(
184 E : reinterpret_cast<RetAddr*>(pc_location)));
185 E : }
186 :
187 E : BOOL WINAPI DllMain(HMODULE instance, DWORD reason, LPVOID reserved) {
188 : using agent::profiler::Profiler;
189 :
190 : // Our AtExit manager required by base.
191 : static base::AtExitManager* at_exit = NULL;
192 :
193 E : switch (reason) {
194 : case DLL_PROCESS_ATTACH:
195 E : DCHECK(at_exit == NULL);
196 E : at_exit = new base::AtExitManager();
197 :
198 E : CommandLine::Init(0, NULL);
199 E : common::InitLoggingForDll(L"profiler");
200 E : break;
201 :
202 : case DLL_THREAD_DETACH:
203 E : Profiler::Instance()->OnThreadDetach();
204 E : break;
205 :
206 : case DLL_PROCESS_DETACH:
207 E : DCHECK(at_exit != NULL);
208 E : delete at_exit;
209 E : at_exit = NULL;
210 : break;
211 :
212 : default:
213 : break;
214 : }
215 :
216 E : return TRUE;
217 E : }
218 :
219 : namespace agent {
220 : namespace profiler {
221 :
222 : class Profiler::ThreadState
223 : : public ReturnThunkFactoryImpl<Profiler::ThreadState>,
224 : public agent::common::ThreadStateBase {
225 : public:
226 : explicit ThreadState(Profiler* profiler);
227 : ~ThreadState();
228 :
229 : // Logs @p module and all other modules in the process, then flushes
230 : // the current trace buffer.
231 : void LogAllModules(HMODULE module);
232 :
233 : // Logs @p module.
234 : void LogModule(HMODULE module);
235 :
236 : // Logs @p thread_name as the current thread's name.
237 : void LogThreadName(const base::StringPiece& thread_name);
238 :
239 : // Processes a single function entry.
240 : void OnFunctionEntry(EntryFrame* entry_frame,
241 : FuncAddr function,
242 : uint64 cycles);
243 :
244 : // @name Callback notification implementation.
245 : // @{
246 : virtual void OnPageAdded(const void* page) OVERRIDE;
247 : virtual void OnPageRemoved(const void* page) OVERRIDE;
248 : // @}
249 :
250 : // Function exit hook.
251 : void OnFunctionExit(const ThunkData* data, uint64 cycles_exit);
252 :
253 E : trace::client::TraceFileSegment* segment() { return &segment_; }
254 :
255 : private:
256 : friend class Profiler;
257 :
258 : void RecordInvocation(RetAddr caller,
259 : FuncAddr function,
260 : uint64 cycles);
261 :
262 : void UpdateOverhead(uint64 entry_cycles);
263 : InvocationInfo* AllocateInvocationInfo();
264 : bool FlushSegment();
265 :
266 : // The profiler we're attached to.
267 : Profiler* profiler_;
268 :
269 : // We keep a running tally of the rough amount of wall clock cycles spent
270 : // inside the profiler. We then subtract the profiler's overhead from the
271 : // wall clock cycle timer on each measurement. This results in a timer that
272 : // measures time exclusive of profiling overhead.
273 : uint64 cycles_overhead_;
274 :
275 : // The invocations we've recorded in our buffer.
276 : InvocationMap invocations_;
277 :
278 : // The trace file segment we're recording to.
279 : trace::client::TraceFileSegment segment_;
280 :
281 : // The current batch record we're writing to, if any.
282 : TraceBatchInvocationInfo* batch_;
283 :
284 : // The set of modules we've logged.
285 : ModuleSet logged_modules_;
286 : };
287 :
288 : Profiler::ThreadState::ThreadState(Profiler* profiler)
289 : : profiler_(profiler),
290 : cycles_overhead_(0LL),
291 E : batch_(NULL) {
292 E : Initialize();
293 E : }
294 :
295 E : Profiler::ThreadState::~ThreadState() {
296 E : batch_ = NULL;
297 E : invocations_.clear();
298 :
299 : // If we have an outstanding buffer, let's deallocate it now.
300 E : if (segment_.write_ptr != NULL)
301 E : profiler_->session_.ReturnBuffer(&segment_);
302 :
303 E : Uninitialize();
304 E : }
305 :
306 E : void Profiler::ThreadState::LogAllModules(HMODULE module) {
307 : // Bail early if we're disabled.
308 E : if (profiler_->session_.IsDisabled())
309 E : return;
310 :
311 E : agent::common::ModuleVector modules;
312 E : agent::common::GetProcessModules(&modules);
313 :
314 : // Our module should be in the process modules.
315 E : DCHECK(std::find(modules.begin(), modules.end(), module) != modules.end());
316 :
317 E : for (size_t i = 0; i < modules.size(); ++i) {
318 E : DCHECK(modules[i] != NULL);
319 E : LogModule(modules[i]);
320 E : }
321 :
322 : // We need to flush module events right away, so that the module is
323 : // defined in the trace file before events using that module start to
324 : // occur (in another thread).
325 E : FlushSegment();
326 E : }
327 :
328 E : void Profiler::ThreadState::LogModule(HMODULE module) {
329 E : batch_ = NULL;
330 E : agent::common::LogModule(module, &profiler_->session_, &segment_);
331 E : }
332 :
333 : void Profiler::ThreadState::LogThreadName(
334 E : const base::StringPiece& thread_name) {
335 E : if (thread_name.empty())
336 i : return;
337 :
338 : // Make sure the event we're about to write will fit.
339 E : if (!segment_.CanAllocate(thread_name.size() + 1) || !FlushSegment()) {
340 : // Failed to allocate a new segment.
341 i : return;
342 : }
343 :
344 E : DCHECK(segment_.CanAllocate(thread_name.size() + 1));
345 E : batch_ = NULL;
346 :
347 : // Allocate a record in the log.
348 : TraceThreadNameInfo* thread_name_event =
349 : reinterpret_cast<TraceThreadNameInfo*>(
350 : segment_.AllocateTraceRecordImpl(
351 E : TRACE_THREAD_NAME, thread_name.size() + 1));
352 E : DCHECK(thread_name_event != NULL);
353 : base::strlcpy(thread_name_event->thread_name,
354 E : thread_name.data(), thread_name.size() + 1);
355 E : }
356 :
357 : void Profiler::ThreadState::OnFunctionEntry(EntryFrame* entry_frame,
358 : FuncAddr function,
359 E : uint64 cycles) {
360 E : if (profiler_->session_.IsDisabled())
361 E : return;
362 :
363 : // Record the details of the entry.
364 : // Note that on tail-recursion and tail-call elimination, the caller recorded
365 : // here will be a thunk. We cater for this case on exit as best we can.
366 E : ThunkData* data = MakeThunk(entry_frame->retaddr);
367 E : DCHECK(data != NULL);
368 E : data->caller = entry_frame->retaddr;
369 E : data->function = function;
370 E : data->cycles_entry = cycles - cycles_overhead_;
371 :
372 E : entry_frame->retaddr = data->thunk;
373 :
374 E : UpdateOverhead(cycles);
375 E : }
376 :
377 : void Profiler::ThreadState::OnFunctionExit(const ThunkData* data,
378 E : uint64 cycles_exit) {
379 : // Calculate the number of cycles in the invocation, exclusive our overhead.
380 E : uint64 cycles_executed = cycles_exit - cycles_overhead_ - data->cycles_entry;
381 :
382 : // See if the return address resolves to a data, which indicates
383 : // tail recursion or tail call elimination. In that case we record the
384 : // calling function as caller, which isn't totally accurate as that'll
385 : // attribute the cost to the first line of the calling function. In the
386 : // absence of more information, it's the best we can do, however.
387 E : Thunk* ret_thunk = CastToThunk(data->caller);
388 E : if (ret_thunk == NULL) {
389 E : RecordInvocation(data->caller, data->function, cycles_executed);
390 E : } else {
391 E : ThunkData* ret_data = DataFromThunk(ret_thunk);
392 E : RecordInvocation(ret_data->function, data->function, cycles_executed);
393 : }
394 :
395 E : UpdateOverhead(cycles_exit);
396 E : }
397 :
398 E : void Profiler::ThreadState::OnPageAdded(const void* page) {
399 E : profiler_->OnPageAdded(page);
400 E : }
401 :
402 E : void Profiler::ThreadState::OnPageRemoved(const void* page) {
403 E : profiler_->OnPageRemoved(page);
404 E : }
405 :
406 : void Profiler::ThreadState::RecordInvocation(RetAddr caller,
407 : FuncAddr function,
408 E : uint64 duration_cycles) {
409 : // See whether we've already recorded an entry for this function.
410 E : InvocationKey key(caller, function);
411 E : InvocationMap::iterator it = invocations_.find(key);
412 E : if (it != invocations_.end()) {
413 : // Yup, we already have an entry. Tally the new data.
414 E : InvocationInfo* info = it->second;
415 E : ++(info->num_calls);
416 E : info->cycles_sum += duration_cycles;
417 E : if (duration_cycles < info->cycles_min) {
418 E : info->cycles_min = duration_cycles;
419 E : } else if (duration_cycles > info->cycles_max) {
420 E : info->cycles_max = duration_cycles;
421 : }
422 E : } else {
423 : // The allocation below may touch last error.
424 E : ScopedLastErrorKeeper keep_last_error;
425 :
426 : // Nopes, allocate a new entry for this invocation.
427 E : InvocationInfo* info = AllocateInvocationInfo();
428 E : if (info != NULL) {
429 E : invocations_[key] = info;
430 E : info->caller = caller;
431 E : info->function = function;
432 E : info->num_calls = 1;
433 E : info->cycles_min = info->cycles_max = info->cycles_sum = duration_cycles;
434 : }
435 E : }
436 E : }
437 :
438 E : void Profiler::ThreadState::UpdateOverhead(uint64 entry_cycles) {
439 : // TODO(siggi): Measure the fixed overhead on setup,
440 : // then add it on every update.
441 E : cycles_overhead_ += (__rdtsc() - entry_cycles);
442 E : }
443 :
444 E : InvocationInfo* Profiler::ThreadState::AllocateInvocationInfo() {
445 : // This is kind of self-evident for the moment, as an invocation info batch
446 : // contains at least one invocation info as currently declared.
447 : // If this fails, please recondsider your implementation, or else revisit
448 : // the allocation code below.
449 : COMPILE_ASSERT(sizeof(TraceBatchInvocationInfo) >= sizeof(InvocationInfo),
450 : invocation_info_batch_must_be_larger_than_invocation_info);
451 :
452 : // Do we have a record that we can grow?
453 E : if (batch_ != NULL && segment_.CanAllocateRaw(sizeof(InvocationInfo))) {
454 : InvocationInfo* invocation_info =
455 E : reinterpret_cast<InvocationInfo*>(segment_.write_ptr);
456 E : RecordPrefix* prefix = trace::client::GetRecordPrefix(batch_);
457 E : prefix->size += sizeof(InvocationInfo);
458 :
459 : // Update the book-keeping.
460 E : segment_.write_ptr += sizeof(InvocationInfo);
461 E : segment_.header->segment_length += sizeof(InvocationInfo);
462 :
463 E : return invocation_info;
464 : }
465 :
466 : // Do we need to scarf a new buffer?
467 : if (!segment_.CanAllocate(sizeof(TraceBatchInvocationInfo)) &&
468 E : !FlushSegment()) {
469 : // We failed to allocate a new buffer.
470 i : return NULL;
471 : }
472 :
473 E : DCHECK(segment_.header != NULL);
474 :
475 E : batch_ = segment_.AllocateTraceRecord<TraceBatchInvocationInfo>();
476 E : return &batch_->invocations[0];
477 E : }
478 :
479 E : bool Profiler::ThreadState::FlushSegment() {
480 E : batch_ = NULL;
481 E : invocations_.clear();
482 :
483 E : return profiler_->session_.ExchangeBuffer(&segment_);
484 E : }
485 :
486 E : void Profiler::OnThreadDetach() {
487 E : ThreadState* state = GetThreadState();
488 E : if (state != NULL)
489 E : thread_state_manager_.MarkForDeath(state);
490 E : }
491 :
492 E : RetAddr* Profiler::ResolveReturnAddressLocation(RetAddr* pc_location) {
493 E : base::AutoLock lock(lock_);
494 :
495 : // In case of tail-call and tail recursion elimination, we can get chained
496 : // thunks, so we loop around here until we resolve to a non-thunk.
497 E : while (true) {
498 : // See whether the return address is one of our thunks.
499 E : RetAddr ret_addr = *pc_location;
500 :
501 : // Compute the page this return address lives in.
502 : const void* page = reinterpret_cast<const void*>(
503 E : reinterpret_cast<uintptr_t>(ret_addr) & ~0xFFF);
504 E : if (!std::binary_search(pages_.begin(), pages_.end(), page))
505 E : return pc_location;
506 :
507 : // It's one of our own, redirect to the thunk's stash.
508 : ThreadState::Thunk* thunk =
509 E : reinterpret_cast<ThreadState::Thunk*>(const_cast<void*>(ret_addr));
510 :
511 E : ThreadState::ThunkData* data = ThreadState::DataFromThunk(thunk);
512 :
513 : // Update the PC location and go around again, in case this
514 : // thunk links to another one.
515 E : pc_location = &data->caller;
516 E : }
517 E : }
518 :
519 : void Profiler::OnModuleEntry(EntryFrame* entry_frame,
520 : FuncAddr function,
521 E : uint64 cycles) {
522 : // The function invoked has a DllMain-like signature.
523 : // Get the module and reason from its invocation record.
524 E : HMODULE module = reinterpret_cast<HMODULE>(entry_frame->args[0]);
525 E : DWORD reason = entry_frame->args[1];
526 :
527 : // Only log module additions.
528 E : bool should_log_module = false;
529 E : switch (reason) {
530 : case DLL_PROCESS_ATTACH:
531 : case DLL_THREAD_ATTACH:
532 E : should_log_module = true;
533 E : break;
534 :
535 : case DLL_PROCESS_DETACH:
536 : case DLL_THREAD_DETACH:
537 i : break;
538 :
539 : default:
540 i : LOG(WARNING) << "Unrecognized module event: " << reason << ".";
541 : break;
542 : }
543 :
544 : // Make sure we only log each module once per process.
545 E : bool is_new_module = false;
546 E : if (should_log_module) {
547 E : base::AutoLock lock(lock_);
548 :
549 E : is_new_module = logged_modules_.insert(module).second;
550 E : }
551 :
552 E : ThreadState* data = GetOrAllocateThreadState();
553 E : DCHECK(data != NULL);
554 E : if (data == NULL)
555 i : return;
556 :
557 E : if (is_new_module) {
558 : // Delegate the logging to our per-thread data.
559 E : data->LogAllModules(module);
560 : }
561 :
562 : // Handle the function entry.
563 E : data->OnFunctionEntry(entry_frame, function, cycles);
564 E : }
565 :
566 E : void Profiler::OnPageAdded(const void* page) {
567 E : base::AutoLock lock(lock_);
568 :
569 : PageVector::iterator it =
570 E : std::lower_bound(pages_.begin(), pages_.end(), page);
571 E : DCHECK(it == pages_.end() || *it != page);
572 E : pages_.insert(it, page);
573 E : }
574 :
575 E : void Profiler::OnPageRemoved(const void* page) {
576 E : base::AutoLock lock(lock_);
577 :
578 : PageVector::iterator it =
579 E : std::lower_bound(pages_.begin(), pages_.end(), page);
580 : // The page must be in our list.
581 E : DCHECK(it != pages_.end());
582 E : DCHECK_EQ(page, *it);
583 E : pages_.erase(it);
584 E : }
585 :
586 E : void Profiler::OnThreadName(const base::StringPiece& thread_name) {
587 E : ThreadState* state = GetOrAllocateThreadState();
588 E : if (state != NULL)
589 E : state->LogThreadName(thread_name);
590 E : }
591 :
592 E : LONG CALLBACK Profiler::ExceptionHandler(EXCEPTION_POINTERS* ex_info) {
593 : // Log the thread if this is the VC thread name exception.
594 : if (ex_info->ExceptionRecord->ExceptionCode == kVCThreadNameException &&
595 : ex_info->ExceptionRecord->NumberParameters ==
596 E : sizeof(THREADNAME_INFO)/sizeof(DWORD)) {
597 : const THREADNAME_INFO* info =
598 : reinterpret_cast<const THREADNAME_INFO*>(
599 E : &ex_info->ExceptionRecord->ExceptionInformation);
600 :
601 E : if (info->dwType == 0x1000) {
602 E : Profiler* instance = Profiler::Instance();
603 E : if (instance != NULL)
604 E : instance->OnThreadName(info->szName);
605 E : } else {
606 i : LOG(WARNING) << "Unrecognised event type " << info->dwType;
607 : }
608 : }
609 :
610 E : return EXCEPTION_CONTINUE_SEARCH;
611 E : }
612 :
613 :
614 E : Profiler* Profiler::Instance() {
615 E : return static_profiler_instance.Pointer();
616 E : }
617 :
618 E : Profiler::Profiler() : handler_registration_(NULL) {
619 : // Create our RPC session and allocate our initial trace segment on first use.
620 E : ThreadState* data = CreateFirstThreadStateAndSession();
621 E : CHECK(data != NULL) << "Failed to allocate thread local state.";
622 :
623 E : handler_registration_ = ::AddVectoredExceptionHandler(TRUE, ExceptionHandler);
624 E : }
625 :
626 E : Profiler::~Profiler() {
627 : // Typically, this will happen on the last thread in the process. We must
628 : // explicitly clean up this thread's state as it will otherwise leak.
629 E : FreeThreadState();
630 :
631 : // Unregister our VEH.
632 E : if (handler_registration_ != NULL) {
633 E : ::RemoveVectoredExceptionHandler(handler_registration_);
634 E : handler_registration_ = NULL;
635 : }
636 E : }
637 :
638 E : Profiler::ThreadState* Profiler::CreateFirstThreadStateAndSession() {
639 E : Profiler::ThreadState* data = GetOrAllocateThreadStateImpl();
640 :
641 : // Create the session (and allocate the first segment).
642 E : scoped_ptr<base::Environment> env(base::Environment::Create());
643 E : std::string id;
644 E : env->GetVar(::kSyzygyRpcInstanceIdEnvVar, &id);
645 E : session_.set_instance_id(UTF8ToWide(id));
646 E : session_.CreateSession(data->segment());
647 :
648 E : return data;
649 E : }
650 :
651 E : Profiler::ThreadState* Profiler::GetOrAllocateThreadState() {
652 E : Profiler::ThreadState* data = GetOrAllocateThreadStateImpl();
653 E : if (!data->segment()->write_ptr && session_.IsTracing()) {
654 E : session_.AllocateBuffer(data->segment());
655 : }
656 E : return data;
657 E : }
658 :
659 E : Profiler::ThreadState* Profiler::GetOrAllocateThreadStateImpl() {
660 E : ThreadState *data = tls_.Get();
661 E : if (data != NULL)
662 E : return data;
663 :
664 E : data = new ThreadState(this);
665 E : if (data == NULL) {
666 i : LOG(ERROR) << "Unable to allocate per-thread data";
667 i : return NULL;
668 : }
669 :
670 E : thread_state_manager_.Register(data);
671 E : tls_.Set(data);
672 :
673 E : return data;
674 E : }
675 :
676 E : Profiler::ThreadState* Profiler::GetThreadState() const {
677 E : return tls_.Get();
678 E : }
679 :
680 E : void Profiler::FreeThreadState() {
681 E : ThreadState *data = GetThreadState();
682 E : if (data != NULL) {
683 E : tls_.Set(NULL);
684 E : thread_state_manager_.Unregister(data);
685 E : delete data;
686 : }
687 E : }
688 :
689 : void WINAPI Profiler::DllMainEntryHook(EntryFrame* entry_frame,
690 : FuncAddr function,
691 E : uint64 cycles) {
692 E : ScopedLastErrorKeeper keep_last_error;
693 :
694 E : Profiler* profiler = Profiler::Instance();
695 E : profiler->OnModuleEntry(entry_frame, function, cycles);
696 E : }
697 :
698 : void WINAPI Profiler::FunctionEntryHook(EntryFrame* entry_frame,
699 : FuncAddr function,
700 E : uint64 cycles) {
701 E : ScopedLastErrorKeeper keep_last_error;
702 :
703 E : Profiler* profiler = Profiler::Instance();
704 E : ThreadState* data = profiler->GetOrAllocateThreadState();
705 E : DCHECK(data != NULL);
706 E : if (data != NULL)
707 E : data->OnFunctionEntry(entry_frame, function, cycles);
708 E : }
709 :
710 : } // namespace profiler
711 : } // namespace agent
|