/*++ Copyright (c) 1990-1999 Microsoft Corporation All rights reserved Module Name: wmi.c Abstract: Holds the internal operations for wmi instrumenation Author: Stuart de Jong (sdejong) 15-Oct-99 Environment: User Mode -Win32 Revision History: --*/ #include "precomp.h" // // WMI files // #include #include #include "wmi.h" #include "wmidata.h" // End WMI // // How it works: // // There are similar guids defined in the sdktools tracecounter programs, and also // descriptions there of the information we send them. This is used by them to print out // and format the data we send. // // When we start up we register ourselves with WMI and provide a callback into our control // code. This allows an external tool to call the start trace code in WMI with our guid, which // then has WMI call us, and start tracing. // // Stopping a trace is also done through the callback. the data is then analyzed by the WMI tools // and output in human readable form, or it could be further analyzed from there. // MODULE_DEBUG_INIT ( DBG_ERROR, DBG_ERROR ); // These Guid's correspond to the definitions in \nt\sdktools\trace\tracedmp\mofdata.guid // // Identifies the PrintJob data logged by the delete job event. // GUID WmiPrintJobGuid = { /* 127eb555-3b06-46ea-a08b-5dc2c3c57cfd */ 0x127eb555, 0x3b06, 0x46ea, 0xa0, 0x8b, 0x5d, 0xc2, 0xc3, 0xc5, 0x7c, 0xfd }; // // Identifies the RenderedJob data logged by the job rendered event. // GUID WmiRenderedJobGuid = { /* 1d32b239-92a6-485a-96d2-dc3659fb803e */ 0x1d32b239, 0x92a6, 0x485a, 0x96, 0xd2, 0xdc, 0x36, 0x59, 0xfb, 0x80, 0x3e }; // // Used by the control app. to find the callback that turns spooler tracing on // and off. // GUID WmiSpoolerControlGuid = { /* 94a984ef-f525-4bf1-be3c-ef374056a592 */ 0x94a984ef, 0xf525, 0x4bf1, 0xbe, 0x3c, 0xef, 0x37, 0x40, 0x56, 0xa5, 0x92 }; #define szWmiResourceName TEXT("Spooler") TRACE_GUID_REGISTRATION WmiTraceGuidReg[] = { { (LPGUID)&WmiPrintJobGuid, NULL }, { (LPGUID)&WmiRenderedJobGuid, NULL } }; // // The mof fields point to the following data. // DWORD JobId; // Unique ID for the transaction of printing a job // WMI_SPOOL_DATA Data; // See splcom.h // typedef struct _WMI_SPOOL_EVENT { EVENT_TRACE_HEADER Header; MOF_FIELD MofData[2]; } WMI_SPOOL_EVENT, *PWMI_SPOOL_EVENT; static TRACEHANDLE WmiRegistrationHandle; static TRACEHANDLE WmiLoggerHandle; static LONG ulWmiEnableLevel = 0; static HANDLE hWmiRegisterThread = NULL; static DWORD dwWmiRegisterThreadId = 0; static ULONG bWmiTraceOnFlag = FALSE; static ULONG bWmiIsInitialized = FALSE; ULONG WmiControlCallback( IN WMIDPREQUESTCODE RequestCode, IN PVOID Context, IN OUT ULONG *InOutBufferSize, IN OUT PVOID Buffer ); /*++ Routine Name: WmiRegisterTrace() Routine Description: Thread routine that registers us with the WMI tools Arguments: LPVOID Arg : Not used. --*/ DWORD WmiRegisterTrace( IN LPVOID arg ) { ULONG Status = ERROR_SUCCESS; WCHAR szImagePath[MAX_PATH]; Status = GetModuleFileName(NULL, szImagePath, COUNTOF(szImagePath)); if (Status == 0) { Status = ERROR_FILE_NOT_FOUND; } else { Status = RegisterTraceGuids( WmiControlCallback, NULL, (LPGUID)&WmiSpoolerControlGuid, 1, WmiTraceGuidReg, szImagePath, szWmiResourceName, &WmiRegistrationHandle); if (Status == ERROR_SUCCESS) { DBGMSG(DBG_TRACE, ("WmiInitializeTrace: SPOOLER WMI INITIALIZED.\n")); InterlockedExchange(&bWmiIsInitialized, TRUE); } else { DBGMSG(DBG_TRACE, ("WmiInitializeTrace: SPOOLER WMI INITIALIZE FAILED: %u.\n", Status)); } } return Status; } /*++ Routine Name: WmiInitializeTrace() Routine Description: Initialises the Trace structures and registers the callback with WMI. This creates a thread and calls WmiRegisterTrace, since the registering may take a long time (up to minutes) Arguments: Returns ERROR_SUCCESS if it succeeds or ERROR_ALREADY_EXISTS otherwise --*/ ULONG WmiInitializeTrace(VOID) { ULONG Status = ERROR_ALREADY_EXISTS; if (!hWmiRegisterThread) { InterlockedExchange(&bWmiIsInitialized, FALSE); // // Registering can block for a long time (I've seen minutes // occationally), so it must be done in its own thread. // if (hWmiRegisterThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WmiRegisterTrace, 0, 0, &dwWmiRegisterThreadId)) { CloseHandle(hWmiRegisterThread); Status = ERROR_SUCCESS; } else { Status = GetLastError(); } } return Status; } /*++ Routine Name: WmiTerminateTrace() Routine Description: Deregisters us from the WMI tools Arguments: Returns ERROR_SUCCESS on success. a Winerror otherwise. --*/ ULONG WmiTerminateTrace(VOID) { ULONG Status = ERROR_SUCCESS; DWORD dwExitCode; if (bWmiIsInitialized) { InterlockedExchange(&bWmiIsInitialized, FALSE); Status = UnregisterTraceGuids(WmiRegistrationHandle); if (Status == ERROR_SUCCESS) { DBGMSG(DBG_TRACE, ("WmiTerminateTrace: SPOOLER WMI UNREGISTERED.\n")); } else { DBGMSG(DBG_TRACE, ("WmiTerminateTrace: SPOOLER WMI UNREGISTER FAILED.\n")); } } return Status; } /*++ Routine Name: SplWmiTraceEvent() Routine Description: If tracing is turned on, this sends the event to the WMI subsystem. Arguments: DWORD JobId : the JobID this is related to. UCHAR EventTraceType : The type of event that happened PWMI_SPOOL_DATA Data : The Event Data, could be NULL Returns ERROR_SUCCESS if it doesn't need to do anything, or if it succeeds. --*/ ULONG LogWmiTraceEvent( IN DWORD JobId, IN UCHAR EventTraceType, IN PWMI_SPOOL_DATA Data OPTIONAL ) { WMI_SPOOL_EVENT WmiSpoolEvent; ULONG Status; if (!bWmiTraceOnFlag) return ERROR_SUCCESS; // // Level 1 tracing just traces response time of individual jobs with job data. // Default level is 0. // if (ulWmiEnableLevel == 1) { switch (EventTraceType) { // // Save overhead by not tracking resource usage. // case EVENT_TRACE_TYPE_SPL_TRACKTHREAD: case EVENT_TRACE_TYPE_SPL_ENDTRACKTHREAD: return ERROR_SUCCESS; default: // // Job data. // break; } } // // Record data. // RtlZeroMemory(&WmiSpoolEvent, sizeof(WmiSpoolEvent)); WmiSpoolEvent.Header.Size = sizeof(WMI_SPOOL_EVENT); WmiSpoolEvent.Header.Flags = (WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR); WmiSpoolEvent.Header.Class.Type = EventTraceType; WmiSpoolEvent.Header.Guid = WmiPrintJobGuid; WmiSpoolEvent.MofData[0].DataPtr = (ULONG64)&JobId; WmiSpoolEvent.MofData[0].Length = sizeof(DWORD); WmiSpoolEvent.MofData[1].DataPtr = (ULONG64)Data; if (Data) { switch (EventTraceType) { case EVENT_TRACE_TYPE_SPL_DELETEJOB: WmiSpoolEvent.MofData[1].Length = sizeof(struct _WMI_JOBDATA); break; case EVENT_TRACE_TYPE_SPL_JOBRENDERED: WmiSpoolEvent.Header.Guid = WmiRenderedJobGuid; WmiSpoolEvent.MofData[1].Length = sizeof(struct _WMI_EMFDATA); break; default: DBGMSG(DBG_TRACE, ("SplWmiTraceEvent: FAILED to log WMI Trace Event Unexpected Data JobId:%u Type:%u\n", JobId, (ULONG) EventTraceType)); return ERROR_INVALID_DATA; } } Status = TraceEvent( WmiLoggerHandle, (PEVENT_TRACE_HEADER) &WmiSpoolEvent); // // logger buffers out of memory should not prevent provider from // generating events. This will only cause events lost. // if (Status == ERROR_NOT_ENOUGH_MEMORY) { DBGMSG(DBG_TRACE, ("SplWmiTraceEvent: FAILED to log WMI Trace Event No Memory JobId:%u Type:%u\n", JobId, (ULONG) EventTraceType)); } else if (Status != ERROR_SUCCESS) { DBGMSG(DBG_TRACE, ("SplWmiTraceEvent: FAILED to log WMI Trace Event JobId:%u Type:%u Status:%u\n", JobId, (ULONG) EventTraceType, Status)); } return Status; } /*++ Routine Name: SplWmiControlCallback() Routine Description: This is the function we provite to the WMI subsystem as a callback, it is used to start and stop the trace events. Arguments: IN WMIDPREQUESTCODE RequestCode : The function to provide (enable/disable) IN PVOID Context : Not used by us. IN OUT ULONG *InOutBufferSize : The Buffersize IN OUT PVOID Buffer : The buffer to use for the events Returns ERROR_SUCCESS on success, or an error code. --*/ ULONG WmiControlCallback( IN WMIDPREQUESTCODE RequestCode, IN PVOID Context, IN OUT ULONG *InOutBufferSize, IN OUT PVOID Buffer ) { ULONG Status; if (!bWmiIsInitialized) { DBGMSG(DBG_TRACE, ("SplWmiControlCallback: SPOOLER WMI NOT INITIALIZED.\n")); return ERROR_GEN_FAILURE; } Status = ERROR_SUCCESS; switch (RequestCode) { case WMI_ENABLE_EVENTS: { WmiLoggerHandle = GetTraceLoggerHandle( Buffer ); ulWmiEnableLevel = GetTraceEnableLevel( WmiLoggerHandle ); InterlockedExchange(&bWmiTraceOnFlag, TRUE); DBGMSG(DBG_TRACE, ("SplWmiControlCallback: SPOOLER WMI ENABLED LEVEL %u.\n", ulWmiEnableLevel)); break; } case WMI_DISABLE_EVENTS: { DBGMSG(DBG_TRACE, ("SplWmiControlCallback: SPOOLER WMI DISABLED.\n")); InterlockedExchange(&bWmiTraceOnFlag, FALSE); WmiLoggerHandle = 0; break; } default: { Status = ERROR_INVALID_PARAMETER; break; } } *InOutBufferSize = 0; return(Status); }