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