Skip to content

Commit ffffc4b

Browse files
authored
CoreCLR initialization failures logging (#78790)
* CoreCLR initialization failures logging This change adds detecting and logging of failures during coreclr initialization. For logging, it uses a new host API `coreclr_set_error_writer` to register a callback to report the errors to the host. The hosts have support for optional usage of this API so that they can work with older runtime versions as well. The logging checks and reports failures with: * System.Private.CoreLib.dll * GC initialization * JIT initialization * libSystem.Native.so/dylib on Unix The logging messages should allow customers to self-diagnose the issues causing the failures. * Reflect PR feedback and a bit of cleanup * revert the try_get_export to not to have the isOptional argument * Unify the error logging in JIT loading to single variadic macro * Also fixes Unix build, the code in gcenv.unix.cpp cannot use the GCToEEInterface * Add single file host detection * Fix libSystem.Native check location * Add the coreclr_set_error_writer API to Mono * Fix Mono x86 missing calling convention spec * Fix error code returned when the S.P.C. is not found
1 parent c6d6b19 commit ffffc4b

26 files changed

+407
-166
lines changed

src/coreclr/binder/utils.cpp

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -150,56 +150,68 @@ namespace BINDER_SPACE
150150
isNativeImage = false;
151151

152152
HRESULT pathResult = S_OK;
153-
IF_FAIL_GO(pathResult = GetNextPath(paths, startPos, outPath));
154-
if (pathResult == S_FALSE)
153+
while(true)
155154
{
156-
return S_FALSE;
157-
}
158-
159-
if (Path::IsRelative(outPath))
160-
{
161-
GO_WITH_HRESULT(E_INVALIDARG);
162-
}
163-
164-
{
165-
// Find the beginning of the simple name
166-
SString::CIterator iSimpleNameStart = outPath.End();
167-
168-
if (!outPath.FindBack(iSimpleNameStart, DIRECTORY_SEPARATOR_CHAR_W))
169-
{
170-
iSimpleNameStart = outPath.Begin();
171-
}
172-
else
155+
IF_FAIL_GO(pathResult = GetNextPath(paths, startPos, outPath));
156+
if (pathResult == S_FALSE)
173157
{
174-
// Advance past the directory separator to the first character of the file name
175-
iSimpleNameStart++;
158+
return S_FALSE;
176159
}
177160

178-
if (iSimpleNameStart == outPath.End())
161+
if (Path::IsRelative(outPath))
179162
{
180163
GO_WITH_HRESULT(E_INVALIDARG);
181164
}
182165

183-
const SString sNiDll(SString::Literal, W(".ni.dll"));
184-
const SString sNiExe(SString::Literal, W(".ni.exe"));
185-
const SString sDll(SString::Literal, W(".dll"));
186-
const SString sExe(SString::Literal, W(".exe"));
187-
188-
if (!dllOnly && (outPath.EndsWithCaseInsensitive(sNiDll) ||
189-
outPath.EndsWithCaseInsensitive(sNiExe)))
190-
{
191-
simpleName.Set(outPath, iSimpleNameStart, outPath.End() - 7);
192-
isNativeImage = true;
193-
}
194-
else if (outPath.EndsWithCaseInsensitive(sDll) ||
195-
(!dllOnly && outPath.EndsWithCaseInsensitive(sExe)))
196-
{
197-
simpleName.Set(outPath, iSimpleNameStart, outPath.End() - 4);
198-
}
199-
else
200166
{
201-
// Invalid filename
202-
GO_WITH_HRESULT(E_INVALIDARG);
167+
// Find the beginning of the simple name
168+
SString::CIterator iSimpleNameStart = outPath.End();
169+
170+
if (!outPath.FindBack(iSimpleNameStart, DIRECTORY_SEPARATOR_CHAR_W))
171+
{
172+
iSimpleNameStart = outPath.Begin();
173+
}
174+
else
175+
{
176+
// Advance past the directory separator to the first character of the file name
177+
iSimpleNameStart++;
178+
}
179+
180+
if (iSimpleNameStart == outPath.End())
181+
{
182+
GO_WITH_HRESULT(E_INVALIDARG);
183+
}
184+
185+
const SString sNiDll(SString::Literal, W(".ni.dll"));
186+
const SString sNiExe(SString::Literal, W(".ni.exe"));
187+
const SString sDll(SString::Literal, W(".dll"));
188+
const SString sExe(SString::Literal, W(".exe"));
189+
190+
if (dllOnly && (outPath.EndsWithCaseInsensitive(sExe) ||
191+
outPath.EndsWithCaseInsensitive(sNiExe)))
192+
{
193+
// Skip exe files when the caller requested only dlls
194+
continue;
195+
}
196+
197+
if (outPath.EndsWithCaseInsensitive(sNiDll) ||
198+
outPath.EndsWithCaseInsensitive(sNiExe))
199+
{
200+
simpleName.Set(outPath, iSimpleNameStart, outPath.End() - 7);
201+
isNativeImage = true;
202+
}
203+
else if (outPath.EndsWithCaseInsensitive(sDll) ||
204+
outPath.EndsWithCaseInsensitive(sExe))
205+
{
206+
simpleName.Set(outPath, iSimpleNameStart, outPath.End() - 4);
207+
}
208+
else
209+
{
210+
// Invalid filename
211+
GO_WITH_HRESULT(E_INVALIDARG);
212+
}
213+
214+
break;
203215
}
204216
}
205217

src/coreclr/dlls/mscoree/exports.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,26 @@ static void ConvertConfigPropertiesToUnicode(
181181
*propertyValuesWRef = propertyValuesW;
182182
}
183183

184+
coreclr_error_writer_callback_fn g_errorWriter = nullptr;
185+
186+
//
187+
// Set callback for writing error logging
188+
//
189+
// Parameters:
190+
// errorWriter - callback that will be called for each line of the error info
191+
// - passing in NULL removes a callback that was previously set
192+
//
193+
// Returns:
194+
// S_OK
195+
//
196+
extern "C"
197+
DLLEXPORT
198+
int coreclr_set_error_writer(coreclr_error_writer_callback_fn error_writer)
199+
{
200+
g_errorWriter = error_writer;
201+
return S_OK;
202+
}
203+
184204
#ifdef FEATURE_GDBJIT
185205
GetInfoForMethodDelegate getInfoForMethodDelegate = NULL;
186206
extern "C" int coreclr_create_delegate(void*, unsigned int, const char*, const char*, const char*, void**);
@@ -462,3 +482,16 @@ int coreclr_execute_assembly(
462482

463483
return hr;
464484
}
485+
486+
void LogErrorToHost(const char* format, ...)
487+
{
488+
if (g_errorWriter != NULL)
489+
{
490+
char messageBuffer[1024];
491+
va_list args;
492+
va_start(args, format);
493+
_vsnprintf_s(messageBuffer, ARRAY_SIZE(messageBuffer), _TRUNCATE, format, args);
494+
g_errorWriter(messageBuffer);
495+
va_end(args);
496+
}
497+
}

src/coreclr/dlls/mscoree/mscorwks_ntdef.src

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ EXPORTS
2222
coreclr_create_delegate
2323
coreclr_execute_assembly
2424
coreclr_initialize
25+
coreclr_set_error_writer
2526
coreclr_shutdown
2627
coreclr_shutdown_2
2728

src/coreclr/dlls/mscoree/mscorwks_unixexports.src

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
coreclr_create_delegate
33
coreclr_execute_assembly
44
coreclr_initialize
5+
coreclr_set_error_writer
56
coreclr_shutdown
67
coreclr_shutdown_2
78

src/coreclr/gc/env/gcenv.ee.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ class GCToEEInterface
9494
static uint32_t GetCurrentProcessCpuCount();
9595

9696
static void DiagAddNewRegion(int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved);
97+
98+
static void LogErrorToHost(const char *message);
9799
};
98100

99101
#endif // __GCENV_EE_H__

src/coreclr/gc/gc.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13575,13 +13575,17 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size,
1357513575
gc_log = CreateLogFile(GCConfig::GetLogFile(), false);
1357613576

1357713577
if (gc_log == NULL)
13578+
{
13579+
GCToEEInterface::LogErrorToHost("Cannot create log file");
1357813580
return E_FAIL;
13581+
}
1357913582

1358013583
// GCLogFileSize in MBs.
1358113584
gc_log_file_size = static_cast<size_t>(GCConfig::GetLogFileSize());
1358213585

1358313586
if (gc_log_file_size <= 0 || gc_log_file_size > 500)
1358413587
{
13588+
GCToEEInterface::LogErrorToHost("Invalid log file size (valid size needs to be larger than 0 and smaller than 500)");
1358513589
fclose (gc_log);
1358613590
return E_FAIL;
1358713591
}
@@ -13591,7 +13595,7 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size,
1359113595
if (!gc_log_buffer)
1359213596
{
1359313597
fclose(gc_log);
13594-
return E_FAIL;
13598+
return E_OUTOFMEMORY;
1359513599
}
1359613600

1359713601
memset (gc_log_buffer, '*', gc_log_buffer_size);
@@ -13606,13 +13610,16 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size,
1360613610
gc_config_log = CreateLogFile(GCConfig::GetConfigLogFile(), true);
1360713611

1360813612
if (gc_config_log == NULL)
13613+
{
13614+
GCToEEInterface::LogErrorToHost("Cannot create log file");
1360913615
return E_FAIL;
13616+
}
1361013617

1361113618
gc_config_log_buffer = new (nothrow) uint8_t [gc_config_log_buffer_size];
1361213619
if (!gc_config_log_buffer)
1361313620
{
1361413621
fclose(gc_config_log);
13615-
return E_FAIL;
13622+
return E_OUTOFMEMORY;
1361613623
}
1361713624

1361813625
compact_ratio = static_cast<int>(GCConfig::GetCompactRatio());
@@ -13739,6 +13746,7 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size,
1373913746
else
1374013747
{
1374113748
assert (!"cannot use regions without specifying the range!!!");
13749+
GCToEEInterface::LogErrorToHost("Cannot use regions without specifying the range (using DOTNET_GCRegionRange)");
1374213750
return E_FAIL;
1374313751
}
1374413752
#else //USE_REGIONS
@@ -13862,6 +13870,7 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size,
1386213870

1386313871
if (!init_semi_shared())
1386413872
{
13873+
GCToEEInterface::LogErrorToHost("PER_HEAP_ISOLATED data members initialization failed");
1386513874
hres = E_FAIL;
1386613875
}
1386713876

@@ -45556,6 +45565,7 @@ HRESULT GCHeap::Initialize()
4555645565

4555745566
if (!WaitForGCEvent->CreateManualEventNoThrow(TRUE))
4555845567
{
45568+
GCToEEInterface::LogErrorToHost("Creation of WaitForGCEvent failed");
4555945569
return E_FAIL;
4556045570
}
4556145571

@@ -45637,9 +45647,15 @@ HRESULT GCHeap::Initialize()
4563745647
int hb_info_size_per_node = hb_info_size_per_proc * procs_per_numa_node;
4563845648
uint8_t* numa_mem = (uint8_t*)GCToOSInterface::VirtualReserve (hb_info_size_per_node, 0, 0, numa_node_index);
4563945649
if (!numa_mem)
45650+
{
45651+
GCToEEInterface::LogErrorToHost("Reservation of numa_mem failed");
4564045652
return E_FAIL;
45653+
}
4564145654
if (!GCToOSInterface::VirtualCommit (numa_mem, hb_info_size_per_node, numa_node_index))
45655+
{
45656+
GCToEEInterface::LogErrorToHost("Commit of numa_mem failed");
4564245657
return E_FAIL;
45658+
}
4564345659

4564445660
heap_balance_info_proc* hb_info_procs = (heap_balance_info_proc*)numa_mem;
4564545661
hb_info_numa_nodes[numa_node_index].hb_info_procs = hb_info_procs;

src/coreclr/gc/gccommon.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ IGCHeapInternal* g_theGCHeap;
1717
IGCHandleManager* g_theGCHandleManager;
1818

1919
#ifdef BUILD_AS_STANDALONE
20-
IGCToCLR* g_theGCToCLR;
20+
IGCToCLR2* g_theGCToCLR;
2121
VersionInfo g_runtimeSupportedVersion;
2222
#endif // BUILD_AS_STANDALONE
2323

src/coreclr/gc/gcenv.ee.standalone.inl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
// The singular interface instance. All calls in GCToEEInterface
1111
// will be forwarded to this interface instance.
12-
extern IGCToCLR* g_theGCToCLR;
12+
extern IGCToCLR2* g_theGCToCLR;
1313

1414
// GC version that the current runtime supports
1515
extern VersionInfo g_runtimeSupportedVersion;
@@ -314,4 +314,12 @@ inline void GCToEEInterface::DiagAddNewRegion(int generation, uint8_t* rangeStar
314314
g_theGCToCLR->DiagAddNewRegion(generation, rangeStart, rangeEnd, rangeEndReserved);
315315
}
316316

317+
inline void GCToEEInterface::LogErrorToHost(const char *message)
318+
{
319+
if (g_runtimeSupportedVersion.MajorVersion >= GC_INTERFACE2_MAJOR_VERSION)
320+
{
321+
g_theGCToCLR->LogErrorToHost(message);
322+
}
323+
}
324+
317325
#endif // __GCTOENV_EE_STANDALONE_INL__

src/coreclr/gc/gcinterface.ee.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,4 +448,11 @@ class IGCToCLR {
448448
void DiagAddNewRegion(int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved) = 0;
449449
};
450450

451+
class IGCToCLR2 : public IGCToCLR {
452+
public:
453+
454+
virtual
455+
void LogErrorToHost(const char *message) = 0;
456+
};
457+
451458
#endif // _GCINTERFACE_EE_H_

src/coreclr/gc/gcinterface.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66

77
// The major version of the GC/EE interface. Breaking changes to this interface
88
// require bumps in the major version number.
9-
#define GC_INTERFACE_MAJOR_VERSION 5
9+
#define GC_INTERFACE_MAJOR_VERSION 6
1010

1111
// The minor version of the GC/EE interface. Non-breaking changes are required
1212
// to bump the minor version number. GCs and EEs with minor version number
1313
// mismatches can still interopate correctly, with some care.
1414
#define GC_INTERFACE_MINOR_VERSION 1
1515

16+
// The major version of the GC/EE interface. Breaking changes to this interface
17+
// require bumps in the major version number.
18+
#define GC_INTERFACE2_MAJOR_VERSION 6
19+
1620
struct ScanContext;
1721
struct gc_alloc_context;
1822
class CrawlFrame;

0 commit comments

Comments
 (0)