/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: tracelog.c Abstract: Sample trace control program. Allows user to start, update, query, stop event tracing, etc. --*/ #include #include #include #include #include #include #include #include #include #define MAXSTR 1024 // Default trace file name. #define DEFAULT_LOGFILE_NAME _T("C:\\LogFile.Etl") // On Windows 2000, we support up to 32 loggers at once. // On Windows XP and .NET server, we support up to 64 loggers. #define MAXIMUM_LOGGERS 32 // In this sample, we support the following actions. // Additional actions that we do not use in this sample include // Flush and Enumerate Guids functionalities. They are supported // only on XP or higher version. #define ACTION_QUERY 0 #define ACTION_START 1 #define ACTION_STOP 2 #define ACTION_UPDATE 3 #define ACTION_LIST 4 #define ACTION_ENABLE 5 #define ACTION_HELP 6 #define ACTION_UNDEFINED 10 void PrintLoggerStatus( IN PEVENT_TRACE_PROPERTIES LoggerInfo, IN ULONG Status ); ULONG ahextoi( IN TCHAR *s ); void StringToGuid( IN TCHAR *str, OUT LPGUID guid ); void PrintHelpMessage(); // // main function // __cdecl main(argc, argv) int argc; char **argv; /*++ Routine Description: It is the main function. Arguments: Return Value: Error Code defined in winerror.h : If the function succeeds, it returns ERROR_SUCCESS (== 0). --*/{ ULONG i, j; ULONG Status = ERROR_SUCCESS; LPTSTR *targv, *utargv = NULL; // Action to be taken USHORT Action = ACTION_UNDEFINED; LPTSTR LoggerName; LPTSTR LogFileName; PEVENT_TRACE_PROPERTIES pLoggerInfo; TRACEHANDLE LoggerHandle = 0; // Target GUID, level and flags for enable/disable GUID TargetGuid; ULONG bEnable = TRUE; ULONG SizeNeeded = 0; // We will enable Process, Thread, Disk, and Network events // if the Kernel Logger is requested. BOOL bKernelLogger = FALSE; // Allocate and initialize EVENT_TRACE_PROPERTIES structure first SizeNeeded = sizeof(EVENT_TRACE_PROPERTIES) + 2 * MAXSTR * sizeof(TCHAR); pLoggerInfo = (PEVENT_TRACE_PROPERTIES) malloc(SizeNeeded); if (pLoggerInfo == NULL) { return (ERROR_OUTOFMEMORY); } RtlZeroMemory(pLoggerInfo, SizeNeeded); pLoggerInfo->Wnode.BufferSize = SizeNeeded; pLoggerInfo->Wnode.Flags = WNODE_FLAG_TRACED_GUID; pLoggerInfo->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); pLoggerInfo->LogFileNameOffset = pLoggerInfo->LoggerNameOffset + MAXSTR * sizeof(TCHAR); LoggerName = (LPTSTR)((char*)pLoggerInfo + pLoggerInfo->LoggerNameOffset); LogFileName = (LPTSTR)((char*)pLoggerInfo + pLoggerInfo->LogFileNameOffset); // If the logger name is not given, we will assume the kernel logger. _tcscpy(LoggerName, KERNEL_LOGGER_NAME); #ifdef UNICODE if ((targv = CommandLineToArgvW( GetCommandLineW(), // pointer to a command-line string &argc // receives the argument count )) == NULL) { free(pLoggerInfo); return (GetLastError()); }; utargv = targv; #else targv = argv; #endif // // Parse the command line options to determine actions and parameters. // while (--argc > 0) { ++targv; if (**targv == '-' || **targv == '/') { // argument found if (targv[0][0] == '/' ) { targv[0][0] = '-'; } // Deterine actions. if (!_tcsicmp(targv[0], _T("-start"))) { Action = ACTION_START; if (argc > 1) { if (targv[1][0] != '-' && targv[1][0] != '/') { ++targv; --argc; _tcscpy(LoggerName, targv[0]); } } } else if (!_tcsicmp(targv[0], _T("-enable"))) { Action = ACTION_ENABLE; if (argc > 1) { if (targv[1][0] != '-' && targv[1][0] != '/') { ++targv; --argc; _tcscpy(LoggerName, targv[0]); } } } else if (!_tcsicmp(targv[0], _T("-disable"))) { Action = ACTION_ENABLE; bEnable = FALSE; if (argc > 1) { if (targv[1][0] != '-' && targv[1][0] != '/') { ++targv; --argc; _tcscpy(LoggerName, targv[0]); } } } else if (!_tcsicmp(targv[0], _T("-stop"))) { Action = ACTION_STOP; if (argc > 1) { if (targv[1][0] != '-' && targv[1][0] != '/') { ++targv; --argc; _tcscpy(LoggerName, targv[0]); } } } else if (!_tcsicmp(targv[0], _T("-update"))) { Action = ACTION_UPDATE; if (argc > 1) { if (targv[1][0] != '-' && targv[1][0] != '/') { ++targv; --argc; _tcscpy(LoggerName, targv[0]); } } } else if (!_tcsicmp(targv[0], _T("-query"))) { Action = ACTION_QUERY; if (argc > 1) { if (targv[1][0] != '-' && targv[1][0] != '/') { ++targv; --argc; _tcscpy(LoggerName, targv[0]); } } } else if (!_tcsicmp(targv[0], _T("-list"))) { Action = ACTION_LIST; } // Get other parameters. // Users can customize logger settings further by adding/changing // values to pLoggerInfo. Refer to EVENT_TRACE_PROPERTIES documentation // for available options. // In this sample, we allow changing maximum number of buffers and // specifying user mode (private) logger. // We also take trace file name and guid for enable/disable. else if (!_tcsicmp(targv[0], _T("-f"))) { if (argc > 1) { _tfullpath(LogFileName, targv[1], MAXSTR); ++targv; --argc; } } else if (!_tcsicmp(targv[0], _T("-guid"))) { if (argc > 1) { // -guid #00000000-0000-0000-0000-000000000000 if (targv[1][0] == _T('#')) { StringToGuid(&targv[1][1], &TargetGuid); ++targv; --argc; } } } else if (!_tcsicmp(targv[0], _T("-max"))) { if (argc > 1) { pLoggerInfo->MaximumBuffers = _ttoi(targv[1]); ++targv; --argc; } } else if (!_tcsicmp(targv[0], _T("-um"))) { pLoggerInfo->LogFileMode |= EVENT_TRACE_PRIVATE_LOGGER_MODE; } else if ( targv[0][1] == 'h' || targv[0][1] == 'H' || targv[0][1] == '?'){ Action = ACTION_HELP; PrintHelpMessage(); if (utargv != NULL) { GlobalFree(utargv); } free(pLoggerInfo); return (ERROR_SUCCESS); } else Action = ACTION_UNDEFINED; } else { _tprintf(_T("Invalid option given: %s\n"), targv[0]); Status = ERROR_INVALID_PARAMETER; SetLastError(Status); if (utargv != NULL) { GlobalFree(utargv); } free(pLoggerInfo); return (Status); } } // Set the kernel logger parameters. if (!_tcscmp(LoggerName, KERNEL_LOGGER_NAME)) { // Set enable flags. Users can add options to add additional kernel events // or remove some of these events. pLoggerInfo->EnableFlags |= EVENT_TRACE_FLAG_PROCESS; pLoggerInfo->EnableFlags |= EVENT_TRACE_FLAG_THREAD; pLoggerInfo->EnableFlags |= EVENT_TRACE_FLAG_DISK_IO; pLoggerInfo->EnableFlags |= EVENT_TRACE_FLAG_NETWORK_TCPIP; pLoggerInfo->Wnode.Guid = SystemTraceControlGuid; bKernelLogger = TRUE; } else if (pLoggerInfo->LogFileMode & EVENT_TRACE_PRIVATE_LOGGER_MODE) { // We must provide a control GUID for a private logger. pLoggerInfo->Wnode.Guid = TargetGuid; } // Process the request. switch (Action) { case ACTION_START: { // Use default file name if not given if (_tcslen(LogFileName) == 0) { _tcscpy(LogFileName, DEFAULT_LOGFILE_NAME); } Status = StartTrace(&LoggerHandle, LoggerName, pLoggerInfo); if (Status != ERROR_SUCCESS) { _tprintf(_T("Could not start logger: %s\n") _T("Operation Status: %uL\n"), LoggerName, Status); break; } _tprintf(_T("Logger Started...\n")); } case ACTION_ENABLE: { // We can allow enabling a GUID during START operation (Note no break in case ACTION_START). // In that case, we do not need to get LoggerHandle separately. if (Action == ACTION_ENABLE ){ // Get Logger Handle though Query. Status = ControlTrace((TRACEHANDLE) 0, LoggerName, pLoggerInfo, EVENT_TRACE_CONTROL_QUERY); if( Status != ERROR_SUCCESS ){ _tprintf( _T("ERROR: Logger not started\n") _T("Operation Status: %uL\n"), Status); break; } LoggerHandle = pLoggerInfo->Wnode.HistoricalContext; } // We do not allow EnableTrace on the Kernel Logger in this sample, // users can use EnableFlags to enable/disable certain kernel events. if (!bKernelLogger) { _tprintf(_T("Enabling trace to logger %d\n"), LoggerHandle); // In this sample, we use EnableFlag = EnableLebel = 0 Status = EnableTrace ( bEnable, 0, 0, &TargetGuid, LoggerHandle); if (Status != ERROR_SUCCESS) { _tprintf(_T("ERROR: Failed to enable Guid...\n")); _tprintf(_T("Operation Status: %uL\n"), Status); break; } } break; } case ACTION_STOP : { LoggerHandle = (TRACEHANDLE) 0; Status = ControlTrace(LoggerHandle, LoggerName, pLoggerInfo, EVENT_TRACE_CONTROL_STOP); break; } case ACTION_LIST : { ULONG returnCount; PEVENT_TRACE_PROPERTIES pLoggerInfo[MAXIMUM_LOGGERS]; PEVENT_TRACE_PROPERTIES pStorage, pTempStorage; ULONG SizeForOneProperty = sizeof(EVENT_TRACE_PROPERTIES) + 2 * MAXSTR * sizeof(TCHAR); // We need to prepare space to receieve the inforamtion on loggers. SizeNeeded = MAXIMUM_LOGGERS * SizeForOneProperty; pStorage = (PEVENT_TRACE_PROPERTIES)malloc(SizeNeeded); if (pStorage == NULL) { Status = ERROR_OUTOFMEMORY; break; } RtlZeroMemory(pStorage, SizeNeeded); // Save the pointer for free() later. pTempStorage = pStorage; for (i = 0; i < MAXIMUM_LOGGERS; i++) { pStorage->Wnode.BufferSize = SizeForOneProperty; pStorage->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); pStorage->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + MAXSTR * sizeof(TCHAR); pLoggerInfo[i] = pStorage; pStorage = (PEVENT_TRACE_PROPERTIES) ( (PUCHAR)pStorage + pStorage->Wnode.BufferSize); } Status = QueryAllTraces(pLoggerInfo, MAXIMUM_LOGGERS, &returnCount); if (Status == ERROR_SUCCESS) { for (j= 0; j < returnCount; j++) { PrintLoggerStatus(pLoggerInfo[j], Status); _tprintf(_T("\n")); } } free(pTempStorage); break; } case ACTION_UPDATE : { // In this sample, users can only update MaximumBuffers and log file name. // User can add more options for other parameters as needed. Status = ControlTrace(LoggerHandle, LoggerName, pLoggerInfo, EVENT_TRACE_CONTROL_UPDATE); break; } case ACTION_QUERY : { Status = ControlTrace(LoggerHandle, LoggerName, pLoggerInfo, EVENT_TRACE_CONTROL_QUERY); break; } case ACTION_HELP: { PrintHelpMessage(); break; } default : { _tprintf(_T("Error: no action specified\n")); PrintHelpMessage(); break; } } if ((Action != ACTION_HELP) && (Action != ACTION_UNDEFINED) && (Action != ACTION_LIST)) { PrintLoggerStatus(pLoggerInfo, Status); } if (Status != ERROR_SUCCESS) { SetLastError(Status); } if (utargv != NULL) { GlobalFree(utargv); } free(pLoggerInfo); return (Status); } void PrintLoggerStatus( IN PEVENT_TRACE_PROPERTIES LoggerInfo, IN ULONG Status ) /*++ Routine Description: Prints out the status of the specified logger. Arguments: LoggerInfo - The pointer to the resident EVENT_TRACE_PROPERTIES that has the information about the current logger. Status - The operation status of the current logger. Return Value: None --*/ { LPTSTR LoggerName, LogFileName; if ((LoggerInfo->LoggerNameOffset > 0) && (LoggerInfo->LoggerNameOffset < LoggerInfo->Wnode.BufferSize)) { LoggerName = (LPTSTR) ((PUCHAR)LoggerInfo + LoggerInfo->LoggerNameOffset); } else LoggerName = NULL; if ((LoggerInfo->LogFileNameOffset > 0) && (LoggerInfo->LogFileNameOffset < LoggerInfo->Wnode.BufferSize)) { LogFileName = (LPTSTR) ((PUCHAR)LoggerInfo + LoggerInfo->LogFileNameOffset); } else LogFileName = NULL; _tprintf(_T("Operation Status: %uL\n"), Status); _tprintf(_T("Logger Name: %s\n"), (LoggerName == NULL) ? _T(" ") : LoggerName); _tprintf(_T("Logger Id: %I64x\n"), LoggerInfo->Wnode.HistoricalContext); _tprintf(_T("Logger Thread Id: %d\n"), LoggerInfo->LoggerThreadId); if (Status != 0) return; _tprintf(_T("Buffer Size: %d Kb"), LoggerInfo->BufferSize); if (LoggerInfo->LogFileMode & EVENT_TRACE_USE_PAGED_MEMORY) { _tprintf(_T(" using paged memory\n")); } else { _tprintf(_T("\n")); } _tprintf(_T("Maximum Buffers: %d\n"), LoggerInfo->MaximumBuffers); _tprintf(_T("Minimum Buffers: %d\n"), LoggerInfo->MinimumBuffers); _tprintf(_T("Number of Buffers: %d\n"), LoggerInfo->NumberOfBuffers); _tprintf(_T("Free Buffers: %d\n"), LoggerInfo->FreeBuffers); _tprintf(_T("Buffers Written: %d\n"), LoggerInfo->BuffersWritten); _tprintf(_T("Events Lost: %d\n"), LoggerInfo->EventsLost); _tprintf(_T("Log Buffers Lost: %d\n"), LoggerInfo->LogBuffersLost); _tprintf(_T("Real Time Buffers Lost: %d\n"), LoggerInfo->RealTimeBuffersLost); _tprintf(_T("AgeLimit: %d\n"), LoggerInfo->AgeLimit); if (LogFileName == NULL) { _tprintf(_T("Buffering Mode: ")); } else { _tprintf(_T("Log File Mode: ")); } if (LoggerInfo->LogFileMode & EVENT_TRACE_FILE_MODE_APPEND) { _tprintf(_T("Append ")); } if (LoggerInfo->LogFileMode & EVENT_TRACE_FILE_MODE_CIRCULAR) { _tprintf(_T("Circular\n")); } else if (LoggerInfo->LogFileMode & EVENT_TRACE_FILE_MODE_SEQUENTIAL) { _tprintf(_T("Sequential\n")); } else { _tprintf(_T("Sequential\n")); } if (LoggerInfo->LogFileMode & EVENT_TRACE_REAL_TIME_MODE) { _tprintf(_T("Real Time mode enabled")); _tprintf(_T("\n")); } if (LoggerInfo->MaximumFileSize > 0) _tprintf(_T("Maximum File Size: %d Mb\n"), LoggerInfo->MaximumFileSize); if (LoggerInfo->FlushTimer > 0) _tprintf(_T("Buffer Flush Timer: %d secs\n"), LoggerInfo->FlushTimer); if (LoggerInfo->EnableFlags != 0) { _tprintf(_T("Enabled tracing: ")); if ((LoggerName != NULL) && (!_tcscmp(LoggerName, KERNEL_LOGGER_NAME))) { if (LoggerInfo->EnableFlags & EVENT_TRACE_FLAG_PROCESS) _tprintf(_T("Process ")); if (LoggerInfo->EnableFlags & EVENT_TRACE_FLAG_THREAD) _tprintf(_T("Thread ")); if (LoggerInfo->EnableFlags & EVENT_TRACE_FLAG_DISK_IO) _tprintf(_T("Disk ")); if (LoggerInfo->EnableFlags & EVENT_TRACE_FLAG_DISK_FILE_IO) _tprintf(_T("File ")); if (LoggerInfo->EnableFlags & EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS) _tprintf(_T("PageFaults ")); if (LoggerInfo->EnableFlags & EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS) _tprintf(_T("HardFaults ")); if (LoggerInfo->EnableFlags & EVENT_TRACE_FLAG_IMAGE_LOAD) _tprintf(_T("ImageLoad ")); if (LoggerInfo->EnableFlags & EVENT_TRACE_FLAG_NETWORK_TCPIP) _tprintf(_T("TcpIp ")); if (LoggerInfo->EnableFlags & EVENT_TRACE_FLAG_REGISTRY) _tprintf(_T("Registry ")); }else{ _tprintf(_T("0x%08x"), LoggerInfo->EnableFlags ); } _tprintf(_T("\n")); } if (LogFileName != NULL) { _tprintf(_T("Log Filename: %s\n"), LogFileName); } } ULONG ahextoi( IN TCHAR *s ) /*++ Routine Description: Converts a hex string into a number. Arguments: s - A hex string in TCHAR. Return Value: ULONG - The number in the string. --*/ { int len; ULONG num, base, hex; len = _tcslen(s); hex = 0; base = 1; num = 0; while (--len >= 0) { if ( (s[len] == 'x' || s[len] == 'X') && (s[len-1] == '0') ) break; if (s[len] >= '0' && s[len] <= '9') num = s[len] - '0'; else if (s[len] >= 'a' && s[len] <= 'f') num = (s[len] - 'a') + 10; else if (s[len] >= 'A' && s[len] <= 'F') num = (s[len] - 'A') + 10; else continue; hex += num * base; base = base * 16; } return hex; } void StringToGuid( IN TCHAR *str, IN OUT LPGUID guid ) /*++ Routine Description: Converts a string into a GUID. Arguments: str - A string in TCHAR. guid - The pointer to a GUID that will have the converted GUID. Return Value: None. --*/ { TCHAR temp[10]; int i; _tcsncpy(temp, str, 8); temp[8] = 0; guid->Data1 = ahextoi(temp); _tcsncpy(temp, &str[9], 4); temp[4] = 0; guid->Data2 = (USHORT) ahextoi(temp); _tcsncpy(temp, &str[14], 4); temp[4] = 0; guid->Data3 = (USHORT) ahextoi(temp); for (i=0; i<2; i++) { _tcsncpy(temp, &str[19 + (i*2)], 2); temp[2] = 0; guid->Data4[i] = (UCHAR) ahextoi(temp); } for (i=2; i<8; i++) { _tcsncpy(temp, &str[20 + (i*2)], 2); temp[2] = 0; guid->Data4[i] = (UCHAR) ahextoi(temp); } } void PrintHelpMessage() /*++ Routine Description: prints out a help message. Arguments: None. Return Value: None. --*/ { _tprintf(_T("Usage: tracelog [actions] [options] | [-h | -help | -?]\n")); _tprintf(_T("\n actions:\n")); _tprintf(_T("\t-start [LoggerName] Starts up the [LoggerName] trace session\n")); _tprintf(_T("\t-stop [LoggerName] Stops the [LoggerName] trace session\n")); _tprintf(_T("\t-update [LoggerName] Updates the [LoggerName] trace session\n")); _tprintf(_T("\t-enable [LoggerName] Enables providers for the [LoggerName] session\n")); _tprintf(_T("\t-disable [LoggerName] Disables providers for the [LoggerName] session\n")); _tprintf(_T("\t-query [LoggerName] Query status of [LoggerName] trace session\n")); _tprintf(_T("\t-list List all trace sessions\n")); _tprintf(_T("\n options:\n")); _tprintf(_T("\t-um Use Process Private tracing\n")); _tprintf(_T("\t-max Sets maximum buffers\n")); _tprintf(_T("\t-f Log to file \n")); _tprintf(_T("\t-guid # Provider GUID to enable/disable\n")); _tprintf(_T("\n")); _tprintf(_T("\t-h\n")); _tprintf(_T("\t-help\n")); _tprintf(_T("\t-? Display usage information\n")); }