/*++ Copyright (c) Microsoft. All rights reserved. Module Name: lxtlog.c Abstract: This file contains lx test logging routines. --*/ #include #include #include #include #include #include #include "lxtutil.h" #include "lxtlog.h" #define LXT_LOG_TIMESTAMP_BUFFER_SIZE 64 static LxtLogType g_LxtLogTypeMask = LXT_LOG_TYPE_DEFAULT_MASK; static const char* g_LxtTestName = ""; static char g_LXTestLogFileName[64] = "/data/test/log/"; static FILE* g_LxtFile = NULL; void LxtLog(LxtLogLevel LogLevel, const char* Message, ...) /*++ --*/ { static volatile int Active = 0; va_list Args; char TimeFormat[LXT_LOG_TIMESTAMP_BUFFER_SIZE]; struct tm* TimeInfo; static struct timespec TimeSpec; char TimeStamp[LXT_LOG_TIMESTAMP_BUFFER_SIZE]; if ((LogLevel == LxtLogLevelResourceError) && ((g_LxtLogTypeMask & LxtLogTypeStress) != 0)) { goto Exit; } // // Create a timestamp string which will precede the provided log message. // The format of the string is: [Hours:Minutes:Seconds.Milliseconds] // // N.B. The returns of strftime and snprintf are not checked because the // provided buffers are large enough for the format strings. // LxtClockGetTime(CLOCK_REALTIME, &TimeSpec); // // N.B. The signal handling code may use this function and not all of the // functions used here are reentrant safe, resulting in possible // deadlocks or crashes. To avoid these keep an active count and skip // functions known to be problematic in the signal case. The functions // identified at this point are: // - fflush // - localtime // - strftime // if (__sync_add_and_fetch(&Active, 1) == 1) { TimeInfo = localtime(&TimeSpec.tv_sec); strftime(TimeFormat, sizeof(TimeFormat), "[%H:%M:%S", TimeInfo); } snprintf(TimeStamp, sizeof(TimeStamp), "%s.%03u] ", TimeFormat, (unsigned int)TimeSpec.tv_nsec / (1000 * 1000)); // // Print the timestamp string followed by the provided message. // if ((g_LxtLogTypeMask & LxtLogTypePrintf) != 0) { va_start(Args, Message); printf("%s", TimeStamp); vprintf(Message, Args); va_end(Args); } if (((g_LxtLogTypeMask & LxtLogTypeFile) != 0) && (g_LxtFile != NULL)) { va_start(Args, Message); fprintf(g_LxtFile, "%s", TimeStamp); vfprintf(g_LxtFile, Message, Args); va_end(Args); } if (__sync_sub_and_fetch(&Active, 1) == 0) { sigset_t PreviousSignals; sigset_t SignalMask; sigfillset(&SignalMask); int MaskResult = pthread_sigmask(SIG_BLOCK, &SignalMask, &PreviousSignals); if (((g_LxtLogTypeMask & LxtLogTypeFile) != 0) && (g_LxtFile != NULL)) { fflush(g_LxtFile); } if ((g_LxtLogTypeMask & LxtLogTypePrintf) != 0) { fflush(stdout); } if (MaskResult == 0) { pthread_sigmask(SIG_SETMASK, &PreviousSignals, NULL); } } Exit: return; } int LxtLogInitialize(const char* TestName, LxtLogType LogTypeMask, bool LogAppend) /*++ --*/ { char* Mode; int Result; g_LxtTestName = TestName; g_LxtLogTypeMask = LogTypeMask; strcat(g_LXTestLogFileName, TestName); if ((g_LxtLogTypeMask & LxtLogTypeFile) != 0) { Mode = "w"; if (LogAppend != false) { Mode = "a"; } g_LxtFile = fopen(g_LXTestLogFileName, Mode); if (g_LxtFile == NULL) { Result = LXT_RESULT_FAILURE; g_LxtLogTypeMask &= ~LxtLogTypeFile; LxtLogError("Failed to open %s: %s", g_LXTestLogFileName, strerror(errno)); goto ErrorExit; } } Result = LXT_RESULT_SUCCESS; ErrorExit: return Result; } void LxtLogUninitialize(void) /*++ --*/ { // // Close and flush the log file. This ensures that the file contents are // able to be read from Windows. // if (g_LxtFile != NULL) { fflush(g_LxtFile); fsync(fileno(g_LxtFile)); fclose(g_LxtFile); g_LxtFile = NULL; } return; }