Инструментарий управления Windows (WMI) — технология, широко используемая на сервере и клиенте Windows для решения различных задач, в частности для проведения инвентаризации оборудования и программ, определения работающих служб и процессов. Обычно WMI работает без затруднений, но, если таковые возникают, диагностика может оказаться сложной. Например, одна из распространенных проблем WMI — высокая пиковая загрузка процессора при выполнении одного из процессов провайдера (wmiprvse.exe). Процесс провайдера относится к выполнению запросов WMI, а процесс службы WMI (svchost.exe) — к возвращению результатов выполнения запроса WMI создавшему его процессу. Любому администратору, вероятно, приходилось сталкиваться с высокой загрузкой процессора при работе WMI либо с необходимостью просмотра поступающих запросов WMI. Анализируя проблемы загрузки процессора, полезно просматривать выполняемые запросы и определять, откуда они поступили.
К сожалению, в лучшем случае просмотр поступающих запросов WMI — трудоемкий процесс, способный свести на нет попытки администратора установить, какие же запросы WMI выполняются в данной системе. Ниже предлагается способ просмотра поступающих запросов в режиме, максимально приближенном к реальному времени, с определением их источника. Этот способ позволяет быстро проводить диагностику распространенных проблем высокой загрузки процессора без необходимости выполнения сложных шагов отладки, сканирования сотен событий, регистрируемых в журналах, или обращения в группу технической поддержки.
Извлечение журналов трассировки WMI
Как описано в статье «Ведение журнала отладки WMI» (tinyurl.com/3a6b7ls), для просмотра журналов трассировки WMI можно включить механизм трассировки в окне Event Viewer. Однако ввиду большого числа регистрируемых событий сложно выделить недавние запросы и определить время их выполнения и источник поступления. Механизм фильтрации, встроенный в меню Event Viewer, не позволяет выводить на экран только запросы WMI.
Запустите из командной стройки программу wevtutil.exe. Эта программа, включенная в Windows Vista и более новые версии, позволяет извлекать информацию из журналов событий, осуществлять экспорт данных, выполнять запросы и задавать параметры для журналов. В частности, с помощью Wevtutil можно устанавливать признак трассировки для журнала событий WMI в локальном и удаленном режиме, обеспечивая вывод данных трассировки в окно Event Viewer.
Ниже приведен синтаксис установки признака трассировки для файла журнала WMIActivity (из окна командной строки с повышенными привилегиями) и сообщение, отображаемое после ввода команды:
C:\Windows\system32>wevtutil sl Microsoft- Windows-WMIActivity/Trace/e: true **** Warning: Enabling this type of log clears it. Do you want to enable and clear this log? [y/n]: y
Ответное сообщение содержит предупреждение об очистке журнала при включении трассировки (для выключения признака трассировки очистка журнала не требуется). Имеется также параметр /r, позволяющий устанавливать признак трассировки на удаленной системе.
После установки признака трассировки начинается вывод «информационных» событий журнала WMI-Activity. Однако эти данные выводятся с большой степенью детализации, а имеющийся механизм фильтрации не позволяет организовать вывод одних только запросов WMI. Вновь воспользуемся программой wevtutil.exe для просмотра поступающих запросов WMI вручную.
Ниже приведен синтаксис команды просмотра запросов WMI с использованием программы Wevtutil (запускаемой после включения признака трассировки из окна командной строки с повышенными привилегиями).
c:\>wevtutil qe Microsoft-Windows-WMI- Activity/Trace/q:"*[System[(Level=4 or Level=0) and (EventID=1)]]" |findstr/i execquery
Параметр qe означает «события запросов из файла журнала»; затем вводится имя файла журнала (Microsoft-Windows-WMIActivity/Trace). Параметр /q: предписывает отфильтровать данные из журнала, оставляя только конкретные события; в этом случае мы запрашиваем «Level 4 or Level 0 AND Event ID=1», указывая идентификатор события, под которым регистрируются все запросы WMI. Затем к результатам применяем функцию поиска строк (findstr) и выполняем фильтрацию, оставляя только команды execquery.
Пример результата выполнения команды показан на экране 1. Этот результат выдается только для одного запроса — нетрудно представить объем информации для сотни таких запросов! Это огромное количество данных, требующих анализа. Однако можно сменить формат выходных данных с XML на текстовый, внеся небольшое изменение в команду Wevtutil:
C:\>wevtutil qe Microsoft-Windows-WMI- Activity/Trace/q:"*[System[(Level=4 or Level=0) and (EventID=1)]]" /f: text |findstr/i execquery
Внесенное изменение заключается лишь в добавлении параметра /f: (формат) и указании текстового формата.
Теперь выходной результат, показанный на экране 2, более удобен для восприятия, но все же слишком громоздок для быстрого просмотра поступающих запросов WMI; кроме того, отсутствуют отметки времени поступления запроса.
Автоматизация трассировки событий WMI
Было бы удобно иметь сценарий, выполнение которого можно было задать через расписание и который бы обеспечивал выдачу удобных для восприятия данных с отметкой времени возникновения событий и позволял просматривать самые «свежие» запросы с момента последнего выполнения сценария. Именно эту задачу выполнила группа поддержки Microsoft. Был создан сценарий выдачи поступающих запросов WMI с указанием времени их поступления. В зависимости от используемой версии Windows можно установить, какая система и какой процесс являются инициаторами запроса, в локальном или удаленном режиме. Сценарий WMIActivity.vbs запрашивает события WMI и выводит информацию только о деятельности, связанной с событиями (см. листинг).
На экране 3 показан выходной результат в формате Excel. Имеется несколько столбцов данных, несущих информацию о времени выполнения запроса, о самом запросе, о системе, с которой он поступил (столбец ClientMachine), и о клиентском процессе, инициировавшем запрос (столбец Client Process ID).
Заметим, что в столбцах ClientMachine и ClientProcessID присутствуют данные, характерные только для систем Windows 7 и Windows Server 2008 R2 или более новых версий. В Vista и Windows Server 2008 не включен код регистрации клиента, инициировавшего запрос, или идентификатора клиентского процесса.
При первом выполнении сценария отметка времени помещается непосредственно в реестр в раздел HKLM\Software\WinITPro. При каждом выполнении сценария эта отметка времени проверяется, и извлекаются только более «свежие» записи. Таким образом, выдаются лишь самые новые запросы, что избавляет от необходимости просматривать одну и ту же информацию или проходить через громоздкое меню в стиле Event Viewer.
Преобразуем столбец TimeCreated в привычный формат. Для этого выделим столбец, а затем в окне, открываемом щелчком правой клавишей, выберем «форматирование ячейки», как показано на экране 4. Можно также создать сводную таблицу, анализируя запросы с точки зрения статистики.
.jpg)
Дополнительная помощь в диагностике
Сценарий WMIActivity.vbs позволяет просматривать поступающие в систему запросы WMI в режиме, максимально приближенном к реальному времени. Это облегчает диагностику проблем высокой загрузки процессора, связанной с деятельностью WMI, а также дает более четкое представление об источниках запросов.
Шон Уилер (seanw@microsoft.com) — старший инженер в команде Microsoft Premier Support, поддерживающей корпоративных клиентов. Один из создателей инструмента MPS Reporting, специалист по написанию сценариев и отладке
Экран 1. Выходные данные Wevtutil
‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’
' WMIActivity.vbs - seanw@microsoft.com
' This script uses wevtutil.exe to query for WMI Activity events and outputs only those events
' that show the WMI query being requested.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
option explicit
on error resume next
Const ForReading = 1, ForWriting = 2, ForAppending = 8
dim fso : set fso = createobject("scripting.filesystemobject")
dim wshShell : set wshShell = createobject("wscript.shell")
Dim strRegLogPath : strRegLogPath = "HKLM\SOFTWARE\WinITPro\"
Dim strAppName : strAppName = "WMIActivity"
Dim outputStream : set outputStream = wscript.stdout
Dim xmlWMIActivityDoc : set xmlWMIActivityDoc = createobject("Msxml2.DOMDocument.3.0")
Dim objWMIActivity : set objWMIActivity = new WMIActivityClass
Dim oExec, LastQueryTime
Dim querycmd, xmldata
LastQueryTime = wshShell.RegRead(strRegLogPath & strAppName & "\TimeStamp")
if err.number <> 0 then
if err.number = &H80070002 then
LastQueryTime = ""
err.clear
else
outputStream.writeline "Error : 0x" & myhex(err.number,8)
outputStream.writeline "Source : " & err.Source
outputStream.writeline "Description : " & err.Description
wscript.quit err.number
end if
end if
if LastQueryTime = "" then
querycmd = "wevtutil.exe qe Microsoft-Windows-WMI-Activity/Trace /f:xml /q:" & chr(34) & _
"*[System[(Level=4 or Level=0) and (EventID=1)]]" & chr(34) & " /e:Events"
else
querycmd = "wevtutil.exe qe Microsoft-Windows-Diagnostics-Performance/Operational /f:xml /q:" & chr(34) & _
"*[System[(Level=4 or Level=0) and (EventID=1) and (TimeCreated[@SystemTime >
'" & LastQueryTime & "'])]]" & chr(34) & " /e:Events"
end if
set oExec = wshShell.Exec(querycmd)
xmldata = oExec.StdOut.ReadAll
xmlWMIActivityDoc.loadxml(xmldata)
set oExec = nothing
dim rootNode,Node,operation
set rootNode = xmlWMIActivityDoc.selectSingleNode("//Events")
outputStream.writeline objWMIActivity.GetCSVHeader
for each Node in rootNode.childnodes
operation = Node.selectSingleNode("UserData/Operation_/Operation").text
if instr(lcase(operation),"execquery") <> 0 then
with objWMIActivity
.Computer = Node.selectSingleNode("System/Computer").text
LastQueryTime = Node.selectSingleNode("System/TimeCreated").getAttribute("SystemTime")
.TimeCreated = fmtDateTime(LastQueryTime,0) ' second parameter
is timezone bias (eg. -480 for Pacific Time)
.Query = mid(replace(operation,chr(34),chr(34) & chr(34)),33)
.ClientMachine = Node.selectSingleNode("UserData/Operation_/ClientMachine").text
.User = Node.selectSingleNode("UserData/Operation_/User").text
.ClientProcessId = Node.selectSingleNode("UserData/Operation_/ClientProcessId").text
.Namespace = Node.selectSingleNode("UserData/Operation_/NamespaceName").text
end with
outputStream.writeline objWMIActivity.GetCSVData
objWMIActivity.Class_Initialize
end if
next
wshShell.RegWrite strRegLogPath & strAppName & "\TimeStamp", LastQueryTime, "REG_SZ"
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
function fmtDateTime(byval strDateTime, byval tzBias)
dim strDT, strMS, temp
strDT = replace(left(strDateTime,19),"T"," ")
strMS = mid(strDateTime,20,4)
temp = cdate(strDT)+timeserial(0,tzBias,0)
fmtDateTime = FormatDateTime(temp,2) & " " & right("0" & Hour(FormatDateTime(temp,3)),2) & ":" & _
right("0" & Minute(FormatDateTime(temp,3)),2) & ":" & right("0" & Second(FormatDate
Time(temp,3)),2) & strMS
end function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Function : MyHex(byval num, byval pad)
' Description : pad hex
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function MyHex(byval num, byval pad)
MyHex = right(string(pad,&H30) & hex(num),pad)
end function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Class Definitions
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
class WMIActivityClass
private colHeader
public Computer
public TimeCreated
public Query
public ClientMachine
public User
public ClientProcessId
public Namespace
''''''''''''''''''''''''''''''''
public sub Class_Initialize
colHeader = array("Computer","TimeCreated","Query","ClientMachine","User","ClientProcessId",
"Namespace")
Computer = ""
TimeCreated = ""
Query = ""
ClientMachine = ""
User = ""
ClientProcessId = ""
Namespace = ""
end sub
''''''''''''''''''''''''''''''''
public function GetCSVHeader
dim colSep : colSep = chr(34) & "," & chr(34)
GetCSVHeader = chr(34) & join(colHeader,colSep) & chr(34)
end function
''''''''''''''''''''''''''''''''
public function GetCSVData
dim arrData
dim colSep : colSep = chr(34) & "," & chr(34)
arrData = array(Computer,TimeCreated,Query,ClientMachine,User,ClientProcessId,Namespace)
GetCSVData = chr(34) & join(arrData,colSep) & chr(34)
end function
end class
‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’