Инструментарий управления 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. Можно также создать сводную таблицу, анализируя запросы с точки зрения статистики.
Дополнительная помощь в диагностике
Сценарий 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 ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’