409 lines
10 KiB
C
409 lines
10 KiB
C
/*++
|
|
|
|
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 <wmistr.h>
|
|
#include <evntrace.h>
|
|
#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);
|
|
}
|
|
|