Welcome to mirror list, hosted at ThFree Co, Russian Federation.

profheapwalkhelper.cpp « Runtime « Native « src - github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: b4dddca180c03db92afa23a8a794a5ff4e4793ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

// 
// On desktop CLR, GC ETW event firing borrows heavily from code in the profiling API,
// as the GC already called hooks in the profapi to notify it of roots & references. 
// This file shims up that profapi code the GC expects, though only for the purpose of
// firing ETW events (not for getting a full profapi up on redhawk).
// 

#include "common.h"

#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)

#include "gcenv.h"
#include "gcheaputilities.h"
#include "eventtrace.h"

//---------------------------------------------------------------------------------------
//
// Callback of type promote_func called by GC while scanning roots (in GCProfileWalkHeap,
// called after the collection).  Wrapper around EEToProfInterfaceImpl::RootReference2,
// which does the real work.
//
// Arguments:
//      pObj - Object reference encountered
///     ppRoot - Address that references ppObject (can be interior pointer)
//      pSC - ProfilingScanContext * containing the root kind and GCReferencesData used
//            by RootReference2 
//      dwFlags - Properties of the root as GC_CALL* constants (this function converts
//                to COR_PRF_GC_ROOT_FLAGS.
//

void ScanRootsHelper(Object* pObj, Object** ppRoot, ScanContext * pSC, DWORD dwFlags)
{
    ProfilingScanContext *pPSC = (ProfilingScanContext *)pSC;

    DWORD dwEtwRootFlags = 0;
    if (dwFlags & GC_CALL_INTERIOR)
        dwEtwRootFlags |= kEtwGCRootFlagsInterior;
    if (dwFlags & GC_CALL_PINNED)
        dwEtwRootFlags |= kEtwGCRootFlagsPinning;

    // Notify ETW of the root

    if (ETW::GCLog::ShouldWalkHeapRootsForEtw())
    {
        ETW::GCLog::RootReference(
            ppRoot,         // root address
            pObj,           // object being rooted
            NULL,           // pSecondaryNodeForDependentHandle is NULL, cuz this isn't a dependent handle
            FALSE,          // is dependent handle
            pPSC,
            dwFlags,        // dwGCFlags
            dwEtwRootFlags);
    }
}

//---------------------------------------------------------------------------------------
//
// Callback of type walk_fn used by GCHeap::WalkObject.  Keeps a count of each
// object reference found.
//
// Arguments:
//      pBO - Object reference encountered in walk
//      context - running count of object references encountered
//
// Return Value:
//      Always returns TRUE to object walker so it walks the entire object
//

BOOL CountContainedObjectRef(Object * pBO, void * context)
{
    LIMITED_METHOD_CONTRACT;
    UNREFERENCED_PARAMETER(pBO);
    // Increase the count
    (*((size_t *)context))++;

    return TRUE;
}

//---------------------------------------------------------------------------------------
//
// Callback of type walk_fn used by GCHeap::WalkObject.  Stores each object reference
// encountered into an array.
//
// Arguments:
//      pBO - Object reference encountered in walk
//      context - Array of locations within the walked object that point to other
//                objects.  On entry, (*context) points to the next unfilled array
//                entry.  On exit, that location is filled, and (*context) is incremented
//                to point to the next entry.
//
// Return Value:
//      Always returns TRUE to object walker so it walks the entire object
//

BOOL SaveContainedObjectRef(Object * pBO, void * context)
{
    LIMITED_METHOD_CONTRACT;
    // Assign the value
    **((Object ***)context) = pBO;

    // Now increment the array pointer
    //
    // Note that HeapWalkHelper has already walked the references once to count them up,
    // and then allocated an array big enough to hold those references.  First time this
    // callback is called for a given object, (*context) points to the first entry in the
    // array.  So "blindly" incrementing (*context) here and using it next time around
    // for the next reference, over and over again, should be safe.
    (*((Object ***)context))++;

    return TRUE;
}

//---------------------------------------------------------------------------------------
//
// Callback of type walk_fn used by the GC when walking the heap, to help profapi
// track objects.  This guy orchestrates the use of the above callbacks which dig
// into object references contained each object encountered by this callback.
//
// Arguments:
//      pBO - Object reference encountered on the heap
//
// Return Value:
//      BOOL indicating whether the heap walk should continue.
//      TRUE=continue
//      FALSE=stop
//

BOOL HeapWalkHelper(Object * pBO, void * pvContext)
{
    OBJECTREF *   arrObjRef      = NULL;
    size_t        cNumRefs       = 0;
    bool          bOnStack       = false;
    //MethodTable * pMT            = pBO->GetMethodTable();

    ProfilerWalkHeapContext * pProfilerWalkHeapContext = (ProfilerWalkHeapContext *) pvContext;

    //if (pMT->ContainsPointersOrCollectible())
    {
        // First round through calculates the number of object refs for this class
        GCHeapUtilities::GetGCHeap()->WalkObject(pBO, &CountContainedObjectRef, (void *)&cNumRefs);

        if (cNumRefs > 0)
        {
            // Create an array to contain all of the refs for this object
            bOnStack = cNumRefs <= 32 ? true : false;

            if (bOnStack)
            {
                // It's small enough, so just allocate on the stack
                arrObjRef = (OBJECTREF *)_alloca(cNumRefs * sizeof(OBJECTREF));
            }
            else
            {
                // Otherwise, allocate from the heap
                arrObjRef = new (nothrow) OBJECTREF[cNumRefs];

                if (!arrObjRef)
                {
                    return FALSE;
                }
            }

            // Second round saves off all of the ref values
            OBJECTREF * pCurObjRef = arrObjRef;
            GCHeapUtilities::GetGCHeap()->WalkObject(pBO, &SaveContainedObjectRef, (void *)&pCurObjRef);
        }
    }

    HRESULT hr = E_FAIL;

#ifdef FEATURE_ETW
    if (ETW::GCLog::ShouldWalkHeapObjectsForEtw())
    {
        ETW::GCLog::ObjectReference(
            pProfilerWalkHeapContext,
            pBO,
            ULONGLONG(pBO->get_SafeEEType()),
            cNumRefs,
            (Object **) arrObjRef);
    }
#endif // FEATURE_ETW

    // If the data was not allocated on the stack, need to clean it up.
    if ((arrObjRef != NULL) && !bOnStack)
    {
        delete [] arrObjRef;
    }

    // Return TRUE iff we want to the heap walk to continue. The only way we'd abort the
    // heap walk is if we're issuing profapi callbacks, and the profapi profiler
    // intentionally returned a failed HR (as its request that we stop the walk). There's
    // a potential conflict here. If a profapi profiler and an ETW profiler are both
    // monitoring the heap dump, and the profapi profiler requests to abort the walk (but
    // the ETW profiler may not want to abort the walk), then what do we do? The profapi
    // profiler gets precedence. We don't want to accidentally send more callbacks to a
    // profapi profiler that explicitly requested an abort. The ETW profiler will just
    // have to deal. In theory, I could make the code more complex by remembering that a
    // profapi profiler requested to abort the dump but an ETW profiler is still
    // attached, and then intentionally inhibit the remainder of the profapi callbacks
    // for this GC. But that's unnecessary complexity. In practice, it should be
    // extremely rare that a profapi profiler is monitoring heap dumps AND an ETW
    // profiler is also monitoring heap dumps.
    return TRUE;
}

#endif // defined(FEATURE_EVENT_TRACE) || defined(GC_PROFILING)