1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//
5// ProfDetach.cpp
6//
7
8//
9// Implementation of helper classes and structures used for Profiling API Detaching
10//
11// ======================================================================================
12
13#include "common.h"
14
15#ifdef FEATURE_PROFAPI_ATTACH_DETACH
16
17#include "profdetach.h"
18#include "profilinghelper.h"
19#include "profilinghelper.inl"
20#include "eetoprofinterfaceimpl.inl"
21
22// Class static member variables
23ProfilerDetachInfo ProfilingAPIDetach::s_profilerDetachInfo;
24CLREvent ProfilingAPIDetach::s_eventDetachWorkAvailable;
25
26
27// ---------------------------------------------------------------------------------------
28// ProfilerDetachInfo constructor
29//
30// Description:
31// Set every member variable to NULL or 0. They'll get initialized to real values
32// in ProfilingAPIDetach::RequestProfilerDetach.
33//
34
35ProfilerDetachInfo::ProfilerDetachInfo()
36{
37 // Executed during construction of a global object, therefore we cannot
38 // use real contracts, as this requires that utilcode has been initialized.
39 STATIC_CONTRACT_NOTHROW;
40 STATIC_CONTRACT_MODE_ANY;
41 STATIC_CONTRACT_GC_NOTRIGGER;
42 STATIC_CONTRACT_CANNOT_TAKE_LOCK;
43
44 Init();
45}
46
47void ProfilerDetachInfo::Init()
48{
49 // Executed during construction of a global object, therefore we cannot
50 // use real contracts, as this requires that utilcode has been initialized.
51 STATIC_CONTRACT_LEAF;
52
53 m_pEEToProf = NULL;
54 m_ui64DetachStartTime = 0;
55 m_dwExpectedCompletionMilliseconds = 0;
56}
57
58
59// ----------------------------------------------------------------------------
60// Implementation of ProfilingAPIAttachDetach statics
61
62
63// ----------------------------------------------------------------------------
64// ProfilingAPIDetach::Initialize
65//
66// Description:
67// Initialize static event
68
69// static
70HRESULT ProfilingAPIDetach::Initialize()
71{
72 CONTRACTL
73 {
74 NOTHROW;
75 MODE_ANY;
76 GC_TRIGGERS;
77 }
78 CONTRACTL_END;
79
80 if (!s_eventDetachWorkAvailable.IsValid())
81 {
82 HRESULT hr = S_OK;
83
84 EX_TRY
85 {
86 s_eventDetachWorkAvailable.CreateAutoEvent(FALSE);
87 }
88 EX_CATCH
89 {
90 hr = GET_EXCEPTION()->GetHR();
91 if (SUCCEEDED(hr))
92 {
93 // For exceptions that give us useless hr's, just use E_FAIL
94 hr = E_FAIL;
95 }
96 }
97 EX_END_CATCH(RethrowTerminalExceptions)
98
99 if (FAILED(hr))
100 {
101 return hr;
102 }
103 }
104
105 return S_OK;
106}
107
108
109
110// ----------------------------------------------------------------------------
111// ProfilingAPIDetach::RequestProfilerDetach
112//
113// Description:
114// Initialize ProfilerDetachInfo structures with parameters passed from
115// ICorProfilerInfo3::RequestProfilerDetach
116//
117// Arguments:
118// * dwExpectedCompletionMilliseconds - A hint to the CLR as to how long it should
119// wait before checking to see if execution has evacuated the profiler and all
120// profiler-instrumented code. If this is 0, the CLR will select a default.
121//
122// Notes:
123//
124// Invariants maintained by profiler:
125// * Before calling RequestProfilerDetach, the profiler must turn off all hijacking.
126// * If RequestProfilerDetach is called from a thread created by the CLR (i.e., from
127// within a callback), the profiler must first have exited all threads of its own
128// creation
129// * If RequestProfilerDetach is called from a thread of the profiler's own creation,
130// then
131// * The profiler must first have exited all OTHER threads of its own creation,
132// AND
133// * The profiler must immediately call FreeLibraryAndExitThread() after
134// RequestProfilerDetach returns.
135//
136// The above invariants result in the following possiblities:
137// * RequestProfilerDetach() may be called multi-threaded, but only from within
138// profiler callbacks. As such, evacuation counters will have been incremented
139// before entry into RequestProfilerDetach(), so the DetachThread will be
140// blocked until all such threads have returned from RequestProfilerDetach and
141// the callback from which RequestProfilerDetach was called. OR
142// * RequestProfilerDetach() is called single-threaded, from a thread of the
143// profiler's creation, which promises not to make any more calls into the CLR
144// afterward. In this case, the DetachThread will be blocked until
145// RequestProfilerDetach signals s_eventDetachWorkAvailable at the end.
146//
147
148// static
149HRESULT ProfilingAPIDetach::RequestProfilerDetach(DWORD dwExpectedCompletionMilliseconds)
150{
151 CONTRACTL
152 {
153 NOTHROW;
154 // Crst is used so GC may be triggered
155 GC_TRIGGERS;
156 MODE_ANY;
157 EE_THREAD_NOT_REQUIRED;
158 // Crst is used to synchronize the initialization of ProfilingAPIDetach internal structure
159 CAN_TAKE_LOCK;
160 PRECONDITION(ProfilingAPIUtility::GetStatusCrst() != NULL);
161 PRECONDITION(s_eventDetachWorkAvailable.IsValid());
162 }
163 CONTRACTL_END;
164
165 // Runtime must be fully started, or else CpuStoreBufferControl used below may not
166 // be initialized yet.
167 if (!g_fEEStarted)
168 {
169 return CORPROF_E_RUNTIME_UNINITIALIZED;
170 }
171
172 if (dwExpectedCompletionMilliseconds == 0)
173 {
174 // Pick suitable default if the profiler just leaves this at 0. 5 seconds is
175 // reasonable.
176 dwExpectedCompletionMilliseconds = 5000;
177 }
178
179 {
180 CRITSEC_Holder csh(ProfilingAPIUtility::GetStatusCrst());
181
182 // return immediately if detach is in progress
183
184 if (s_profilerDetachInfo.m_pEEToProf != NULL)
185 {
186 return CORPROF_E_PROFILER_DETACHING;
187 }
188
189 ProfilerStatus curProfStatus = g_profControlBlock.curProfStatus.Get();
190
191 if ((curProfStatus == kProfStatusInitializingForStartupLoad) ||
192 (curProfStatus == kProfStatusInitializingForAttachLoad))
193 {
194 return CORPROF_E_PROFILER_NOT_YET_INITIALIZED;
195 }
196
197 if (curProfStatus != kProfStatusActive)
198 {
199 // Before we acquired the lock, someone else must have unloaded the profiler
200 // for us (e.g., shutdown or the DetachThread in response to a prior
201 // RequestProfilerDetach call).
202 return CORPROF_E_PROFILER_DETACHING;
203 }
204
205 EEToProfInterfaceImpl * pEEToProf = g_profControlBlock.pProfInterface;
206
207 // Since prof status was active after entering the lock, the profiler must not
208 // have unloaded out from under us.
209 _ASSERTE(pEEToProf != NULL);
210
211 if (!pEEToProf->IsCallback3Supported())
212 {
213 return CORPROF_E_CALLBACK3_REQUIRED;
214 }
215
216 // Did the profiler do anything immutable? That will prevent us from allowing it to
217 // detach.
218 HRESULT hr = pEEToProf->EnsureProfilerDetachable();
219 if (FAILED(hr))
220 {
221 return hr;
222 }
223 s_profilerDetachInfo.m_pEEToProf = pEEToProf;
224 s_profilerDetachInfo.m_ui64DetachStartTime = CLRGetTickCount64();
225 s_profilerDetachInfo.m_dwExpectedCompletionMilliseconds = dwExpectedCompletionMilliseconds;
226
227 // Ok, time to seal the profiler from receiving or making calls with the CLR.
228 // (This will force a FlushStoreBuffers().)
229 g_profControlBlock.curProfStatus.Set(kProfStatusDetaching);
230 }
231
232 // Sealing done. Wake up the DetachThread so it can loop until the profiler code is
233 // fully evacuated off of all stacks.
234 if (!s_eventDetachWorkAvailable.Set())
235 {
236 return HRESULT_FROM_WIN32(GetLastError());
237 }
238
239 // FUTURE: Currently, kProfStatusDetaching prevents callbacks from being sent to the
240 // profiler AND prevents another profiler from attaching. In the future, when
241 // implementing the reattach-with-neutered-profilers feature crew, we may want to add
242 // another block here to call ProfilingAPIUtility::SetProfStatus(kProfStatusNone), so callbacks are
243 // prevented but a new profiler may attempt to attach.
244
245 EX_TRY
246 {
247 ProfilingAPIUtility::LogProfInfo(IDS_PROF_DETACH_INITIATED);
248 }
249 EX_CATCH
250 {
251 // Oh well, rest of detach succeeded, so we should still return success to the
252 // profiler.
253 }
254 EX_END_CATCH(RethrowTerminalExceptions);
255
256 return S_OK;
257}
258
259//---------------------------------------------------------------------------------------
260//
261// This is where the DetachThread spends its life. This waits until there's a profiler
262// to detach, then loops until the profiler code is completely evacuated off all stacks.
263// This will then unload the profiler.
264//
265
266// static
267void ProfilingAPIDetach::ExecuteEvacuationLoop()
268{
269 CONTRACTL
270 {
271 THROWS;
272 GC_TRIGGERS;
273 MODE_PREEMPTIVE;
274 CAN_TAKE_LOCK;
275 }
276 CONTRACTL_END;
277
278 // Wait until there's a profiler to detach (or until this thread should "wake up"
279 // for some other reason, such as exiting due to an unsuccessful startup-load of a
280 // profiler).
281 DWORD dwRet = s_eventDetachWorkAvailable.Wait(INFINITE, FALSE /* alertable */);
282 if (dwRet != WAIT_OBJECT_0)
283 {
284 // The wait ended due to a failure or a reason other than the event getting
285 // signaled (e.g., WAIT_ABANDONED)
286 DWORD dwErr;
287 if (dwRet == WAIT_FAILED)
288 {
289 dwErr = GetLastError();
290 LOG((
291 LF_CORPROF,
292 LL_ERROR,
293 "**PROF: DetachThread wait for s_eventDetachWorkAvailable failed with GetLastError = %d.\n",
294 dwErr));
295 }
296 else
297 {
298 dwErr = dwRet; // No extra error info available beyond the return code
299 LOG((
300 LF_CORPROF,
301 LL_ERROR,
302 "**PROF: DetachThread wait for s_eventDetachWorkAvailable terminated with %d.\n",
303 dwErr));
304 }
305
306 ProfilingAPIUtility::LogProfError(IDS_PROF_DETACH_THREAD_ERROR, dwErr);
307 return;
308 }
309
310 // Peek to make sure there's actually a profiler to detach
311 {
312 CRITSEC_Holder csh(ProfilingAPIUtility::GetStatusCrst());
313
314 if (s_profilerDetachInfo.m_pEEToProf == NULL)
315 {
316 // Nothing to detach. This can happen if the DetachThread (i.e., current
317 // thread) was created but then the profiler failed to load.
318 return;
319 }
320 }
321
322 do
323 {
324 // Give profiler a chance to return from its procs
325 SleepWhileProfilerEvacuates();
326 }
327 while (!IsProfilerEvacuated());
328
329 UnloadProfiler();
330}
331
332//---------------------------------------------------------------------------------------
333//
334// This is called in between evacuation counter checks. This calculates how long to
335// sleep, and then sleeps.
336//
337
338// static
339void ProfilingAPIDetach::SleepWhileProfilerEvacuates()
340{
341 CONTRACTL
342 {
343 THROWS;
344 GC_TRIGGERS;
345 MODE_PREEMPTIVE;
346 CAN_TAKE_LOCK;
347 }
348 CONTRACTL_END;
349
350 // Don't want to check evacuation any more frequently than every 300ms
351 const DWORD kdwDefaultMinSleepMs = 300;
352
353 // The default "steady state" max sleep is how long we'll wait if, after a couple
354 // tries the profiler still hasn't evacuated. Default to every 10 minutes
355 const DWORD kdwDefaultMaxSleepMs = 600000;
356
357 static DWORD s_dwMinSleepMs = 0;
358 static DWORD s_dwMaxSleepMs = 0;
359
360 // First time through, initialize the static min / max sleep times. Normally, we'll
361 // just use the constants above, but the user may customize these (within reason).
362
363 // They should either both be uninitialized or both initialized
364 _ASSERTE(
365 ((s_dwMinSleepMs == 0) && (s_dwMaxSleepMs == 0)) ||
366 ((s_dwMinSleepMs != 0) && (s_dwMaxSleepMs != 0)));
367
368 if (s_dwMaxSleepMs == 0)
369 {
370 // No race here, since only the DetachThread runs this code
371
372 s_dwMinSleepMs = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ProfAPI_DetachMinSleepMs);
373 s_dwMaxSleepMs = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ProfAPI_DetachMaxSleepMs);
374
375 // Here's the "within reason" part: the user may not customize these values to
376 // be more "extreme" than the constants, or to be 0 (which would confuse the
377 // issue of whether these statics were intialized yet).
378 if ((s_dwMinSleepMs < kdwDefaultMinSleepMs) || (s_dwMinSleepMs > kdwDefaultMaxSleepMs))
379 {
380 // Sleeping less than 300ms between evac checks could negatively affect the
381 // app by having the DetachThread execute too often. And a min sleep time
382 // that's too high could result in a profiler hanging around way too long
383 // when it's actually ready to be unloaded.
384 s_dwMinSleepMs = kdwDefaultMinSleepMs;
385 }
386 if ((s_dwMaxSleepMs < kdwDefaultMinSleepMs) || (s_dwMaxSleepMs > kdwDefaultMaxSleepMs))
387 {
388 // A steady state that's too small would retry the evac checks too often on
389 // an ongoing basis. A steady state that's too high could result in a
390 // profiler hanging around way too long when it's actually ready to be
391 // unloaded.
392 s_dwMaxSleepMs = kdwDefaultMaxSleepMs;
393 }
394 }
395
396 // Take note of when the detach was requested and how long to sleep for
397 ULONGLONG ui64ExpectedCompletionMilliseconds;
398 ULONGLONG ui64DetachStartTime;
399 {
400 CRITSEC_Holder csh(ProfilingAPIUtility::GetStatusCrst());
401
402 _ASSERTE(s_profilerDetachInfo.m_pEEToProf != NULL);
403 ui64ExpectedCompletionMilliseconds = s_profilerDetachInfo.m_dwExpectedCompletionMilliseconds;
404 ui64DetachStartTime = s_profilerDetachInfo.m_ui64DetachStartTime;
405 }
406
407 // ui64SleepMilliseconds is calculated to ensure that CLR checks evacuation status roughly:
408 // * After profiler's ui64ExpectedCompletionMilliseconds hint has elapsed (but not
409 // too soon)
410 // * At least once more after 2*ui64ExpectedCompletionMilliseconds have elapsed
411 // (but not too soon)
412 // * Occasionally thereafter (steady state)
413
414 ULONGLONG ui64ElapsedMilliseconds = CLRGetTickCount64() - ui64DetachStartTime;
415 ULONGLONG ui64SleepMilliseconds;
416 if (ui64ExpectedCompletionMilliseconds > ui64ElapsedMilliseconds)
417 {
418 // Haven't hit ui64ExpectedCompletionMilliseconds yet, so sleep for remainder
419 ui64SleepMilliseconds = ui64ExpectedCompletionMilliseconds - ui64ElapsedMilliseconds;
420 }
421 else if ((2*ui64ExpectedCompletionMilliseconds) > ui64ElapsedMilliseconds)
422 {
423 // We're between ui64ExpectedCompletionMilliseconds &
424 // 2*ui64ExpectedCompletionMilliseconds, so sleep until
425 // 2*ui64ExpectedCompletionMilliseconds have transpired
426 ui64SleepMilliseconds = (2*ui64ExpectedCompletionMilliseconds) - ui64ElapsedMilliseconds;
427 }
428 else
429 {
430 // Steady state
431 ui64SleepMilliseconds = s_dwMaxSleepMs;
432 }
433
434 // ...but keep it in bounds!
435 ui64SleepMilliseconds = min(
436 max(ui64SleepMilliseconds, s_dwMinSleepMs),
437 s_dwMaxSleepMs);
438
439 // At this point it's safe to cast ui64SleepMilliseconds down to a DWORD since we
440 // know it's between s_dwMinSleepMs & s_dwMaxSleepMs
441 _ASSERTE(ui64SleepMilliseconds <= 0xFFFFffff);
442 ClrSleepEx((DWORD) ui64SleepMilliseconds, FALSE /* alertable */);
443}
444
445//---------------------------------------------------------------------------------------
446//
447// Performs the evacuation checks by grabbing the thread store lock, iterating through
448// all EE Threads, and querying each one's evacuation counter. If they're all 0, the
449// profiler is ready to be unloaded.
450//
451// Return Value:
452// Nonzero iff the profiler is fully evacuated and ready to be unloaded.
453//
454
455// static
456BOOL ProfilingAPIDetach::IsProfilerEvacuated()
457{
458 CONTRACTL
459 {
460 NOTHROW;
461 GC_TRIGGERS;
462 MODE_ANY;
463 CAN_TAKE_LOCK;
464 }
465 CONTRACTL_END;
466
467 _ASSERTE(g_profControlBlock.curProfStatus.Get() == kProfStatusDetaching);
468
469 // Check evacuation counters on all the threads (see
470 // code:ProfilingAPIUtility::InitializeProfiling#LoadUnloadCallbackSynchronization
471 // for details). Doing this under the thread store lock not only ensures we can
472 // iterate through the Thread objects safely, but also forces us to serialize with
473 // the GC. The latter is important, as server GC enters the profiler on non-EE
474 // Threads, and so no evacuation counters might be incremented during server GC even
475 // though control could be entering the profiler.
476 {
477 ThreadStoreLockHolder TSLockHolder;
478
479 Thread * pThread = ThreadStore::GetAllThreadList(
480 NULL, // cursor thread; always NULL to begin with
481 0, // mask to AND with Thread::m_State to filter returned threads
482 0); // bits to match the result of the above AND. (m_State & 0 == 0,
483 // so we won't filter out any threads)
484
485 // Note that, by not filtering out any of the threads, we're intentionally including
486 // stuff like TS_Dead or TS_Unstarted. But that keeps us on the safe
487 // side. If an EE Thread object exists, we want to check its counters to be
488 // absolutely certain it isn't executing in a profiler.
489
490 while (pThread != NULL)
491 {
492 // Note that pThread is still in motion as we check its evacuation counter.
493 // This is ok, because we've already changed the profiler status to
494 // kProfStatusDetaching and flushed CPU buffers. So at this point the counter
495 // will typically only go down to 0 (and not increment anymore), with one
496 // small exception (below). So if we get a read of 0 below, the counter will
497 // typically stay there. Specifically:
498 // * pThread is most likely not about to increment its evacuation counter
499 // from 0 to 1 because pThread sees that the status is
500 // kProfStatusDetaching.
501 // * Note that there is a small race where pThread might actually
502 // increment its evac counter from 0 to 1 (if it dirty-read the
503 // profiler status a tad too early), but that implies that when
504 // pThread rechecks the profiler status (clean read) then pThread
505 // will immediately decrement the evac counter back to 0 and avoid
506 // calling into the EEToProfInterfaceImpl pointer.
507 //
508 // (see
509 // code:ProfilingAPIUtility::InitializeProfiling#LoadUnloadCallbackSynchronization
510 // for details)
511 DWORD dwEvacCounter = pThread->GetProfilerEvacuationCounter();
512 if (dwEvacCounter != 0)
513 {
514 LOG((
515 LF_CORPROF,
516 LL_INFO100,
517 "**PROF: Profiler not yet evacuated because OS Thread ID 0x%x has evac counter of %d (decimal).\n",
518 pThread->GetOSThreadId(),
519 dwEvacCounter));
520 return FALSE;
521 }
522
523 pThread = ThreadStore::GetAllThreadList(pThread, 0, 0);
524 }
525 }
526
527 // FUTURE: When rejit feature crew complete, add code to verify all rejitted
528 // functions are fully reverted and off of all stacks. If this is very easy to
529 // verify (e.g., checking a single value), consider putting it above the loop
530 // above so we can early-out quicker if rejitted code is still around.
531
532 // We got this far without returning, so the profiler is fully evacuated
533 return TRUE;
534}
535
536// ---------------------------------------------------------------------------------------
537// After we've verified a detaching profiler has fully evacuated, call this to unload the
538// profiler and clean up state.
539//
540// Assumptions:
541// Since this is called well after the profiler called RequestProfilerDetach, the
542// profiler must not have any other threads in use. Also, now that the profiler has
543// been evacuated, no CLR threads will be calling into the profiler (thus the
544// profiler will not gain control via CLR threads either). That means the profiler
545// may not call back into the CLR on any other threads.
546//
547
548// static
549void ProfilingAPIDetach::UnloadProfiler()
550{
551 CONTRACTL
552 {
553 THROWS;
554 GC_TRIGGERS;
555 MODE_ANY;
556 CAN_TAKE_LOCK;
557 }
558 CONTRACTL_END;
559
560 _ASSERTE(g_profControlBlock.curProfStatus.Get() == kProfStatusDetaching);
561
562 {
563 CRITSEC_Holder csh(ProfilingAPIUtility::GetStatusCrst());
564
565 // Notify profiler it's about to be unloaded
566 _ASSERTE(s_profilerDetachInfo.m_pEEToProf != NULL);
567 s_profilerDetachInfo.m_pEEToProf->ProfilerDetachSucceeded();
568
569 // Reset detach state.
570 s_profilerDetachInfo.Init();
571
572 // This deletes the EEToProfInterfaceImpl object managing the detaching profiler,
573 // releases the profiler's callback interfaces, unloads the profiler DLL, sets
574 // the status to kProfStatusNone, and resets g_profControlBlock for use next time
575 // a profiler tries to attach.
576 //
577 // Note that s_profilerDetachInfo.Init() has already NULL'd out
578 // s_profilerDetachInfo.m_pEEToProf, so we won't have a dangling pointer to the
579 // EEToProfInterfaceImpl that's about to be destroyed.
580 ProfilingAPIUtility::TerminateProfiling();
581 }
582
583 ProfilingAPIUtility::LogProfInfo(IDS_PROF_DETACH_COMPLETE);
584}
585
586// ----------------------------------------------------------------------------
587// ProfilingAPIDetach::ProfilingAPIDetachThreadStart
588//
589// Description:
590// Thread proc for DetachThread. Serves as a simple try/catch wrapper around a call to
591// ProfilingAPIDetach::ExecuteEvacuationLoop. This thread proc is specified by
592// code:ProfilingAPIDetach::CreateDetachThread when it spins up the new DetachThread.
593// This occurs when a profiler is either startup-loaded or attach-loaded.
594//
595// Arguments:
596// * LPVOID thread proc param is ignored
597//
598// Return Value:
599// Just returns 0 always.
600//
601
602// static
603DWORD WINAPI ProfilingAPIDetach::ProfilingAPIDetachThreadStart(LPVOID)
604{
605 CONTRACTL
606 {
607 NOTHROW;
608 GC_TRIGGERS;
609 MODE_PREEMPTIVE;
610 CAN_TAKE_LOCK;
611 }
612 CONTRACTL_END;
613
614 // At start of this thread, set its type so SOS !threads and anyone else knows who we
615 // are.
616 ClrFlsSetThreadType(ThreadType_ProfAPI_Detach);
617
618 LOG((
619 LF_CORPROF,
620 LL_INFO10,
621 "**PROF: DetachThread created and executing.\n"));
622
623 // This try block is a last-ditch stop-gap to prevent an unhandled exception on the
624 // DetachThread from bringing down the process. Note that if the unhandled
625 // exception is a terminal one, then hey, sure, let's tear everything down. Also
626 // note that any naughtiness in the profiler (e.g., throwing an exception from its
627 // Initialize callback) should already be handled before we pop back to here, so this
628 // is just being super paranoid.
629 EX_TRY
630 {
631 // Don't care about return value, thread proc will just return 0 regardless
632 ExecuteEvacuationLoop();
633 }
634 EX_CATCH
635 {
636 _ASSERTE(!"Unhandled exception on profiling API detach thread");
637 }
638 EX_END_CATCH(RethrowTerminalExceptions);
639
640 LOG((
641 LF_CORPROF,
642 LL_INFO10,
643 "**PROF: DetachThread exiting.\n"));
644
645 return 0;
646}
647
648// ---------------------------------------------------------------------------------------
649// Called during startup or attach load of a profiler to create a new thread to fill the role of
650// the DetachThread.
651//
652
653// static
654HRESULT ProfilingAPIDetach::CreateDetachThread()
655{
656 // This function is practically a leaf (though not quite), so keeping the contract
657 // strict to allow for maximum flexibility on when this may called.
658 CONTRACTL
659 {
660 NOTHROW;
661 GC_NOTRIGGER;
662 MODE_ANY;
663 CANNOT_TAKE_LOCK;
664 }
665 CONTRACTL_END;
666
667 // FUTURE: When reattach with neutered profilers is implemented, this
668 // function should check if a DetachThread already exists (use synchronization
669 // to prevent race), and just return if so.
670
671 HandleHolder hDetachThread;
672
673 // The DetachThread is intentionally not an EE Thread-object thread (it won't
674 // execute managed code).
675 hDetachThread = ::CreateThread(
676 NULL, // lpThreadAttributes; don't want child processes inheriting this handle
677 0, // dwStackSize (0 = use default)
678 ProfilingAPIDetachThreadStart,
679 NULL, // lpParameter (none to pass)
680 0, // dwCreationFlags (0 = use default flags, start thread immediately)
681 NULL // lpThreadId (don't need therad ID)
682 );
683 if (hDetachThread == NULL)
684 {
685 DWORD dwErr = GetLastError();
686
687 LOG((
688 LF_CORPROF,
689 LL_ERROR,
690 "**PROF: Failed to create DetachThread. GetLastError=%d.\n",
691 dwErr));
692
693 return HRESULT_FROM_WIN32(dwErr);
694 }
695
696 return S_OK;
697}
698
699//---------------------------------------------------------------------------------------
700//
701// Accessor for ProfilingAPIDetach::s_profilerDetachInfo.m_pEEToProf, which is the
702// profiler being detached (or NULL if no profiler is being detached).
703//
704// Return Value:
705// EEToProfInterfaceImpl * for the profiler being detached.
706//
707
708// static
709EEToProfInterfaceImpl * ProfilingAPIDetach::GetEEToProfPtr()
710{
711 LIMITED_METHOD_CONTRACT;
712 return s_profilerDetachInfo.m_pEEToProf;
713}
714
715#endif // FEATURE_PROFAPI_ATTACH_DETACH
716