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 | // FILE: ProfilingEnumerators.cpp |
6 | // |
7 | // All enumerators returned by the profiling API to enumerate objects or to catch up on |
8 | // the current CLR state (usually for attaching profilers) are defined in |
9 | // ProfilingEnumerators.h,cpp. |
10 | // |
11 | // This cpp file contains implementations specific to the derived enumerator classes, as |
12 | // well as helpers for iterating over AppDomains, assemblies, modules, etc., that have |
13 | // been loaded enough that they may be made visible to profilers. |
14 | // |
15 | |
16 | // |
17 | |
18 | #include "common.h" |
19 | |
20 | #ifdef PROFILING_SUPPORTED |
21 | |
22 | #include "proftoeeinterfaceimpl.h" |
23 | #include "profilingenumerators.h" |
24 | |
25 | // --------------------------------------------------------------------------------------- |
26 | // ProfilerFunctionEnum/ICorProfilerFunctionEnum implementation |
27 | // --------------------------------------------------------------------------------------- |
28 | |
29 | BOOL ProfilerFunctionEnum::Init(BOOL fWithReJITIDs) |
30 | { |
31 | CONTRACTL |
32 | { |
33 | // Yay! |
34 | NOTHROW; |
35 | |
36 | // Yay! |
37 | // If we needs to get rejit ID, which requires a lock (which, in turn may switch us to |
38 | // preemptive mode). |
39 | if (fWithReJITIDs) GC_TRIGGERS; else GC_NOTRIGGER; |
40 | |
41 | // Yay! |
42 | MODE_ANY; |
43 | |
44 | // Depending on our GC mode, the jit manager may have to take a |
45 | // reader lock to prevent things from changing while reading... |
46 | CAN_TAKE_LOCK; |
47 | |
48 | SO_NOT_MAINLINE; |
49 | } CONTRACTL_END; |
50 | |
51 | EEJitManager::CodeHeapIterator heapIterator; |
52 | while(heapIterator.Next()) |
53 | { |
54 | MethodDesc *pMD = heapIterator.GetMethod(); |
55 | |
56 | // On AMD64 JumpStub is used to call functions that is 2GB away. JumpStubs have a CodeHeader |
57 | // with NULL MethodDesc, are stored in code heap and are reported by EEJitManager::EnumCode. |
58 | if (pMD == NULL) |
59 | continue; |
60 | |
61 | // There are two possible reasons to skip this MD. |
62 | // |
63 | // 1) If it has no metadata (i.e., LCG / IL stubs), then skip it |
64 | // |
65 | // 2) If it has no code compiled yet for it, then skip it. |
66 | // |
67 | if (pMD->IsNoMetadata() || !pMD->HasNativeCode()) |
68 | { |
69 | continue; |
70 | } |
71 | |
72 | COR_PRF_FUNCTION * element = m_elements.Append(); |
73 | if (element == NULL) |
74 | { |
75 | return FALSE; |
76 | } |
77 | element->functionId = (FunctionID) pMD; |
78 | |
79 | if (fWithReJITIDs) |
80 | { |
81 | // This guy causes triggering and locking, while the non-rejitid case does not. |
82 | element->reJitId = ReJitManager::GetReJitId(pMD, heapIterator.GetMethodCode()); |
83 | } |
84 | else |
85 | { |
86 | element->reJitId = 0; |
87 | } |
88 | } |
89 | |
90 | return TRUE; |
91 | } |
92 | |
93 | // --------------------------------------------------------------------------------------- |
94 | // Catch-up helpers |
95 | // |
96 | // #ProfilerEnumGeneral |
97 | // |
98 | // The following functions factor out the iteration code to ensure we only consider |
99 | // AppDomains, assemblies, modules, etc., that the profiler can safely query about. The |
100 | // parameters to these functions are of types that may have confusing syntax, but all |
101 | // that's going on is that the caller may supply an object instance and a member function |
102 | // on that object (non-static) to be called for each iterated item. This is just a |
103 | // statically-typed way of doing the usual pattern of providing a function pointer for |
104 | // the callback plus a void * context object to pass to the function. If the |
105 | // caller-supplied callback returns anything other than S_OK, the iteration code will |
106 | // stop iterating, and immediately propagate the callback's return value to the original |
107 | // caller. Start looking at code:ProfilerModuleEnum::Init for an example of how these |
108 | // helpers get used. |
109 | // |
110 | // The reason we have helpers to begin with is so we can centralize the logic that |
111 | // enforces the following rather subtle invariants: |
112 | // |
113 | // * Provide enough entities that the profiler gets a complete set of entities from |
114 | // the union of catch-up enumeration and "callbacks" (e.g., ModuleLoadFinished). |
115 | // * Exclude entities that have unloaded to the point where it's no longer safe to |
116 | // query information about them. |
117 | // |
118 | // The catch-up spec summarizes this via the following timeline for any given entity: |
119 | // |
120 | // Entity available in catch-up enumeration |
121 | // < Entity's LoadFinished (or equivalent) callback is issued |
122 | // < Entity NOT available from catch-up enumeration |
123 | // < Entity's UnloadStarted (or equivalent) callback is issued |
124 | // |
125 | // These helpers avoid duplicate code in the ProfilerModuleEnum implementation, and will |
126 | // also help avoid future duplicate code should we decide to provide more catch-up |
127 | // enumerations for attaching profilers to find currently loaded AppDomains, Classes, |
128 | // etc. |
129 | // |
130 | // Note: The debugging API has similar requirements around which entities at which stage |
131 | // of loading are permitted to be enumerated over. See code:IDacDbiInterface#Enumeration |
132 | // for debugger details. Note that profapi's needs are not exactly the same. For example, |
133 | // Assemblies appear in the debugging API enumerations as soon as they begin to load, |
134 | // whereas Assemblies (like all other entities) appear in the profiling API enumerations |
135 | // once their load is complete (i.e., just before AssemblyLoadFinished). Also, |
136 | // debuggers enumerate DomainModules and DomainAssemblies, whereas profilers enumerate |
137 | // Modules and Assemblies. |
138 | // |
139 | // For information about other synchronization issues with profiler catch-up, see |
140 | // code:ProfilingAPIUtility::LoadProfiler#ProfCatchUpSynchronization |
141 | // |
142 | // --------------------------------------------------------------------------------------- |
143 | |
144 | |
145 | //--------------------------------------------------------------------------------------- |
146 | // |
147 | // Iterates through exactly those AppDomains that should be visible to the profiler, and |
148 | // calls a caller-supplied function to operate on each iterated AppDomain |
149 | // |
150 | // Arguments: |
151 | // * callbackObj - Caller-supplied object containing the callback method to call for |
152 | // each AppDomain |
153 | // * callbackMethod - Caller-supplied method to call for each AppDomain. If this |
154 | // method returns anything other than S_OK, then the iteration is aborted, and |
155 | // callbackMethod's return value is returned to our caller. |
156 | // |
157 | |
158 | template<typename CallbackObject> |
159 | HRESULT IterateAppDomains(CallbackObject * callbackObj, |
160 | HRESULT (CallbackObject:: * callbackMethod)(AppDomain *)) |
161 | { |
162 | CONTRACTL |
163 | { |
164 | NOTHROW; |
165 | GC_TRIGGERS; |
166 | MODE_ANY; |
167 | CAN_TAKE_LOCK; |
168 | // (See comments in code:ProfToEEInterfaceImpl::EnumModules for info about contracts.) |
169 | |
170 | SO_NOT_MAINLINE; |
171 | } |
172 | CONTRACTL_END; |
173 | |
174 | // #ProfilerEnumAppDomains (See also code:#ProfilerEnumGeneral) |
175 | // |
176 | // When enumerating AppDomains, ensure this timeline: |
177 | // AD available in catch-up enumeration |
178 | // < AppDomainCreationFinished issued |
179 | // < AD NOT available from catch-up enumeration |
180 | // |
181 | // The AppDomainIterator constructor parameter m_bActive is set to be TRUE below, |
182 | // meaning only AppDomains in stage STAGE_ACTIVE or higher will be included |
183 | // in the iteration. |
184 | // * AppDomainCreationFinished (with S_OK hrStatus) is issued once the AppDomain |
185 | // reaches STAGE_ACTIVE. |
186 | AppDomainIterator appDomainIterator(TRUE); |
187 | while (appDomainIterator.Next()) |
188 | { |
189 | AppDomain * pAppDomain = appDomainIterator.GetDomain(); |
190 | |
191 | // Of course, the AD could start unloading here, but if it does we're guaranteed |
192 | // the profiler has had a chance to see the Unload callback for the AD, and thus |
193 | // the profiler can block in that callback until it's done with the enumerator |
194 | // we provide. |
195 | |
196 | // Call user-supplied callback, and cancel iteration if requested |
197 | HRESULT hr = (callbackObj->*callbackMethod)(pAppDomain); |
198 | if (hr != S_OK) |
199 | { |
200 | return hr; |
201 | } |
202 | } |
203 | |
204 | return S_OK; |
205 | } |
206 | |
207 | |
208 | //--------------------------------------------------------------------------------------- |
209 | // |
210 | // Iterates through exactly those Modules that should be visible to the profiler, and |
211 | // calls a caller-supplied function to operate on each iterated Module. Any module that |
212 | // is loaded domain-neutral is skipped. |
213 | // |
214 | // Arguments: |
215 | // * pAppDomain - Only unshared modules loaded into this AppDomain will be iterated |
216 | // * callbackObj - Caller-supplied object containing the callback method to call for |
217 | // each Module |
218 | // * callbackMethod - Caller-supplied method to call for each Module. If this |
219 | // method returns anything other than S_OK, then the iteration is aborted, and |
220 | // callbackMethod's return value is returned to our caller. |
221 | // |
222 | // Notes: |
223 | // * In theory, this could be broken down into an unshared assembly iterator that |
224 | // takes a callback, and an unshared module iterator (based on an input |
225 | // assembly) that takes a callback. But that kind of granularity is unnecessary |
226 | // now, and probably not useful in the future. If that turns out to be wrong, |
227 | // this can still be broken down that way later on. |
228 | // |
229 | |
230 | template<typename CallbackObject> |
231 | HRESULT IterateUnsharedModules(AppDomain * pAppDomain, |
232 | CallbackObject * callbackObj, |
233 | HRESULT (CallbackObject:: * callbackMethod)(Module *)) |
234 | { |
235 | CONTRACTL |
236 | { |
237 | NOTHROW; |
238 | GC_TRIGGERS; |
239 | MODE_ANY; |
240 | CAN_TAKE_LOCK; |
241 | } |
242 | CONTRACTL_END; |
243 | |
244 | // #ProfilerEnumAssemblies (See also code:#ProfilerEnumGeneral) |
245 | // |
246 | // When enumerating assemblies, ensure this timeline: |
247 | // Assembly available in catch-up enumeration |
248 | // < AssemblyLoadFinished issued |
249 | // < Assembly NOT available from catch-up enumeration |
250 | // < AssemblyUnloadStarted issued |
251 | // |
252 | // The IterateAssembliesEx parameter below ensures we will only include assemblies at |
253 | // load level >= FILE_LOAD_LOADLIBRARY. |
254 | // * AssemblyLoadFinished is issued once the Assembly reaches |
255 | // code:FILE_LOAD_LOADLIBRARY |
256 | // * AssemblyUnloadStarted is issued as a result of either: |
257 | // * Collectible assemblies unloading. Such assemblies will no longer be |
258 | // enumerable. |
259 | // |
260 | // Note: To determine what happens in a given load stage of a module or assembly, |
261 | // look at the switch statement in code:DomainFile::DoIncrementalLoad, and keep in |
262 | // mind that it takes cases on the *next* load stage; in other words, the actions |
263 | // that appear in a case for a given load stage are actually executed as we attempt |
264 | // to transition TO that load stage, and thus they actually execute while the module |
265 | // / assembly is still in the previous load stage. |
266 | // |
267 | // Note that the CLR may issue ModuleLoadFinished / AssemblyLoadFinished later, at |
268 | // FILE_LOAD_EAGER_FIXUPS stage, if for some reason MLF/ALF hadn't been issued |
269 | // earlier during FILE_LOAD_LOADLIBRARY. This does not affect the timeline, as either |
270 | // way the profiler receives the notification AFTER the assembly would appear in the |
271 | // enumeration. |
272 | // |
273 | // Although it's called an "AssemblyIterator", it actually iterates over |
274 | // DomainAssembly instances. |
275 | AppDomain::AssemblyIterator domainAssemblyIterator = |
276 | pAppDomain->IterateAssembliesEx( |
277 | (AssemblyIterationFlags) (kIncludeAvailableToProfilers | kIncludeExecution)); |
278 | CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; |
279 | |
280 | while (domainAssemblyIterator.Next(pDomainAssembly.This())) |
281 | { |
282 | _ASSERTE(pDomainAssembly != NULL); |
283 | _ASSERTE(pDomainAssembly->GetAssembly() != NULL); |
284 | |
285 | // #ProfilerEnumModules (See also code:#ProfilerEnumGeneral) |
286 | // |
287 | // When enumerating modules, ensure this timeline: |
288 | // Module available in catch-up enumeration |
289 | // < ModuleLoadFinished issued |
290 | // < Module NOT available from catch-up enumeration |
291 | // < ModuleUnloadStarted issued |
292 | // |
293 | // The IterateModules parameter below ensures only modules at level >= |
294 | // code:FILE_LOAD_LOADLIBRARY will be included in the iteration. |
295 | // |
296 | // Details for module callbacks are the same as those for assemblies, so see |
297 | // code:#ProfilerEnumAssemblies for info on how the timing works. |
298 | DomainModuleIterator domainModuleIterator = |
299 | pDomainAssembly->IterateModules(kModIterIncludeAvailableToProfilers); |
300 | while (domainModuleIterator.Next()) |
301 | { |
302 | // Call user-supplied callback, and cancel iteration if requested |
303 | HRESULT hr = (callbackObj->*callbackMethod)(domainModuleIterator.GetModule()); |
304 | if (hr != S_OK) |
305 | { |
306 | return hr; |
307 | } |
308 | } |
309 | } |
310 | |
311 | return S_OK; |
312 | } |
313 | |
314 | //--------------------------------------------------------------------------------------- |
315 | // ProfilerModuleEnum implementation |
316 | //--------------------------------------------------------------------------------------- |
317 | |
318 | |
319 | //--------------------------------------------------------------------------------------- |
320 | // This is a helper class used by ProfilerModuleEnum when determining which shared |
321 | // modules should be added to the enumerator. See code:ProfilerModuleEnum::Init for how |
322 | // this gets used |
323 | |
324 | class IterateAppDomainsForSharedModule |
325 | { |
326 | public: |
327 | IterateAppDomainsForSharedModule(CDynArray< ModuleID > * pElements, Module * pModule) |
328 | : m_pElements(pElements), m_pModule(pModule) |
329 | { |
330 | LIMITED_METHOD_CONTRACT; |
331 | } |
332 | |
333 | //--------------------------------------------------------------------------------------- |
334 | // Callback passed to IterateAppDomains, that takes the currently iterated AppDomain, |
335 | // and adds m_pModule to the enumerator if it's loaded into the AppDomain. See |
336 | // code:ProfilerModuleEnum::Init for how this gets used. |
337 | // |
338 | // Arguments: |
339 | // * pAppDomain - Current AppDomain being iterated. |
340 | // |
341 | // Return Value: |
342 | // * S_OK = the iterator should continue after we return. |
343 | // * S_FALSE = we verified m_pModule is loaded into this AppDomain, so no need |
344 | // for the iterator to continue with the next AppDomain |
345 | // * error indicating a failure |
346 | // |
347 | HRESULT AddSharedModuleForAppDomain(AppDomain * pAppDomain) |
348 | { |
349 | CONTRACTL |
350 | { |
351 | NOTHROW; |
352 | GC_NOTRIGGER; |
353 | MODE_ANY; |
354 | CANNOT_TAKE_LOCK; |
355 | } |
356 | CONTRACTL_END; |
357 | |
358 | DomainFile * pDomainFile = m_pModule->FindDomainFile(pAppDomain); |
359 | if ((pDomainFile == NULL) || !pDomainFile->IsAvailableToProfilers()) |
360 | { |
361 | // This AD doesn't contain a fully loaded DomainFile for m_pModule. So continue |
362 | // iterating with the next AD |
363 | return S_OK; |
364 | } |
365 | |
366 | ModuleID * pElement = m_pElements->Append(); |
367 | if (pElement == NULL) |
368 | { |
369 | // Stop iteration with error |
370 | return E_OUTOFMEMORY; |
371 | } |
372 | |
373 | // If we're here, we found a fully loaded DomainFile for m_pModule. So add |
374 | // m_pModule to our array, and no need to look at other other ADs for this |
375 | // m_pModule. |
376 | *pElement = (ModuleID) m_pModule; |
377 | return S_FALSE; |
378 | } |
379 | |
380 | private: |
381 | // List of ModuleIDs in the enumerator we're building |
382 | CDynArray< ModuleID > * m_pElements; |
383 | |
384 | // Shared Module we're testing for load status in the iterated ADs. |
385 | Module * m_pModule; |
386 | }; |
387 | |
388 | |
389 | //--------------------------------------------------------------------------------------- |
390 | // |
391 | // Callback passed to IterateAppDomains, that takes the currently iterated AppDomain, |
392 | // and then iterates through the unshared modules loaded into that AD. See |
393 | // code:ProfilerModuleEnum::Init for how this gets used. |
394 | // |
395 | // Arguments: |
396 | // * pAppDomain - Current AppDomain being iterated. |
397 | // |
398 | // Return Value: |
399 | // * S_OK = the iterator should continue after we return. |
400 | // * S_FALSE = we verified m_pModule is loaded into this AppDomain, so no need |
401 | // for the iterator to continue with the next AppDomain |
402 | // * error indicating a failure |
403 | // |
404 | |
405 | HRESULT ProfilerModuleEnum::AddUnsharedModulesFromAppDomain(AppDomain * pAppDomain) |
406 | { |
407 | CONTRACTL |
408 | { |
409 | NOTHROW; |
410 | GC_TRIGGERS; |
411 | MODE_ANY; |
412 | CAN_TAKE_LOCK; |
413 | } |
414 | CONTRACTL_END; |
415 | |
416 | return IterateUnsharedModules<ProfilerModuleEnum>( |
417 | pAppDomain, |
418 | this, |
419 | &ProfilerModuleEnum::AddUnsharedModule); |
420 | } |
421 | |
422 | |
423 | //--------------------------------------------------------------------------------------- |
424 | // |
425 | // Callback passed to IterateUnsharedModules, that takes the currently iterated unshared |
426 | // Module, and adds it to the enumerator. See code:ProfilerModuleEnum::Init for how this |
427 | // gets used. |
428 | // |
429 | // Arguments: |
430 | // * pModule - Current Module being iterated. |
431 | // |
432 | // Return Value: |
433 | // * S_OK = the iterator should continue after we return. |
434 | // * error indicating a failure |
435 | // |
436 | HRESULT ProfilerModuleEnum::AddUnsharedModule(Module * pModule) |
437 | { |
438 | CONTRACTL |
439 | { |
440 | NOTHROW; |
441 | GC_NOTRIGGER; |
442 | MODE_ANY; |
443 | CANNOT_TAKE_LOCK; |
444 | } |
445 | CONTRACTL_END; |
446 | |
447 | ModuleID * pElement = m_elements.Append(); |
448 | if (pElement == NULL) |
449 | { |
450 | return E_OUTOFMEMORY; |
451 | } |
452 | *pElement = (ModuleID) pModule; |
453 | return S_OK; |
454 | } |
455 | |
456 | |
457 | //--------------------------------------------------------------------------------------- |
458 | // |
459 | // Populate the module enumerator that's about to be given to the profiler. This is |
460 | // called from the ICorProfilerInfo3::EnumModules implementation. |
461 | // |
462 | // This code controls how the above iterator helpers and callbacks are used, so you might |
463 | // want to look here first to understand how how the helpers and callbacks are used. |
464 | // |
465 | // Return Value: |
466 | // HRESULT indicating success or failure. |
467 | // |
468 | HRESULT ProfilerModuleEnum::Init() |
469 | { |
470 | CONTRACTL |
471 | { |
472 | NOTHROW; |
473 | GC_TRIGGERS; |
474 | MODE_ANY; |
475 | CAN_TAKE_LOCK; |
476 | // (See comments in code:ProfToEEInterfaceImpl::EnumModules for info about contracts.) |
477 | |
478 | SO_NOT_MAINLINE; |
479 | } |
480 | CONTRACTL_END; |
481 | |
482 | HRESULT hr = S_OK; |
483 | |
484 | // When an assembly or module is loaded into an AppDomain, a separate DomainFile is |
485 | // created (one per pairing of the AppDomain with the module or assembly). This means |
486 | // that we'll create multiple DomainFiles for the same module if it is loaded |
487 | // domain-neutral (i.e., "shared"). The profiling API callbacks shield the profiler |
488 | // from this, and only report a given module the first time it's loaded. So a |
489 | // profiler sees only one ModuleLoadFinished for a module loaded domain-neutral, even |
490 | // though the module may be used by multiple AppDomains. The module enumerator must |
491 | // mirror the behavior of the profiling API callbacks, by avoiding duplicate Modules |
492 | // in the module list we return to the profiler. So first add unshared modules (non |
493 | // domain-neutral) to the enumerator, and then separately add any shared modules that |
494 | // were loaded into at least one AD. |
495 | |
496 | // First, iterate through all ADs. For each one, call |
497 | // AddUnsharedModulesFromAppDomain, which iterates through all UNSHARED modules and |
498 | // adds them to the enumerator. |
499 | hr = IterateAppDomains<ProfilerModuleEnum>( |
500 | this, |
501 | &ProfilerModuleEnum::AddUnsharedModulesFromAppDomain); |
502 | if (FAILED(hr)) |
503 | { |
504 | return hr; |
505 | } |
506 | |
507 | return S_OK; |
508 | } |
509 | |
510 | |
511 | //--------------------------------------------------------------------------------------- |
512 | // |
513 | // Callback passed to IterateAppDomains, that takes the currently iterated AppDomain, |
514 | // and adds it to the enumerator if it has loaded the given module. See |
515 | // code:IterateAppDomainContainingModule::PopulateArray for how this gets used. |
516 | // |
517 | // Arguments: |
518 | // * pAppDomain - Current AppDomain being iterated. |
519 | // |
520 | // Return Value: |
521 | // * S_OK = the iterator should continue after we return. |
522 | // * error indicating a failure |
523 | // |
524 | HRESULT IterateAppDomainContainingModule::AddAppDomainContainingModule(AppDomain * pAppDomain) |
525 | { |
526 | CONTRACTL |
527 | { |
528 | NOTHROW; |
529 | // This method iterates over AppDomains, which adds, then releases, a reference on |
530 | // each AppDomain iterated. This causes locking, and can cause triggering if the |
531 | // AppDomain gets destroyed as a result of the release. (See code:AppDomainIterator::Next |
532 | // and its call to code:AppDomain::Release.) |
533 | GC_TRIGGERS; |
534 | MODE_ANY; |
535 | CAN_TAKE_LOCK; |
536 | SO_NOT_MAINLINE; |
537 | } |
538 | CONTRACTL_END; |
539 | |
540 | DomainFile * pDomainFile = m_pModule->FindDomainFile(pAppDomain); |
541 | if ((pDomainFile != NULL) && (pDomainFile->IsAvailableToProfilers())) |
542 | { |
543 | if (m_index < m_cAppDomainIds) |
544 | { |
545 | m_rgAppDomainIds[m_index] = reinterpret_cast<AppDomainID>(pAppDomain); |
546 | } |
547 | |
548 | m_index++; |
549 | } |
550 | |
551 | return S_OK; |
552 | } |
553 | |
554 | |
555 | //--------------------------------------------------------------------------------------- |
556 | // |
557 | // Populate the array with AppDomains in which the given module has been loaded |
558 | // |
559 | // Return Value: |
560 | // HRESULT indicating success or failure. |
561 | // |
562 | HRESULT IterateAppDomainContainingModule::PopulateArray() |
563 | { |
564 | CONTRACTL |
565 | { |
566 | NOTHROW; |
567 | // This method iterates over AppDomains, which adds, then releases, a reference on |
568 | // each AppDomain iterated. This causes locking, and can cause triggering if the |
569 | // AppDomain gets destroyed as a result of the release. (See code:AppDomainIterator::Next |
570 | // and its call to code:AppDomain::Release.) |
571 | GC_TRIGGERS; |
572 | MODE_ANY; |
573 | CAN_TAKE_LOCK; |
574 | SO_NOT_MAINLINE; |
575 | } |
576 | CONTRACTL_END; |
577 | |
578 | HRESULT hr = IterateAppDomains<IterateAppDomainContainingModule>( |
579 | this, |
580 | &IterateAppDomainContainingModule::AddAppDomainContainingModule); |
581 | |
582 | *m_pcAppDomainIds = m_index; |
583 | |
584 | return hr; |
585 | } |
586 | |
587 | //--------------------------------------------------------------------------------------- |
588 | // |
589 | // Populate the thread enumerator that's about to be given to the profiler. This is |
590 | // called from the ICorProfilerInfo4::EnumThread implementation. |
591 | // |
592 | // Return Value: |
593 | // HRESULT indicating success or failure. |
594 | // |
595 | HRESULT ProfilerThreadEnum::Init() |
596 | { |
597 | CONTRACTL |
598 | { |
599 | NOTHROW; |
600 | GC_NOTRIGGER; |
601 | MODE_ANY; |
602 | CAN_TAKE_LOCK; |
603 | SO_NOT_MAINLINE; |
604 | } |
605 | CONTRACTL_END; |
606 | |
607 | ThreadStoreLockHolder tsLock; |
608 | |
609 | Thread * pThread = NULL; |
610 | |
611 | // |
612 | // Walk through all the threads with the lock taken |
613 | // Because the thread enumeration status need to change before the ThreadCreated/ThreadDestroyed |
614 | // callback, we need to: |
615 | // 1. Include Thread::TS_FullyInitialized threads for ThreadCreated |
616 | // 2. Exclude Thread::TS_Dead | Thread::TS_ReportDead for ThreadDestroyed |
617 | // |
618 | while((pThread = ThreadStore::GetAllThreadList( |
619 | pThread, |
620 | Thread::TS_Dead | Thread::TS_ReportDead | Thread::TS_FullyInitialized, |
621 | Thread::TS_FullyInitialized |
622 | ))) |
623 | { |
624 | if (pThread->IsGCSpecial()) |
625 | continue; |
626 | |
627 | *m_elements.Append() = (ThreadID) pThread; |
628 | } |
629 | |
630 | return S_OK; |
631 | } |
632 | |
633 | |
634 | #endif // PROFILING_SUPPORTED |
635 | |