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
6//
7
8#include "common.h"
9#include "testhookmgr.h"
10#include "appdomain.hpp"
11#include "appdomain.inl"
12#include "finalizerthread.h"
13
14#ifdef FEATURE_TESTHOOKS
15CLRTestHookManager* CLRTestHookManager::g_pManager=NULL;
16CLRTestHookManager::~CLRTestHookManager()
17{
18
19}
20
21HRESULT CLRTestHookManager::AddTestHook(ICLRTestHook* hook)
22{
23 WRAPPER_NO_CONTRACT;
24 DWORD newidx=FastInterlockIncrement(&m_nHooks);
25 if (newidx>=NumItems(m_pHooks))
26 {
27 FastInterlockDecrement(&m_nHooks);
28 return DISP_E_OVERFLOW;
29 }
30 m_pHooks[newidx-1].Set(hook);
31 return S_OK;
32}
33
34
35ICLRTestHookManager* CLRTestHookManager::Start()
36{
37 LIMITED_METHOD_CONTRACT;
38 if (g_pManager==NULL)
39 {
40 CLRTestHookManager* newman=new (nothrow)CLRTestHookManager();
41 if (newman!=NULL && FastInterlockCompareExchangePointer(&g_pManager, newman, 0)!=0)
42 delete newman;
43 }
44 if(g_pManager)
45 g_pManager->AddRef();
46 return g_pManager;
47}
48
49CLRTestHookManager::CLRTestHookManager()
50{
51 WRAPPER_NO_CONTRACT;
52 m_nHooks=0;
53 m_cRef=1;
54 ZeroMemory(m_pHooks,sizeof(m_pHooks));
55}
56
57HRESULT CLRTestHookManager::AppDomainStageChanged(DWORD adid,DWORD oldstage,DWORD newstage)
58{
59 STATIC_CONTRACT_NOTHROW;
60
61 struct Param
62 {
63 CLRTestHookManager *pThis;
64 DWORD adid;
65 DWORD oldstage;
66 DWORD newstage;
67 } param;
68 param.pThis = this;
69 param.adid = adid;
70 param.oldstage = oldstage;
71 param.newstage = newstage;
72
73 PAL_TRY(Param *, pParam, &param)
74 {
75 //ignores the returned codes
76 for (LONG i = 0; i < pParam->pThis->m_nHooks; i++)
77 {
78 ICLRTestHook* hook = pParam->pThis->m_pHooks[i].v1();
79 if(hook)
80 {
81 HRESULT hr=hook->AppDomainStageChanged(pParam->adid, pParam->oldstage, pParam->newstage);
82 _ASSERTE(SUCCEEDED(hr));
83 }
84 }
85 }
86 PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
87 {
88 _ASSERTE(!"Test Hook threw an exception.");
89 }
90 PAL_ENDTRY;
91
92 return S_OK;
93};
94
95
96HRESULT CLRTestHookManager::NextFileLoadLevel(DWORD adid, LPVOID domainfile,DWORD newlevel)
97{
98 STATIC_CONTRACT_THROWS;
99 HRESULT hr=S_OK;
100 {
101 for (LONG i=0;i<m_nHooks;i++)
102 {
103 ICLRTestHook* hook=m_pHooks[i].v1();
104 if(hook)
105 {
106 HRESULT hr2=hook->NextFileLoadLevel( adid, domainfile, newlevel);
107 _ASSERTE(SUCCEEDED(hr)||SUCCEEDED(hr2));
108 if (SUCCEEDED(hr))
109 hr=hr2;
110 }
111 }
112 }
113 IfFailThrow(hr);
114 return hr;
115}
116
117HRESULT CLRTestHookManager::CompletingFileLoadLevel(DWORD adid, LPVOID domainfile,DWORD newlevel)
118{
119 STATIC_CONTRACT_THROWS;
120 HRESULT hr=S_OK;
121 {
122 for (LONG i=0;i<m_nHooks;i++)
123 {
124 ICLRTestHook* hook=m_pHooks[i].v1();
125 if(hook)
126 {
127 HRESULT hr2=hook->CompletingFileLoadLevel( adid, domainfile, newlevel);
128 _ASSERTE(SUCCEEDED(hr)||SUCCEEDED(hr2));
129 if (SUCCEEDED(hr))
130 hr=hr2;
131 }
132 }
133 }
134
135
136 IfFailThrow(hr);
137 return hr;
138}
139
140HRESULT CLRTestHookManager::CompletedFileLoadLevel(DWORD adid, LPVOID domainfile,DWORD newlevel)
141{
142 STATIC_CONTRACT_NOTHROW;
143
144 struct Param
145 {
146 CLRTestHookManager *pThis;
147 DWORD adid;
148 LPVOID domainfile;
149 DWORD newlevel;
150 } param;
151 param.pThis = this;
152 param.adid = adid;
153 param.domainfile = domainfile;
154 param.newlevel = newlevel;
155
156 PAL_TRY(Param *, pParam, &param)
157 {
158 //ignores the returned codes
159 for (LONG i = 0; i < pParam->pThis->m_nHooks; i++)
160 {
161 ICLRTestHook* hook = pParam->pThis->m_pHooks[i].v1();
162 if(hook)
163 {
164 HRESULT hr=hook->CompletedFileLoadLevel(pParam->adid, pParam->domainfile, pParam->newlevel);
165 _ASSERTE(SUCCEEDED(hr));
166 }
167 }
168 }
169 PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
170 {
171 _ASSERTE(!"Test Hook threw an exception.");
172 }
173 PAL_ENDTRY
174
175 return S_OK;
176}
177
178HRESULT CLRTestHookManager::EnteringAppDomain(DWORD adid)
179{
180 STATIC_CONTRACT_THROWS;
181 HRESULT hr=S_OK;
182 {
183 for (LONG i=0;i<m_nHooks;i++)
184 {
185 ICLRTestHook* hook=m_pHooks[i].v1();
186 if(hook)
187 {
188 HRESULT hr2=hook->EnteringAppDomain(adid);
189 _ASSERTE(SUCCEEDED(hr)||SUCCEEDED(hr2));
190 if (SUCCEEDED(hr))
191 hr=hr2;
192 }
193 }
194 }
195 IfFailThrow(hr);
196 return hr;
197}
198
199HRESULT CLRTestHookManager::EnteredAppDomain(DWORD adid)
200{
201 STATIC_CONTRACT_THROWS;
202 HRESULT hr=S_OK;
203 {
204 for (LONG i=0;i<m_nHooks;i++)
205 {
206 ICLRTestHook* hook=m_pHooks[i].v1();
207 if(hook)
208 {
209 HRESULT hr2=hook->EnteredAppDomain(adid);
210 _ASSERTE(SUCCEEDED(hr)||SUCCEEDED(hr2));
211 if (SUCCEEDED(hr))
212 hr=hr2;
213 }
214 }
215 }
216 IfFailThrow(hr);
217 return hr;
218}
219
220HRESULT CLRTestHookManager::LeavingAppDomain(DWORD adid)
221{
222 STATIC_CONTRACT_THROWS;
223 HRESULT hr=S_OK;
224 {
225 for (LONG i=0;i<m_nHooks;i++)
226 {
227 ICLRTestHook* hook=m_pHooks[i].v1();
228 if(hook)
229 {
230 HRESULT hr2=hook->LeavingAppDomain(adid);
231 _ASSERTE(SUCCEEDED(hr)||SUCCEEDED(hr2));
232 if (SUCCEEDED(hr))
233 hr=hr2;
234 }
235 }
236 }
237 IfFailThrow(hr);
238 return hr;
239}
240
241HRESULT CLRTestHookManager::LeftAppDomain(DWORD adid)
242{
243 STATIC_CONTRACT_THROWS;
244 HRESULT hr=S_OK;
245 {
246 for (LONG i=0;i<m_nHooks;i++)
247 {
248 ICLRTestHook* hook=m_pHooks[i].v1();
249 if(hook)
250 {
251 HRESULT hr2=hook->LeftAppDomain(adid);
252 _ASSERTE(SUCCEEDED(hr)||SUCCEEDED(hr2));
253 if (SUCCEEDED(hr))
254 hr=hr2;
255 }
256 }
257 }
258 IfFailThrow(hr);
259 return hr;
260}
261
262HRESULT CLRTestHookManager::UnwindingThreads(DWORD adid)
263{
264 STATIC_CONTRACT_NOTHROW;
265
266 struct Param
267 {
268 CLRTestHookManager *pThis;
269 DWORD adid;
270 } param;
271 param.pThis = this;
272 param.adid = adid;
273
274 PAL_TRY(Param *, pParam, &param)
275 {
276 //ignores the returned codes
277 for (LONG i = 0; i < pParam->pThis->m_nHooks; i++)
278 {
279 ICLRTestHook* hook = pParam->pThis->m_pHooks[i].v1();
280 if(hook)
281 {
282 HRESULT hr=hook->UnwindingThreads(pParam->adid);
283 _ASSERTE(SUCCEEDED(hr));
284 }
285 }
286 }
287 PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
288 {
289 _ASSERTE(!"Test Hook threw an exception.");
290 }
291 PAL_ENDTRY
292
293 return S_OK;
294}
295
296HRESULT CLRTestHookManager::RuntimeStarted(DWORD code)
297{
298 STATIC_CONTRACT_NOTHROW;
299
300 struct Param
301 {
302 CLRTestHookManager *pThis;
303 DWORD code;
304 } param;
305 param.pThis = this;
306 param.code = code;
307
308 PAL_TRY(Param *, pParam, &param)
309 {
310 //ignores the returned codes
311 for (LONG i = 0; i < pParam->pThis->m_nHooks; i++)
312 {
313 ICLRTestHook* hook = pParam->pThis->m_pHooks[i].v1();
314 if(hook)
315 {
316 HRESULT hr=hook->RuntimeStarted(pParam->code);
317 _ASSERTE(SUCCEEDED(hr));
318 }
319 }
320 }
321 PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
322 {
323 _ASSERTE(!"Test Hook threw an exception.");
324 }
325 PAL_ENDTRY
326
327 return S_OK;
328}
329
330HRESULT CLRTestHookManager::UnwoundThreads(DWORD adid)
331{
332 STATIC_CONTRACT_NOTHROW;
333
334 struct Param
335 {
336 CLRTestHookManager *pThis;
337 DWORD adid;
338 } param;
339 param.pThis = this;
340 param.adid = adid;
341
342 PAL_TRY(Param *, pParam, &param)
343 {
344 //ignores the returned codes
345 for (LONG i = 0; i < pParam->pThis->m_nHooks; i++)
346 {
347 ICLRTestHook* hook = pParam->pThis->m_pHooks[i].v1();
348 if(hook)
349 {
350 HRESULT hr=hook->UnwoundThreads(pParam->adid);
351 _ASSERTE(SUCCEEDED(hr));
352 }
353 }
354 }
355 PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
356 {
357 _ASSERTE(!"Test Hook threw an exception.");
358 }
359 PAL_ENDTRY
360
361 return S_OK;
362}
363
364HRESULT CLRTestHookManager::AppDomainDestroyed(DWORD adid)
365{
366 STATIC_CONTRACT_NOTHROW;
367
368 struct Param
369 {
370 CLRTestHookManager *pThis;
371 DWORD adid;
372 } param;
373 param.pThis = this;
374 param.adid = adid;
375
376 PAL_TRY(Param *, pParam, &param)
377 {
378 //ignores the returned codes
379 for (LONG i = 0; i < pParam->pThis->m_nHooks; i++)
380 {
381 ICLRTestHook* hook = pParam->pThis->m_pHooks[i].v1();
382 if(hook)
383 {
384 HRESULT hr=hook->AppDomainDestroyed(pParam->adid);
385 _ASSERTE(SUCCEEDED(hr));
386 }
387 }
388 }
389 PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
390 {
391 _ASSERTE(!"Test Hook threw an exception.");
392 }
393 PAL_ENDTRY
394
395 return S_OK;
396}
397
398STDMETHODIMP CLRTestHookManager::ImageMapped(LPCWSTR wszPath, LPCVOID pBaseAddress,DWORD flags)
399{
400 STATIC_CONTRACT_NOTHROW;
401
402 struct Param
403 {
404 CLRTestHookManager *pThis;
405 LPCWSTR wszPath;
406 LPCVOID pBaseAddress;
407 DWORD flags;
408 } param;
409 param.pThis = this;
410 param.wszPath = wszPath;
411 param.pBaseAddress = pBaseAddress;
412 param.flags = flags;
413
414 PAL_TRY(Param *, pParam, &param)
415 {
416 //ignores the returned codes
417 for (LONG i = 0; i < pParam->pThis->m_nHooks; i++)
418 {
419 ICLRTestHook2* hook = pParam->pThis->m_pHooks[i].v2();
420 if(hook)
421 {
422 HRESULT hr=hook->ImageMapped(pParam->wszPath,pParam->pBaseAddress,pParam->flags);
423 _ASSERTE(SUCCEEDED(hr));
424 }
425 }
426 }
427 PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
428 {
429 _ASSERTE(!"Test Hook threw an exception.");
430 }
431 PAL_ENDTRY
432
433 return S_OK;
434
435}
436
437HRESULT CLRTestHookManager::AppDomainCanBeUnloaded(DWORD adid, BOOL bUnsafePoint)
438{
439 STATIC_CONTRACT_THROWS;
440 HRESULT hr=S_OK;
441 {
442 if (!ThreadCanBeAborted())
443 return S_OK;
444 for (LONG i=0;i<m_nHooks;i++)
445 {
446 ICLRTestHook* hook=m_pHooks[i].v1();
447 if(hook)
448 {
449 HRESULT hr2=hook->AppDomainCanBeUnloaded(adid,bUnsafePoint);
450 _ASSERTE(SUCCEEDED(hr)||SUCCEEDED(hr2));
451 if (SUCCEEDED(hr))
452 hr=hr2;
453 }
454 }
455 }
456 IfFailThrow(hr);
457 return hr;
458}
459
460HRESULT CLRTestHookManager::StartingNativeImageBind(LPCWSTR wszAsmName, BOOL bIsCompilationProcess)
461{
462 STATIC_CONTRACT_THROWS;
463 HRESULT hr=S_OK;
464 {
465 for (LONG i=0;i<m_nHooks;i++)
466 {
467 ICLRTestHook3* hook=m_pHooks[i].v3();
468 if(hook)
469 {
470 HRESULT hr2=hook->StartingNativeImageBind(wszAsmName, bIsCompilationProcess);
471 _ASSERTE(SUCCEEDED(hr)||SUCCEEDED(hr2));
472 if (SUCCEEDED(hr))
473 hr=hr2;
474 }
475 }
476 }
477
478 IfFailThrow(hr);
479 return hr;
480}
481
482HRESULT CLRTestHookManager::CompletedNativeImageBind(LPVOID pFile,LPCUTF8 simpleName, BOOL hasNativeImage)
483{
484 STATIC_CONTRACT_THROWS;
485 HRESULT hr=S_OK;
486 {
487 for (LONG i=0;i<m_nHooks;i++)
488 {
489 ICLRTestHook3* hook=m_pHooks[i].v3();
490 if(hook)
491 {
492 HRESULT hr2=hook->CompletedNativeImageBind(pFile, simpleName, hasNativeImage);
493 _ASSERTE(SUCCEEDED(hr)||SUCCEEDED(hr2));
494 if (SUCCEEDED(hr))
495 hr=hr2;
496 }
497 }
498 }
499
500 IfFailThrow(hr);
501 return hr;
502}
503
504HRESULT CLRTestHookManager::AboutToLockImage(LPCWSTR wszPath, BOOL bIsCompilationProcess)
505{
506 STATIC_CONTRACT_THROWS;
507 HRESULT hr=S_OK;
508 {
509 for (LONG i=0;i<m_nHooks;i++)
510 {
511 ICLRTestHook3* hook=m_pHooks[i].v3();
512 if(hook)
513 {
514 HRESULT hr2=hook->AboutToLockImage(wszPath, bIsCompilationProcess);
515 _ASSERTE(SUCCEEDED(hr)||SUCCEEDED(hr2));
516 if (SUCCEEDED(hr))
517 hr=hr2;
518 }
519 }
520 }
521
522 IfFailThrow(hr);
523 return hr;
524}
525
526HRESULT CLRTestHookManager::EnableSlowPath (BOOL bEnable)
527{
528 WRAPPER_NO_CONTRACT;
529 ThreadStore::TrapReturningThreads(bEnable);
530 return S_OK;
531}
532
533ULONG CLRTestHookManager::AddRef()
534{
535 return FastInterlockIncrement(&m_cRef);
536}
537
538ULONG CLRTestHookManager::Release()
539{
540 ULONG nRet= FastInterlockDecrement(&m_cRef);
541 // never goes away
542 return nRet;
543}
544
545HRESULT CLRTestHookManager::QueryInterface(REFIID riid, void **ppv)
546{
547 if (riid!=IID_IUnknown && riid!=IID_ICLRTestHookManager)
548 return E_NOINTERFACE;
549 AddRef();
550 *ppv=(ICLRTestHookManager*)this;
551 return S_OK;
552}
553
554
555HRESULT CLRTestHookManager::CheckConfig()
556{
557 CONTRACTL
558 {
559 THROWS;
560 GC_NOTRIGGER;
561 MODE_ANY;
562 }
563 CONTRACTL_END;
564
565 HRESULT hr=S_OK;
566 if (g_pConfig)
567 {
568 LPWSTR szTestHooks=NULL;
569 hr=CLRConfig::GetConfigValue(CLRConfig::INTERNAL_TestHooks,&szTestHooks);
570 if (SUCCEEDED(hr) && szTestHooks!=NULL && *szTestHooks!=W('\0'))
571 {
572 LPWSTR curr=szTestHooks;
573 do
574 {
575 LPWSTR next=wcschr(curr,W(';'));
576 if (next)
577 *(next++)=0;
578 LPWSTR delim=wcschr(curr,W(','));
579 if (delim)
580 {
581 *(delim++)=W('\0');
582 HMODULE hMod=WszLoadLibrary(curr);
583 _ASSERTE(hMod);
584 if (hMod!=NULL)
585 {
586 MAKE_MULTIBYTE_FROMWIDE(szFName,delim,CP_ACP);
587 CLRTESTHOOKPROC* fn=(CLRTESTHOOKPROC*)GetProcAddress(hMod,szFName);
588 _ASSERTE(fn);
589 if(fn)
590 fn(Start());
591 }
592 }
593 curr=next;
594 }
595 while(curr!=NULL && *curr!=W('\0'));
596
597 delete szTestHooks;
598 }
599 }
600 return hr;
601}
602
603
604HRESULT CLRTestHookManager::UnloadAppDomain(DWORD adid,DWORD flags)
605{
606 return COR_E_CANNOTUNLOADAPPDOMAIN;
607}
608
609VOID CLRTestHookManager::DoAppropriateWait( int cObjs, HANDLE *pObjs, INT32 iTimeout, BOOL bWaitAll, int* res)
610{
611 CONTRACTL
612 {
613 THROWS;
614 GC_TRIGGERS;
615 MODE_ANY;
616 }
617 CONTRACTL_END;
618
619
620 Thread* thread=GetThread();
621 DWORD result = WAIT_FAILED;
622 if(thread)
623 result=thread->DoAppropriateWait(cObjs,pObjs,bWaitAll,iTimeout,WaitMode_Alertable,NULL);
624 else
625 {
626 result = WaitForMultipleObjectsEx(cObjs,pObjs,bWaitAll,iTimeout,TRUE);
627 }
628}
629
630
631HRESULT CLRTestHookManager::GC(int generation)
632{
633 CONTRACTL
634 {
635 THROWS;
636 GC_TRIGGERS;
637 MODE_ANY;
638 }
639 CONTRACTL_END;
640
641 _ASSERTE(GetThread()==NULL || !GetThread()->PreemptiveGCDisabled());
642 GCHeapUtilities::GetGCHeap()->GarbageCollect(generation);
643 FinalizerThread::FinalizerThreadWait();
644 return S_OK;
645}
646
647
648HRESULT CLRTestHookManager::GetSimpleName(LPVOID domainfile,LPCUTF8* name)
649{
650 HRESULT hr=S_OK;
651 EX_TRY
652 {
653 *name=((DomainFile*)domainfile)->GetSimpleName();
654 }
655 EX_CATCH_HRESULT(hr);
656 return hr;
657}
658
659
660
661INT_PTR CLRTestHookManager::GetCurrentThreadType()
662{
663 WRAPPER_NO_CONTRACT;
664 return (INT_PTR) ClrFlsGetValue (TlsIdx_ThreadType);
665}
666
667INT_PTR CLRTestHookManager::GetCurrentThreadLockCount (VOID)
668{
669 LIMITED_METHOD_CONTRACT;
670 Thread* thread=GetThread();
671 if(!thread)
672 return 0;
673 return thread->m_dwLockCount;
674
675};
676
677
678BOOL CLRTestHookManager::IsPreemptiveGC (VOID)
679{
680 LIMITED_METHOD_CONTRACT;
681 Thread *thread = GetThread();
682 // Preemptive GC is default
683 if (thread == NULL)
684 return TRUE;
685 else
686 return !thread->PreemptiveGCDisabled();
687};
688
689
690BOOL CLRTestHookManager::ThreadCanBeAborted (VOID)
691{
692 LIMITED_METHOD_CONTRACT;
693 return (GetThread()==NULL || GetThread()->IsAbortPrevented() || GetThread()->IsAsyncPrevented())?FALSE:TRUE;
694}
695
696HRESULT CLRTestHookManager::HasNativeImage(LPVOID domainfile,BOOL* pHasNativeImage)
697{
698 STATIC_CONTRACT_THROWS;
699 HRESULT hr=S_OK;
700 EX_TRY
701 {
702 if (domainfile && ((DomainFile*)domainfile)->GetFile())
703 {
704 *pHasNativeImage=((DomainFile*)domainfile)->GetFile()->HasNativeImage();
705 }
706 else
707 *pHasNativeImage = 0;
708 }
709 EX_CATCH_HRESULT(hr);
710 return hr;
711}
712
713
714void CLRTestHookInfo::Set(ICLRTestHook* hook)
715{
716 LIMITED_METHOD_CONTRACT;
717 if (SUCCEEDED(hook->QueryInterface(IID_ICLRTestHook3,(void**)&m_Hook.v3)))
718 {
719 m_Version=3;
720 return;
721 }
722 else if (SUCCEEDED(hook->QueryInterface(IID_ICLRTestHook2,(void**)&m_Hook.v2)))
723 {
724 m_Version=2;
725 return;
726 }
727 else
728 {
729 m_Version=1;
730 }
731 hook->AddRef();
732 m_Hook.v1=hook;
733}
734
735ICLRTestHook* CLRTestHookInfo::v1()
736{
737 return m_Hook.v1;
738}
739
740ICLRTestHook2* CLRTestHookInfo::v2()
741{
742 LIMITED_METHOD_CONTRACT;
743 if(m_Version==2)
744 return m_Hook.v2;
745 return NULL;
746}
747
748ICLRTestHook3* CLRTestHookInfo::v3()
749{
750 if(m_Version>=3)
751 return m_Hook.v3;
752 return NULL;
753}
754
755
756
757//to make sure CLRTestHook is ok
758static CLRTestHook _hook;
759
760#endif
761
762
763