diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index 9b8e1ee3dfd704..0221afafe08c9b 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -37,6 +37,7 @@ using std::nothrow; #include #include +#include #include #include "clrnt.h" diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index 3d251f4e7bd192..9df0a0c29e56be 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -29,9 +29,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #define DLLEXPORT #endif // !DLLEXPORT -#if defined(HOST_ANDROID) -#include -#endif +#include "minipal/log.h" /*****************************************************************************/ @@ -150,16 +148,19 @@ FILE* jitstdout() // Like printf/logf, but only outputs to jitstdout -- skips call back into EE. int jitprintf(const char* fmt, ...) { + int status; va_list vl; va_start(vl, fmt); -#if defined(HOST_ANDROID) - int status = jitstdout() == procstdout() - ? __android_log_vprint(ANDROID_LOG_VERBOSE, MAIN_CLR_MODULE_NAME_A, fmt, vl) - : vfprintf(jitstdout(), fmt, vl); -#else - int status = vfprintf(jitstdout(), fmt, vl); -#endif + if (jitstdout() == procstdout()) + { + status = minipal_log_vprint_verbose(fmt, vl); + } + else + { + status = vfprintf(jitstdout(), fmt, vl); + } va_end(vl); + return status; } diff --git a/src/coreclr/jit/error.cpp b/src/coreclr/jit/error.cpp index 5ae6cea056efeb..d04585cef94965 100644 --- a/src/coreclr/jit/error.cpp +++ b/src/coreclr/jit/error.cpp @@ -15,6 +15,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif #include "compiler.h" +#include "minipal/log.h" #if MEASURE_FATAL unsigned fatal_badCode; @@ -318,7 +319,14 @@ int vflogf(FILE* file, const char* fmt, va_list args) // 0-length string means flush if (fmt[0] == '\0') { - fflush(file); + if (file == procstdout()) + { + minipal_log_flush_verbose(); + } + else + { + fflush(file); + } return 0; } @@ -331,8 +339,15 @@ int vflogf(FILE* file, const char* fmt, va_list args) OutputDebugStringA(buffer); } - // We use fputs here so that this executes as fast a possible - fputs(&buffer[0], file); + if (file == procstdout()) + { + minipal_log_write_verbose(buffer); + } + else + { + fputs(&buffer[0], file); + } + return written; } diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 1688d3b16779bf..e9596e2ea9aba7 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -2452,6 +2452,16 @@ PAL_GenerateCoreDump( (no return value) --*/ +#ifdef HOST_ANDROID +#include +VOID +PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, bool serialize) +{ + // TODO: Dump all managed threads callstacks into logcat and/or file? + // TODO: Dump stress log into logcat and/or file when enabled? + minipal_log_write_fatal("Aborting process.\n"); +} +#else VOID PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, bool serialize) { @@ -2520,6 +2530,7 @@ PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, bool serialize) free(signalAddressArg); } } +#endif /*++ Function: diff --git a/src/coreclr/utilcode/log.cpp b/src/coreclr/utilcode/log.cpp index 56ddf6aef9c20e..b95523fc33a218 100644 --- a/src/coreclr/utilcode/log.cpp +++ b/src/coreclr/utilcode/log.cpp @@ -372,10 +372,9 @@ VOID LogSpewAlwaysValist(const char *fmt, va_list args) if (LogFlags & LOG_ENABLE_CONSOLE_LOGGING) { - WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), pBuffer, buflen, &written, 0); - //@TODO ...Unnecessary to flush console? + minipal_log_write_info(pBuffer); if (LogFlags & LOG_ENABLE_FLUSH_FILE) - FlushFileBuffers( GetStdHandle(STD_OUTPUT_HANDLE) ); + minipal_log_sync_info(); } if (LogFlags & LOG_ENABLE_DEBUGGER_LOGGING) @@ -415,6 +414,5 @@ VOID LogSpewAlways (const char *fmt, ... ) LogSpewValist (LF_ALWAYS, LL_ALWAYS, fmt, args); va_end(args); } - #endif // LOGGING diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index fe435ce8d569dc..b3f4a568a6696f 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -48,7 +48,7 @@ void SafeExitProcess(UINT exitCode, ShutdownCompleteAction sca = SCA_ExitProcess { _ASSERTE(!"Bad Exit value"); FAULT_NOT_FATAL(); // if we OOM we can simply give up - fprintf(stderr, "Error 0x%08x.\n\nBreakOnBadExit: returning bad exit code.", exitCode); + minipal_log_print_error("Error 0x%08x.\n\nBreakOnBadExit: returning bad exit code.", exitCode); DebugBreak(); } } @@ -233,8 +233,9 @@ class CallStackLogger MethodDesc* pMD = m_frames[index]; TypeString::AppendMethodInternal(str, pMD, TypeString::FormatNamespace|TypeString::FormatFullInst|TypeString::FormatSignature); + str.Append(W("\n")); + PrintToStdErrW(str.GetUnicode()); - PrintToStdErrA("\n"); } public: @@ -265,6 +266,7 @@ class CallStackLogger repeatStr.AppendPrintf("Repeated %d times:\n", m_largestCommonStartRepeat); PrintToStdErrW(repeatStr.GetUnicode()); + PrintToStdErrA("--------------------------------\n"); for (int i = 0; i < m_largestCommonStartLength; i++) { @@ -373,11 +375,11 @@ void LogInfoForFatalError(UINT exitCode, LPCWSTR pszMessage, PEXCEPTION_POINTERS { if (exitCode == (UINT)COR_E_FAILFAST) { - PrintToStdErrA("Process terminated. "); + PrintToStdErrA("Process terminated.\n"); } else { - PrintToStdErrA("Fatal error. "); + PrintToStdErrA("Fatal error.\n"); } if (errorSource != NULL) @@ -393,9 +395,9 @@ void LogInfoForFatalError(UINT exitCode, LPCWSTR pszMessage, PEXCEPTION_POINTERS else { // If no message was passed in, generate it from the exitCode - SString exitCodeMessage; + InlineSString<256> exitCodeMessage; GetHRMsg(exitCode, exitCodeMessage); - PrintToStdErrW((LPCWSTR)exitCodeMessage); + PrintToStdErrW(exitCodeMessage.GetUnicode()); } PrintToStdErrA("\n"); diff --git a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h index fb6c0c3feeda09..2be358789efc59 100644 --- a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h +++ b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h @@ -11,6 +11,7 @@ #include "ep-rt-coreclr.h" #include #include +#include #include #include #include @@ -404,10 +405,10 @@ ds_rt_server_log_pause_message (void) uint32_t port_suspended = ds_rt_config_value_get_default_port_suspend(); - printf("The runtime has been configured to pause during startup and is awaiting a Diagnostics IPC ResumeStartup command from a Diagnostic Port.\n"); - printf("DOTNET_%s=\"%s\"\n", diagPortsName, ports == nullptr ? "" : ports); - printf("DOTNET_DefaultDiagnosticPortSuspend=%u\n", port_suspended); - fflush(stdout); + minipal_log_print_info("The runtime has been configured to pause during startup and is awaiting a Diagnostics IPC ResumeStartup command from a Diagnostic Port.\n"); + minipal_log_print_info("DOTNET_%s=\"%s\"\n", diagPortsName, ports == nullptr ? "" : ports); + minipal_log_print_info("DOTNET_DefaultDiagnosticPortSuspend=%u\n", port_suspended); + minipal_log_flush_info(); } #endif /* ENABLE_PERFTRACING */ diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 7bd720baf28676..2bef68c8814e95 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -4756,8 +4756,6 @@ LONG __stdcall COMUnhandledExceptionFilter( // EXCEPTION_CONTINUE_SEARCH or #pragma code_seg(pop, uef) #endif // !TARGET_UNIX -void PrintStackTraceToStdout(); - static SString GetExceptionMessageWrapper(Thread* pThread, OBJECTREF throwable) { STATIC_CONTRACT_THROWS; @@ -4788,17 +4786,17 @@ DefaultCatchHandlerExceptionMessageWorker(Thread* pThread, wcsncpy_s(buf, buf_size, SZ_UNHANDLED_EXCEPTION, SZ_UNHANDLED_EXCEPTION_CHARLEN); } - PrintToStdErrW(buf); - PrintToStdErrA(" "); - - SString message = GetExceptionMessageWrapper(pThread, throwable); + SString message(buf); + SString exceptionMessage = GetExceptionMessageWrapper(pThread, throwable); - if (!message.IsEmpty()) + message.Append(W(" ")); + if (!exceptionMessage.IsEmpty()) { - PrintToStdErrW(message); + message.Append(exceptionMessage); } + message.Append(W("\n")); - PrintToStdErrA("\n"); + PrintToStdErrW(message.GetUnicode()); #if defined(FEATURE_EVENT_TRACE) && !defined(TARGET_UNIX) // Send the log to Windows Event Log @@ -5005,10 +5003,13 @@ DefaultCatchHandler(PEXCEPTION_POINTERS pExceptionPointers, EX_CATCH { LOG((LF_EH, LL_INFO10, "Exception occurred while processing uncaught exception\n")); - UtilLoadStringRC(IDS_EE_EXCEPTION_TOSTRING_FAILED, buf, buf_size); - PrintToStdErrA("\n "); + + _ASSERTE(buf_size > 6); + wcscpy_s(buf, buf_size, W("\n ")); + UtilLoadStringRC(IDS_EE_EXCEPTION_TOSTRING_FAILED, buf + 4, buf_size - 6); + wcscat_s(buf, buf_size, W("\n")); + PrintToStdErrW(buf); - PrintToStdErrA("\n"); } EX_END_CATCH(SwallowAllExceptions); } diff --git a/src/coreclr/vm/util.cpp b/src/coreclr/vm/util.cpp index 4fc2242095a37b..06216ef18fb9d8 100644 --- a/src/coreclr/vm/util.cpp +++ b/src/coreclr/vm/util.cpp @@ -15,10 +15,6 @@ #ifndef DACCESS_COMPILE -#if defined(TARGET_ANDROID) -#include -#endif // defined(TARGET_ANDROID) - thread_local size_t t_ThreadType; void ClrFlsSetThreadType(TlsThreadTypeFlag flag) @@ -124,8 +120,7 @@ LPVOID CQuickHeap::Alloc(UINT sz) // Output functions that avoid the crt's. //---------------------------------------------------------------------------- -static -void NPrintToHandleA(HANDLE Handle, const char *pszString, size_t BytesToWrite) +void PrintToStdErrA(const char *pszString) { CONTRACTL { @@ -135,58 +130,7 @@ void NPrintToHandleA(HANDLE Handle, const char *pszString, size_t BytesToWrite) } CONTRACTL_END - if (Handle == INVALID_HANDLE_VALUE || Handle == NULL) - return; - - BOOL success; - DWORD dwBytesWritten; - const size_t maxWriteFileSize = 32767; // This is somewhat arbitrary limit, but 2**16-1 doesn't work - - while (BytesToWrite > 0) { - DWORD dwChunkToWrite = (DWORD) min(BytesToWrite, maxWriteFileSize); - - // Try to write to handle. If this is not a CUI app, then this is probably - // not going to work unless the dev took special pains to set their own console - // handle during CreateProcess. So try it, but don't yell if it doesn't work in - // that case. Also, if we redirect stdout to a pipe then the pipe breaks (ie, we - // write to something like the UNIX head command), don't complain. - success = WriteFile(Handle, pszString, dwChunkToWrite, &dwBytesWritten, NULL); - if (!success) - { -#if defined(_DEBUG) - // This can happen if stdout is a closed pipe. This might not help - // much, but we'll have half a chance of seeing this. - OutputDebugStringA("CLR: Writing out an unhandled exception to stdout failed!\n"); - OutputDebugStringA(pszString); -#endif //_DEBUG - - break; - } - else { - _ASSERTE(dwBytesWritten == dwChunkToWrite); - } - pszString = pszString + dwChunkToWrite; - BytesToWrite -= dwChunkToWrite; - } - -} - -void PrintToStdErrA(const char *pszString) { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - FORBID_FAULT; - } - CONTRACTL_END - -#if defined(TARGET_ANDROID) - __android_log_write(ANDROID_LOG_FATAL, MAIN_CLR_MODULE_NAME_A, pszString); -#else - HANDLE Handle = GetStdHandle(STD_ERROR_HANDLE); - size_t len = strlen(pszString); - NPrintToHandleA(Handle, pszString, len); -#endif // defined(TARGET_ANDROID) + minipal_log_write_error(pszString); } void PrintToStdErrW(const WCHAR *pwzString) diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index f0160b4974df59..834c4881aac512 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -882,7 +882,6 @@ endif() ### End of OS specific checks include_directories("${CLR_SRC_NATIVE_DIR}") -add_subdirectory(${CLR_SRC_NATIVE_DIR}/minipal minipal) if(NOT DISABLE_LIBS) set(INSTALL_MONO_API 1) diff --git a/src/mono/mono/CMakeLists.txt b/src/mono/mono/CMakeLists.txt index c59470996f5825..9368f312502d0e 100644 --- a/src/mono/mono/CMakeLists.txt +++ b/src/mono/mono/CMakeLists.txt @@ -1,6 +1,7 @@ project(mono) set(subdirs + minipal eglib utils sgen diff --git a/src/mono/mono/minipal/CMakeLists.txt b/src/mono/mono/minipal/CMakeLists.txt new file mode 100644 index 00000000000000..2b862bd623b3e3 --- /dev/null +++ b/src/mono/mono/minipal/CMakeLists.txt @@ -0,0 +1,10 @@ + +if (HOST_WIN32) + add_definitions(-DHOST_WINDOWS) +endif (HOST_WIN32) + +if (HOST_DARWIN OR HOST_ANDROID OR HOST_LINUX OR HOST_FREEBSD OR HOST_SOLARIS OR HOST_HAIKU OR HOST_BROWSER) + add_definitions(-DHOST_UNIX) +endif (HOST_DARWIN OR HOST_ANDROID OR HOST_LINUX OR HOST_FREEBSD OR HOST_SOLARIS OR HOST_HAIKU OR HOST_BROWSER) + +add_subdirectory(${CLR_SRC_NATIVE_DIR}/minipal minipal) diff --git a/src/native/minipal/CMakeLists.txt b/src/native/minipal/CMakeLists.txt index 8ebacd8cd6f404..f27ad0d5fea50e 100644 --- a/src/native/minipal/CMakeLists.txt +++ b/src/native/minipal/CMakeLists.txt @@ -10,6 +10,7 @@ set(SOURCES unicodedata.c utf8.c xoshiro128pp.c + log.c ) # Provide an object library for scenarios where we ship static libraries @@ -36,6 +37,10 @@ endif() add_library(minipal STATIC) target_link_libraries(minipal PRIVATE minipal_objects) +if(CLR_CMAKE_HOST_ANDROID) + target_link_libraries(minipal PRIVATE log) +endif(CLR_CMAKE_HOST_ANDROID) + add_library(minipal_sanitizer_support OBJECT sansupport.c) # Exclude this target from the default build as we may not have sanitzer headers available diff --git a/src/native/minipal/configure.cmake b/src/native/minipal/configure.cmake index 8a5dd2346a0e02..448b03ba7a250c 100644 --- a/src/native/minipal/configure.cmake +++ b/src/native/minipal/configure.cmake @@ -2,18 +2,16 @@ include(CheckFunctionExists) include(CheckIncludeFiles) include(CheckSymbolExists) +check_include_files("windows.h" HAVE_WINDOWS_H) +check_include_files("windows.h;bcrypt.h" HAVE_BCRYPT_H) check_include_files("sys/auxv.h;asm/hwcap.h" HAVE_AUXV_HWCAP_H) + check_function_exists(sysctlbyname HAVE_SYSCTLBYNAME) +check_function_exists(fsync HAVE_FSYNC) check_symbol_exists(arc4random_buf "stdlib.h" HAVE_ARC4RANDOM_BUF) check_symbol_exists(O_CLOEXEC fcntl.h HAVE_O_CLOEXEC) -check_include_files("windows.h" HAVE_WINDOWS_H) -check_include_files("windows.h;bcrypt.h" HAVE_BCRYPT_H) - -check_symbol_exists( - clock_gettime_nsec_np - time.h - HAVE_CLOCK_GETTIME_NSEC_NP) +check_symbol_exists(clock_gettime_nsec_np time.h HAVE_CLOCK_GETTIME_NSEC_NP) if(CMAKE_C_BYTE_ORDER STREQUAL "BIG_ENDIAN") set(BIGENDIAN 1) diff --git a/src/native/minipal/guid.c b/src/native/minipal/guid.c index a3620c10783c3a..8169e738bb6acf 100644 --- a/src/native/minipal/guid.c +++ b/src/native/minipal/guid.c @@ -8,6 +8,7 @@ #include #ifdef HOST_WINDOWS #include +#include #endif // See RFC-4122 section 4.4 on creation of random GUID. diff --git a/src/native/minipal/log.c b/src/native/minipal/log.c new file mode 100644 index 00000000000000..29ff23262e7676 --- /dev/null +++ b/src/native/minipal/log.c @@ -0,0 +1,329 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "minipalconfig.h" +#include "log.h" +#include +#include +#include + +#ifndef MINIPAL_LOG_RUNTIME_TAG +#define MINIPAL_LOG_RUNTIME_TAG "DOTNET" +#endif + +#ifdef HOST_ANDROID +#include +#include + +// Android defines its LOGGER_ENTRY_MAX_PAYLOAD to 4068 bytes. +// Use 4000 bytes to include some slack for future changes to LOGGER_ENTRY_MAX_PAYLOAD. +#define MINIPAL_LOG_MAX_PAYLOAD 4000 + +// Android defines its internal log buffer used in __android_log_vprint to 1024 bytes. +// Use same internal stack buffer size avoiding dynamic memory allocation for majority of logging. +#define MINIPAL_LOG_BUF_SIZE 1024 + +static int android_log_flag(minipal_log_flags flags) +{ + switch(flags) + { + case minipal_log_flags_fatal: + return ANDROID_LOG_FATAL; + case minipal_log_flags_error: + return ANDROID_LOG_ERROR; + case minipal_log_flags_warning: + return ANDROID_LOG_WARN; + case minipal_log_flags_info: + return ANDROID_LOG_INFO; + case minipal_log_flags_debug: + return ANDROID_LOG_DEBUG; + case minipal_log_flags_verbose: + return ANDROID_LOG_VERBOSE; + default: + return ANDROID_LOG_UNKNOWN; + } +} + +static size_t log_write(minipal_log_flags flags, const char* msg, size_t msg_len) +{ + if (msg_len == 1 && msg[0] == '\n') + return 0; + + return __android_log_write(android_log_flag(flags), MINIPAL_LOG_RUNTIME_TAG, msg) == 1 ? msg_len : 0; +} + +int minipal_log_print(minipal_log_flags flags, const char* fmt, ... ) +{ + va_list args; + va_start(args, fmt); + int bytes_written = minipal_log_vprint(flags, fmt, args); + va_end(args); + return bytes_written; +} + +int minipal_log_vprint(minipal_log_flags flags, const char* fmt, va_list args) +{ + char stack_buffer[MINIPAL_LOG_BUF_SIZE]; + int bytes_written = 0; + va_list args_copy; + + va_copy(args_copy, args); + + int len = vsnprintf(stack_buffer, sizeof(stack_buffer), fmt, args_copy); + if (len < sizeof(stack_buffer)) + { + bytes_written = minipal_log_write(flags, stack_buffer); + } + else + { + char* dyn_buffer = (char*)malloc(len + 1); + if (dyn_buffer != NULL) + { + vsnprintf(dyn_buffer, len + 1, fmt, args); + bytes_written = minipal_log_write(flags, dyn_buffer); + free(dyn_buffer); + } + } + + va_end(args_copy); + return bytes_written; +} + +void minipal_log_flush(minipal_log_flags flags) +{ +} + +void minipal_log_flush_all(void) +{ +} + +static size_t log_write_line(minipal_log_flags flags, const char* msg, size_t msg_len) +{ + char buffer[MINIPAL_LOG_MAX_PAYLOAD]; + if (msg_len < MINIPAL_LOG_MAX_PAYLOAD) + { + strncpy(buffer, msg, msg_len); + buffer[msg_len] = '\0'; + return log_write(flags, buffer, msg_len); + } + + const char* msg_end = msg + msg_len; + size_t bytes_written = 0; + while (msg < msg_end) + { + ptrdiff_t chunk_size = MINIPAL_LOG_MAX_PAYLOAD - 1; + if (msg_end - msg < chunk_size) + { + chunk_size = msg_end - msg; + } + + strncpy(buffer, msg, chunk_size); + buffer[chunk_size] = '\0'; + bytes_written += log_write(flags, buffer, chunk_size); + msg += chunk_size; + } + + return bytes_written; +} + +int minipal_log_write(minipal_log_flags flags, const char* msg) +{ + assert(msg != NULL && msg[0] != '\0'); + + size_t msg_len = strlen(msg); + const char* msg_end = msg + msg_len; + + if (msg_len < MINIPAL_LOG_MAX_PAYLOAD) + return (int)log_write(flags, msg, msg_len); + + const char* next_msg = NULL; + size_t bytes_written = 0; + for (next_msg = msg; next_msg < msg_end;) + { + const char* next_line_break = strchr(next_msg, '\n'); + if (next_line_break == NULL && (msg_end - next_msg < MINIPAL_LOG_MAX_PAYLOAD)) + { + bytes_written += log_write(flags, next_msg, msg_end - next_msg); + break; + } + else if (next_line_break == NULL) + { + bytes_written += log_write_line(flags, next_msg, msg_end - next_msg); + break; + } + else + { + bytes_written += log_write_line(flags, next_msg, next_line_break - next_msg); + next_msg = next_line_break + 1; + } + } + + return (int)bytes_written; +} + +void minipal_log_sync(minipal_log_flags flags) +{ +} + +void minipal_log_sync_all(void) +{ +} +#else +#include +#include + +#define MINIPAL_LOG_MAX_PAYLOAD 32767 + +static FILE * get_std_file(minipal_log_flags flags) +{ + switch(flags) + { + case minipal_log_flags_fatal: + case minipal_log_flags_error: + return stderr; + case minipal_log_flags_warning: + case minipal_log_flags_info: + case minipal_log_flags_debug: + case minipal_log_flags_verbose: + default: + return stdout; + } +} + +int minipal_log_print(minipal_log_flags flags, const char* fmt, ... ) +{ + va_list args; + va_start(args, fmt); + int status = vfprintf(get_std_file(flags), fmt, args); + va_end(args); + return status; +} + +int minipal_log_vprint(minipal_log_flags flags, const char* fmt, va_list args) +{ + return vfprintf(get_std_file(flags), fmt, args); +} + +void minipal_log_flush(minipal_log_flags flags) +{ + FILE* file = get_std_file(flags); + if (file != NULL) + fflush(file); +} + +void minipal_log_flush_all(void) +{ + minipal_log_flush(minipal_log_flags_error); + minipal_log_flush(minipal_log_flags_info); +} + +#ifdef HOST_WINDOWS +#include +#include +static int sync_file(minipal_log_flags flags) +{ + switch(flags) + { + case minipal_log_flags_fatal: + case minipal_log_flags_error: + FlushFileBuffers(GetStdHandle(STD_ERROR_HANDLE)); + break; + case minipal_log_flags_warning: + case minipal_log_flags_info: + case minipal_log_flags_debug: + case minipal_log_flags_verbose: + default: + FlushFileBuffers(GetStdHandle(STD_OUTPUT_HANDLE)); + break; + } + + return 0; +} +#define fileno _fileno +#define write _write +#elif defined(__APPLE__) +#include +#include +static int sync_file(minipal_log_flags flags) +{ + if (fcntl(fileno(get_std_file(flags)), F_FULLFSYNC) != -1) + return 0; + + return errno; +} +#elif HAVE_FSYNC +#include +static int sync_file(minipal_log_flags flags) +{ + if (fsync(fileno(get_std_file(flags))) == 0) + return 0; + + return errno; +} +#else +#include +static int sync_file(minipal_log_flags flags) +{ + sync(); + return 0; +} +#endif + +static int write_file(int fd, const char* msg, size_t bytes_to_write) +{ + if (fd == -1) + return 0; + + assert(msg != NULL && msg[0] != '\0'); + assert(bytes_to_write < INT_MAX); + + return write(fd, msg, (int)bytes_to_write); +} + +int minipal_log_write(minipal_log_flags flags, const char* msg) +{ + assert(msg != NULL && msg[0] != '\0'); + + size_t bytes_to_write = strlen(msg); + size_t bytes_written = 0; + + int fd = fileno(get_std_file(flags)); + while (bytes_to_write > 0) + { + size_t chunk_to_write = bytes_to_write < MINIPAL_LOG_MAX_PAYLOAD ? bytes_to_write : MINIPAL_LOG_MAX_PAYLOAD; + size_t chunk_written = write_file(fd, msg, chunk_to_write); + + if (chunk_written == 0) + break; + + msg = msg + chunk_written; + bytes_to_write -= chunk_written; + bytes_written += chunk_written; + } + + return (int)bytes_written; +} + +void minipal_log_sync(minipal_log_flags flags) +{ + bool retry = false; + do + { + switch (sync_file(flags)) + { + case EINTR: + retry = true; + break; + default: + retry = false; + break; + } + } while (retry); +} + +void minipal_log_sync_all(void) +{ + minipal_log_sync(minipal_log_flags_error); + minipal_log_sync(minipal_log_flags_info); +} +#endif diff --git a/src/native/minipal/log.h b/src/native/minipal/log.h new file mode 100644 index 00000000000000..e23b3efd4598d7 --- /dev/null +++ b/src/native/minipal/log.h @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef HAVE_MINIPAL_LOG_H +#define HAVE_MINIPAL_LOG_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +typedef enum +{ + minipal_log_flags_fatal = 1 << 1, + minipal_log_flags_error = 1 << 2, + minipal_log_flags_warning = 1 << 3, + minipal_log_flags_info = 1 << 4, + minipal_log_flags_debug = 1 << 5, + minipal_log_flags_verbose = 1 << 6 +} minipal_log_flags; + +#define minipal_log_print_fatal(...) minipal_log_print(minipal_log_flags_fatal, __VA_ARGS__) +#define minipal_log_print_error(...) minipal_log_print(minipal_log_flags_error, __VA_ARGS__) +#define minipal_log_print_info(...) minipal_log_print(minipal_log_flags_info, __VA_ARGS__) +#define minipal_log_print_verbose(...) minipal_log_print(minipal_log_flags_verbose, __VA_ARGS__) +int minipal_log_print(minipal_log_flags flags, const char* fmt, ... ); + +#define minipal_log_vprint_fatal(...) minipal_log_vprint(minipal_log_flags_fatal, __VA_ARGS__) +#define minipal_log_vprint_error(...) minipal_log_vprint(minipal_log_flags_error, __VA_ARGS__) +#define minipal_log_vprint_info(...) minipal_log_vprint(minipal_log_flags_info, __VA_ARGS__) +#define minipal_log_vprint_verbose(...) minipal_log_vprint(minipal_log_flags_verbose, __VA_ARGS__) +int minipal_log_vprint(minipal_log_flags flags, const char* fmt,va_list args); + +#define minipal_log_flush_fatal() minipal_log_flush(minipal_log_flags_fatal) +#define minipal_log_flush_error() minipal_log_flush(minipal_log_flags_error) +#define minipal_log_flush_info() minipal_log_flush(minipal_log_flags_info) +#define minipal_log_flush_verbose() minipal_log_flush(minipal_log_flags_verbose) +void minipal_log_flush(minipal_log_flags flags); +void minipal_log_flush_all(void); + +// None crt, async safe log write. +#define minipal_log_write_fatal(msg) minipal_log_write(minipal_log_flags_fatal, msg) +#define minipal_log_write_error(msg) minipal_log_write(minipal_log_flags_error, msg) +#define minipal_log_write_info(msg) minipal_log_write(minipal_log_flags_info, msg) +#define minipal_log_write_verbose(msg) minipal_log_write(minipal_log_flags_verbose, msg) +int minipal_log_write(minipal_log_flags flags, const char* msg); + +// None crt, async safe log sync. +#define minipal_log_sync_fatal() minipal_log_sync(minipal_log_flags_fatal) +#define minipal_log_sync_error() minipal_log_sync(minipal_log_flags_error) +#define minipal_log_sync_info() minipal_log_sync(minipal_log_flags_info) +#define minipal_log_sync_verbose() minipal_log_sync(minipal_log_flags_verbose) +void minipal_log_sync(minipal_log_flags flags); +void minipal_log_sync_all(void); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif /* HAVE_MINIPAL_LOG_H */ diff --git a/src/native/minipal/minipalconfig.h.in b/src/native/minipal/minipalconfig.h.in index 00764722ef1b86..7310b2f6962cd9 100644 --- a/src/native/minipal/minipalconfig.h.in +++ b/src/native/minipal/minipalconfig.h.in @@ -9,5 +9,6 @@ #cmakedefine01 BIGENDIAN #cmakedefine01 HAVE_BCRYPT_H #cmakedefine01 HAVE_WINDOWS_H +#cmakedefine01 HAVE_FSYNC #endif