-
Notifications
You must be signed in to change notification settings - Fork 5k
/
Copy pathgcheaputilities.cpp
416 lines (350 loc) · 14.4 KB
/
gcheaputilities.cpp
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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#include "common.h"
#include "configuration.h"
#include "gcheaputilities.h"
#include "appdomain.hpp"
#include "hostinformation.h"
#include "../gc/env/gcenv.ee.h"
#include "../gc/env/gctoeeinterface.standalone.inl"
// These globals are variables used within the GC and maintained
// by the EE for use in write barriers. It is the responsibility
// of the GC to communicate updates to these globals to the EE through
// GCToEEInterface::StompWriteBarrierResize and GCToEEInterface::StompWriteBarrierEphemeral.
GPTR_IMPL_INIT(uint32_t, g_card_table, nullptr);
GPTR_IMPL_INIT(uint8_t, g_lowest_address, nullptr);
GPTR_IMPL_INIT(uint8_t, g_highest_address, nullptr);
GVAL_IMPL_INIT(GCHeapType, g_heap_type, GC_HEAP_INVALID);
uint8_t* g_ephemeral_low = (uint8_t*)1;
uint8_t* g_ephemeral_high = (uint8_t*)~0;
uint8_t* g_region_to_generation_table = nullptr;
uint8_t g_region_shr = 0;
bool g_region_use_bitwise_write_barrier = false;
#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
uint32_t* g_card_bundle_table = nullptr;
#endif
// This is the global GC heap, maintained by the VM.
GPTR_IMPL(IGCHeap, g_pGCHeap);
GcDacVars g_gc_dac_vars;
GPTR_IMPL(GcDacVars, g_gcDacGlobals);
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
uint8_t* g_sw_ww_table = nullptr;
bool g_sw_ww_enabled_for_gc_heap = false;
#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
GVAL_IMPL_INIT(ee_alloc_context, g_global_alloc_context, {});
thread_local ee_alloc_context::PerThreadRandom ee_alloc_context::t_random = PerThreadRandom();
enum GC_LOAD_STATUS {
GC_LOAD_STATUS_BEFORE_START,
GC_LOAD_STATUS_START,
GC_LOAD_STATUS_DONE_LOAD,
GC_LOAD_STATUS_GET_VERSIONINFO,
GC_LOAD_STATUS_CALL_VERSIONINFO,
GC_LOAD_STATUS_DONE_VERSION_CHECK,
GC_LOAD_STATUS_GET_INITIALIZE,
GC_LOAD_STATUS_LOAD_COMPLETE
};
// Load status of the GC. If GC loading fails, the value of this
// global indicates where the failure occurred.
GC_LOAD_STATUS g_gc_load_status = GC_LOAD_STATUS_BEFORE_START;
// The version of the GC that we have loaded.
VersionInfo g_gc_version_info;
// The module that contains the GC.
PTR_VOID g_gc_module_base;
bool GCHeapUtilities::s_useThreadAllocationContexts;
// GC entrypoints for the linked-in GC. These symbols are invoked
// directly if we are not using a standalone GC.
extern "C" void LOCALGC_CALLCONV GC_VersionInfo(/* Out */ VersionInfo* info);
extern "C" HRESULT LOCALGC_CALLCONV GC_Initialize(
/* In */ IGCToCLR* clrToGC,
/* Out */ IGCHeap** gcHeap,
/* Out */ IGCHandleManager** gcHandleManager,
/* Out */ GcDacVars* gcDacVars
);
#ifndef DACCESS_COMPILE
PTR_VOID GCHeapUtilities::GetGCModuleBase()
{
assert(g_gc_module_base);
return g_gc_module_base;
}
namespace
{
// This block of code contains all of the state necessary to handle incoming
// EtwCallbacks before the GC has been initialized. This is a tricky problem
// because EtwCallbacks can appear at any time, even when we are just about
// finished initializing the GC.
//
// The below lock is taken by the "main" thread (the thread in EEStartup) and
// the "ETW" thread, the one calling EtwCallback. EtwCallback may or may not
// be called on the main thread.
DangerousNonHostedSpinLock g_eventStashLock;
GCEventLevel g_stashedLevel = GCEventLevel_None;
GCEventKeyword g_stashedKeyword = GCEventKeyword_None;
GCEventLevel g_stashedPrivateLevel = GCEventLevel_None;
GCEventKeyword g_stashedPrivateKeyword = GCEventKeyword_None;
BOOL g_gcEventTracingInitialized = FALSE;
// FinalizeLoad is called by the main thread to complete initialization of the GC.
// At this point, the GC has provided us with an IGCHeap instance and we are preparing
// to "publish" it by assigning it to g_pGCHeap.
//
// This function can proceed concurrently with StashKeywordAndLevel below.
void FinalizeLoad(IGCHeap* gcHeap, IGCHandleManager* handleMgr, PTR_VOID pGcModuleBase)
{
g_pGCHeap = gcHeap;
{
DangerousNonHostedSpinLockHolder lockHolder(&g_eventStashLock);
// Ultimately, g_eventStashLock ensures that no two threads call ControlEvents at any
// point in time.
g_pGCHeap->ControlEvents(g_stashedKeyword, g_stashedLevel);
g_pGCHeap->ControlPrivateEvents(g_stashedPrivateKeyword, g_stashedPrivateLevel);
g_gcEventTracingInitialized = TRUE;
}
g_pGCHandleManager = handleMgr;
g_gcDacGlobals = &g_gc_dac_vars;
g_gc_load_status = GC_LOAD_STATUS_LOAD_COMPLETE;
g_gc_module_base = pGcModuleBase;
LOG((LF_GC, LL_INFO100, "GC load successful\n"));
StressLog::AddModule((uint8_t*)pGcModuleBase);
}
void StashKeywordAndLevel(bool isPublicProvider, GCEventKeyword keywords, GCEventLevel level)
{
DangerousNonHostedSpinLockHolder lockHolder(&g_eventStashLock);
if (!g_gcEventTracingInitialized)
{
if (isPublicProvider)
{
g_stashedKeyword = keywords;
g_stashedLevel = level;
}
else
{
g_stashedPrivateKeyword = keywords;
g_stashedPrivateLevel = level;
}
}
else
{
if (isPublicProvider)
{
g_pGCHeap->ControlEvents(keywords, level);
}
else
{
g_pGCHeap->ControlPrivateEvents(keywords, level);
}
}
}
#ifdef FEATURE_STANDALONE_GC
HMODULE LoadStandaloneGc(LPCWSTR libFileName, LPCWSTR libFilePath)
{
LIMITED_METHOD_CONTRACT;
HMODULE result = nullptr;
if (libFilePath)
{
return CLRLoadLibrary(libFilePath);
}
//
// This is not a security feature.
// The libFileName originates either from an environment variable or from the runtimeconfig.json
// These are trusted locations, and therefore even if it is a relative path, there is no security risk.
//
// However, users often don't know the absolute path to their coreclr module, especially on production.
// Therefore we allow referencing it from an arbitrary location through libFilePath instead. Users, however
// are warned that they should keep the file in a secure location such that it cannot be tampered.
//
if (!ValidateModuleName(libFileName))
{
LOG((LF_GC, LL_INFO100, "Invalid GC name found %s\n", libFileName));
return nullptr;
}
SString appBase;
if (HostInformation::GetProperty("APP_CONTEXT_BASE_DIRECTORY", appBase))
{
PathString libPath = appBase.GetUnicode();
libPath.Append(libFileName);
LOG((LF_GC, LL_INFO100, "Loading standalone GC from appBase %s\n", libPath.GetUTF8()));
LPCWSTR libraryName = libPath.GetUnicode();
result = CLRLoadLibrary(libraryName);
}
if (result == nullptr)
{
// Look for the standalone GC module next to the clr binary
PathString libPath = GetInternalSystemDirectory();
libPath.Append(libFileName);
LOG((LF_GC, LL_INFO100, "Loading standalone GC by coreclr %s\n", libPath.GetUTF8()));
LPCWSTR libraryName = libPath.GetUnicode();
result = CLRLoadLibrary(libraryName);
}
return result;
}
#endif // FEATURE_STANDALONE_GC
// Loads and initializes a standalone GC, given the path to the GC
// that we should load. Returns S_OK on success and the failed HRESULT
// on failure.
//
// See Documentation/design-docs/standalone-gc-loading.md for details
// on the loading protocol in use here.
HRESULT LoadAndInitializeGC(LPCWSTR standaloneGCName, LPCWSTR standaloneGCPath)
{
LIMITED_METHOD_CONTRACT;
#ifndef FEATURE_STANDALONE_GC
LOG((LF_GC, LL_FATALERROR, "EE not built with the ability to load standalone GCs"));
return E_FAIL;
#else
HMODULE hMod = LoadStandaloneGc(standaloneGCName, standaloneGCPath);
if (!hMod)
{
HRESULT err = GetLastError();
#ifdef LOGGING
LPCWSTR standaloneGCNameLogging = standaloneGCName ? standaloneGCName : W("");
LPCWSTR standaloneGCPathLogging = standaloneGCPath ? standaloneGCPath : W("");
MAKE_UTF8PTR_FROMWIDE(standaloneGCNameUtf8, standaloneGCNameLogging);
MAKE_UTF8PTR_FROMWIDE(standaloneGCPathUtf8, standaloneGCPathLogging);
LOG((LF_GC, LL_FATALERROR, "Load of %s or %s failed\n", standaloneGCNameUtf8, standaloneGCPathUtf8));
#endif // LOGGING
return __HRESULT_FROM_WIN32(err);
}
// a standalone GC dispatches virtually on GCToEEInterface, so we must instantiate
// a class for the GC to use.
IGCToCLR* gcToClr = new (nothrow) standalone::GCToEEInterface();
if (!gcToClr)
{
return E_OUTOFMEMORY;
}
g_gc_load_status = GC_LOAD_STATUS_DONE_LOAD;
GC_VersionInfoFunction versionInfo = (GC_VersionInfoFunction)GetProcAddress(hMod, "GC_VersionInfo");
if (!versionInfo)
{
HRESULT err = GetLastError();
LOG((LF_GC, LL_FATALERROR, "Load of `GC_VersionInfo` from standalone GC failed\n"));
return __HRESULT_FROM_WIN32(err);
}
g_gc_load_status = GC_LOAD_STATUS_GET_VERSIONINFO;
g_gc_version_info.MajorVersion = EE_INTERFACE_MAJOR_VERSION;
g_gc_version_info.MinorVersion = 0;
g_gc_version_info.BuildVersion = 0;
versionInfo(&g_gc_version_info);
g_gc_load_status = GC_LOAD_STATUS_CALL_VERSIONINFO;
if (g_gc_version_info.MajorVersion < GC_INTERFACE_MAJOR_VERSION)
{
LOG((LF_GC, LL_FATALERROR, "Loaded GC has incompatible major version number (expected at least %d, got %d)\n",
GC_INTERFACE_MAJOR_VERSION, g_gc_version_info.MajorVersion));
return E_FAIL;
}
if ((g_gc_version_info.MajorVersion == GC_INTERFACE_MAJOR_VERSION) &&
(g_gc_version_info.MinorVersion < GC_INTERFACE_MINOR_VERSION))
{
LOG((LF_GC, LL_INFO100, "Loaded GC has lower minor version number (%d) than EE was compiled against (%d)\n",
g_gc_version_info.MinorVersion, GC_INTERFACE_MINOR_VERSION));
}
LOG((LF_GC, LL_INFO100, "Loaded GC identifying itself with name `%s`\n", g_gc_version_info.Name));
g_gc_load_status = GC_LOAD_STATUS_DONE_VERSION_CHECK;
GC_InitializeFunction initFunc = (GC_InitializeFunction)GetProcAddress(hMod, "GC_Initialize");
if (!initFunc)
{
HRESULT err = GetLastError();
LOG((LF_GC, LL_FATALERROR, "Load of `GC_Initialize` from standalone GC failed\n"));
return __HRESULT_FROM_WIN32(err);
}
g_gc_load_status = GC_LOAD_STATUS_GET_INITIALIZE;
IGCHeap* heap;
IGCHandleManager* manager;
HRESULT initResult = initFunc(gcToClr, &heap, &manager, &g_gc_dac_vars);
if (initResult == S_OK)
{
PTR_VOID pGcModuleBase;
#if TARGET_WINDOWS
pGcModuleBase = (PTR_VOID)hMod;
#else
pGcModuleBase = (PTR_VOID)PAL_GetSymbolModuleBase((PVOID)initFunc);
#endif
FinalizeLoad(heap, manager, pGcModuleBase);
}
else
{
LOG((LF_GC, LL_FATALERROR, "GC initialization failed with HR = 0x%X\n", initResult));
}
return initResult;
#endif // FEATURE_STANDALONE_GC
}
// Initializes a non-standalone GC. The protocol for initializing a non-standalone GC
// is similar to loading a standalone one, except that the GC_VersionInfo and
// GC_Initialize symbols are linked to directory and thus don't need to be loaded.
//
// The major and minor versions are still checked in debug builds - it must be the case
// that the GC and EE agree on a shared version number because they are built from
// the same sources.
HRESULT InitializeDefaultGC()
{
LIMITED_METHOD_CONTRACT;
LOG((LF_GC, LL_INFO100, "Standalone GC location not provided, using provided GC\n"));
g_gc_load_status = GC_LOAD_STATUS_DONE_LOAD;
GC_VersionInfo(&g_gc_version_info);
g_gc_load_status = GC_LOAD_STATUS_CALL_VERSIONINFO;
// the default GC builds with the rest of the EE. By definition, it must have been
// built with the same interface version.
assert(g_gc_version_info.MajorVersion == GC_INTERFACE_MAJOR_VERSION);
assert(g_gc_version_info.MinorVersion == GC_INTERFACE_MINOR_VERSION);
g_gc_load_status = GC_LOAD_STATUS_DONE_VERSION_CHECK;
IGCHeap* heap;
IGCHandleManager* manager;
HRESULT initResult = GC_Initialize(nullptr, &heap, &manager, &g_gc_dac_vars);
if (initResult == S_OK)
{
FinalizeLoad(heap, manager, GetClrModuleBase());
}
else
{
LOG((LF_GC, LL_FATALERROR, "GC initialization failed with HR = 0x%X\n", initResult));
}
return initResult;
}
} // anonymous namespace
// Loads (if necessary) and initializes the GC. If using a standalone GC,
// it loads the library containing it and dynamically loads the GC entry point.
// If using a non-standalone GC, it invokes the GC entry point directly.
HRESULT GCHeapUtilities::LoadAndInitialize()
{
LIMITED_METHOD_CONTRACT;
// When running on a single-proc Intel system, it's more efficient to use a single global
// allocation context for SOH allocations than to use one for every thread.
#if (defined(TARGET_X86) || defined(TARGET_AMD64)) && !defined(TARGET_UNIX)
#if DEBUG
bool useGlobalAllocationContext = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCUseGlobalAllocationContext) != 0);
#else
bool useGlobalAllocationContext = false;
#endif
s_useThreadAllocationContexts = !useGlobalAllocationContext && (IsServerHeap() || ::g_SystemInfo.dwNumberOfProcessors != 1 || CPUGroupInfo::CanEnableGCCPUGroups());
#else
s_useThreadAllocationContexts = true;
#endif
// we should only call this once on startup. Attempting to load a GC
// twice is an error.
assert(g_pGCHeap == nullptr);
// we should not have attempted to load a GC already. Attempting a
// load after the first load already failed is an error.
assert(g_gc_load_status == GC_LOAD_STATUS_BEFORE_START);
g_gc_load_status = GC_LOAD_STATUS_START;
LPCWSTR standaloneGCName = Configuration::GetKnobStringValue(W("System.GC.Name"), CLRConfig::EXTERNAL_GCName);
LPCWSTR standaloneGCPath = Configuration::GetKnobStringValue(W("System.GC.Path"), CLRConfig::EXTERNAL_GCPath);
g_gc_dac_vars.major_version_number = GC_INTERFACE_MAJOR_VERSION;
g_gc_dac_vars.minor_version_number = GC_INTERFACE_MINOR_VERSION;
if (!standaloneGCName && !standaloneGCPath)
{
return InitializeDefaultGC();
}
else
{
return LoadAndInitializeGC(standaloneGCName, standaloneGCPath);
}
}
void GCHeapUtilities::RecordEventStateChange(bool isPublicProvider, GCEventKeyword keywords, GCEventLevel level)
{
CONTRACTL {
MODE_ANY;
NOTHROW;
GC_NOTRIGGER;
CAN_TAKE_LOCK;
} CONTRACTL_END;
StashKeywordAndLevel(isPublicProvider, keywords, level);
}
#endif // DACCESS_COMPILE