Показаны сообщения с ярлыком Microsoft Windows. Показать все сообщения
Показаны сообщения с ярлыком Microsoft Windows. Показать все сообщения

вторник, 22 ноября 2011 г.

Сколько проживет SSD

Немного провокационный заголовок, не так ли? На самом деле я не собираюсь тут приводить процедуру расчета времени жизни SSD. Хочу написать о другом. Сегодня коллега задал вопрос: “Есть ли счетчик, который помог бы определить, сколько данных записано на диск. И вообще, как определить количество данных, записанных на диск?”. Собственно тут хотел бы привести свое видение ответа, так сказать.
Как оказалось – нет в perfmon такого счетчика. Ну, нет и все тут. Вероятно, есть на то какие-то причины. Но интересующие данные, судя по всему, получить таки можно. На ответ наткнулся совсем случайно. Для того чтобы получить нужные цифры можно воспользоваться утилитой fsutil. А конкретно, командой, например fsutil fsinfo statistics g: Утилита возвращает кучу всего, и, в частности значение UserFileWriteBytes, которое, похоже, и показывает нужную информацию. Стоит отметить, что эти данные, видимо, сбрасываются при перезагрузке. Ну и в качестве тренировки написал скрипт, который парсит все это счастье и возвращает хэш-таблицу с нужными данными:
function Get-DiskStats ($diskLetter){
 if (Test-Path $diskLetter) {
    (fsutil fsinfo statistics $diskLetter) | `
    where {$_.length -ne 0} | `
    %{$_.replace(" ","")} | `
    % {$_ -match "(?\w+):(?[0-9]{1,})"; $matches} | `
    % {$ret = @{}} {$ret["$($_.name)"]=$_.value}
    return $ret}
 else {Write-Error "There is no disk $diskLetter"; return $null}
}

Get-DiskStats g:

Подозреваю правда, что информацию можно получить так же при помощи ETW. Вот только как – не углублялся пока. PS. Некоторые изменения к плану. Если вы хотите собрать данные с работающего сервера за некоторый промежуток времени - вы можете поступить следующим образом.
1. Получить статистику на начальный момент времени:
Get-DiskStats c: | Export-Clixml c:\Files\disk.xml
Этим самым вы сохраните начальные данные в файл xml.
2. На конечный момент можно поступить следующим образом:
$old = Import-Clixml c:\Files\disk.xml
$new = Get-DiskStats c:


$old.Keys | ? {$old[$_] -ne $new[$_]} | % {$obj = New-Object -TypeName psobject;
             $obj | Add-Member -MemberType NoteProperty -Name name -Value $_;
             $obj | Add-Member -MemberType NoteProperty -Name oldValue -Value $old[$_];
             $obj | Add-Member -MemberType NoteProperty -Name newValue -Value $new[$_];
             $obj} `
             | sort name | ft -AutoSize
Что вернет вам таблицу со сравнениями старых и новых значений. Вот, примерно такую:
name                oldValue   newValue   
----                --------   --------   
BitmapReadBytes     12632064   12673024   
BitmapReads         534        544        
BitmapWriteBytes    134942720  143458304  
BitmapWrites        28034      29936      
LogFileWriteBytes   1601335296 1709424640 
LogFileWrites       214884     230697     

вторник, 4 октября 2011 г.

Получения списка выданных dhcp адресов скриптом

Всем привет. Недавно в форуме technet появился вопрос: как получить список выданных адресов и когда они были выданы. После короткого погружения в тему выяснилось, что используя стандартный API узнать это нельзя, поскольку сервер, похоже, таких данных не хранит, ну или не предоставляет. Коллега, в обсуждении, предложил парсить журнал сервера с тем чтобы достать из журнала нужные данные. Однако этот вариант требовал написания достаточно сложной для меня схемы, с отслеживание состояний и изменений, происходящих с адресом. С другой стороны, можно было использовать netsh для получения данных о выданных адресах, например вот так: netsh dhcp server scope 172.20.0.0 show clients 1. Однако этот способ не возвращает данных о дате выдачи, только о времени окончания. В итоге мы пришли к мнению, что проще использовать скрипт, и рассчитать нужную дату. Но при подробном изучении и этот способ оказался несколько более сложным, чем мне казалось на первый взгляд. Проблема в том, что я очень не люблю, наверное потому что не умею, парсить строки. Это вызывает у меня баттхёрт и головокружение. В итоге я воспользовался вот этим скриптом и слегка его изменил исключительно в целях пруф оф концепт:

$DHCP_EnumSubnetClients = @'          
[DllImport("dhcpsapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern uint DhcpEnumSubnetClients(
string ServerIpAddress,
uint SubnetAddress,
ref uint ResumeHandle,
uint PreferredMaximum,
out IntPtr ClientInfo,
ref uint ElementsRead,
ref uint ElementsTotal);
'@

$DHCP_Structs = @'
namespace mystruct {
using System;
using System.Runtime.InteropServices;

public struct CUSTOM_CLIENT_INFO
{
public string ClientName;
public string IpAddress;
public string MacAddress;
}

[StructLayout(LayoutKind.Sequential)]
public struct DHCP_CLIENT_INFO_ARRAY
{
public uint NumElements;
public IntPtr Clients;
}

[StructLayout(LayoutKind.Sequential)]
public struct DHCP_CLIENT_UID
{
public uint DataLength;
public IntPtr Data;
}

[StructLayout(LayoutKind.Sequential)]
public struct DATE_TIME
{
public uint dwLowDateTime;
public uint dwHighDateTime;

public DateTime Convert()
{
if (dwHighDateTime== 0 && dwLowDateTime == 0)
{
return DateTime.MinValue;
}
if (dwHighDateTime == int.MaxValue && dwLowDateTime == UInt32.MaxValue)
{
return DateTime.MaxValue;
}
return DateTime.FromFileTime((((long) dwHighDateTime) << 32) | (UInt32) dwLowDateTime);
}
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct DHCP_HOST_INFO
{
public uint IpAddress;
public string NetBiosName;
public string HostName;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct DHCP_CLIENT_INFO
{
public uint ClientIpAddress;
public uint SubnetMask;
public DHCP_CLIENT_UID ClientHardwareAddress;
[MarshalAs(UnmanagedType.LPWStr)]
public string ClientName;
[MarshalAs(UnmanagedType.LPWStr)]
public string ClientComment;
public DATE_TIME ClientLeaseExpires;
public DHCP_HOST_INFO OwnerHost;
}
}
'@

$resumeHandle = 0
$clientInfo = 0
$ElementsRead = 0
$ElementsTotal = 0

Add-Type $DHCP_Structs
Add-Type -MemberDefinition $DHCP_EnumSubnetClients -Name GetDHCPInfo -Namespace Win32DHCP

$DHCPServerIP = "172.20.10.10" # change this to match your DHCP server IP

[void][Win32DHCP.GetDHCPInfo]::DhcpEnumSubnetClients($DHCPServerIP,0,[ref]$resumeHandle,0,[ref]$clientInfo,[ref]$ElementsRead,[ref]$ElementsTotal)

$clients = [system.runtime.interopservices.marshal]::PtrToStructure($clientInfo,[mystruct.DHCP_CLIENT_INFO_ARRAY])

[int]$size = $clients.NumElements
[int]$current = $clients.Clients
$ptr_array = new-object system.intptr[]($size)
$current = new-object system.intptr($current)

for ($i=0;$i -lt $size;$i++)
{
$ptr_array[$i] = [system.runtime.interopservices.marshal]::ReadIntPtr($current)
$current = $current + [system.runtime.interopservices.marshal]::SizeOf([system.IntPtr])
}

function uIntToIP {
param ($intIP)
$objIP = new-object system.net.ipaddress($intIP)
$arrIP = $objIP.IPAddressToString.split(".")
return $arrIP[3] + "." + $arrIP[2] + "." + $arrIP[1] + "." + $arrIP[0]
}

[array]$clients_array = new-object mystruct.CUSTOM_CLIENT_INFO

for ($i=0;$i -lt $size;$i++)
{
$objDHCPInfo = New-Object psobject
$current_element = [system.runtime.interopservices.marshal]::PtrToStructure($ptr_array[$i],[mystruct.DHCP_CLIENT_INFO])
add-member -inputobject $objDHCPInfo -memberType noteproperty -name ClientIP -value $(uIntToIP $current_element.ClientIpAddress)
add-member -inputobject $objDHCPInfo -memberType noteproperty -name ClientName -value $current_element.ClientName
add-member -inputobject $objDHCPInfo -memberType noteproperty -name OwnerIP -value $(uIntToIP $current_element.Ownerhost.IpAddress)
add-member -inputobject $objDHCPInfo -memberType noteproperty -name OwnerName -value $current_element.Ownerhost.NetBiosName
add-member -inputobject $objDHCPInfo -memberType noteproperty -name SubnetMask -value $(uIntToIP $current_element.SubnetMask)
add-member -inputobject $objDHCPInfo -memberType noteproperty -name LeaseExpires -value $current_element.ClientLeaseExpires.Convert()
if (($current_element.ClientLeaseExpires.Convert()).Year -ne 9999)
{add-member -inputobject $objDHCPInfo -memberType noteproperty -name LeaseObtained -value ($current_element.ClientLeaseExpires.Convert()).AddDays(-8)}

$mac = [System.String]::Format(
"{0:x2}-{1:x2}-{2:x2}-{3:x2}-{4:x2}-{5:x2}",
[system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data),
[system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data, 1),
[system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data, 2),
[system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data, 3),
[system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data, 4),
[system.runtime.interopservices.marshal]::ReadByte($current_element.ClientHardwareAddress.Data, 5)
)

add-member -inputobject $objDHCPInfo -memberType noteproperty -name MacAddress -value $mac
$objDHCPInfo
}

По сути я вставил только одну строку:

if (($current_element.ClientLeaseExpires.Convert()).Year -ne 9999) 
{add-member -inputobject $objDHCPInfo -memberType noteproperty -name LeaseObtained -value ($current_element.ClientLeaseExpires.Convert()).AddDays(-8)}
Которая добавляет дополнительное поле в выводимый объект. Значение этого поля равно значению параметра LeaseExpires - 8 дней. Можно поправить как себе задумается. В принципе можно докрутить все это счастье и узнавать на сколько был выдан адрес из параметров скоупа. Но это уже совсем другая история.

воскресенье, 17 июля 2011 г.

Всякие полезные "команды" certutil

Собственно говоря, слово "команды" тут не совсем верное, поскольку тут не совсем команды, а некоторые действия, которые можно сделать с использованием утилиты certutil. В частности очистка кеша OCSP и CRL, проверка валидности сертификата из командной строки. В общем - небольшой полезный набор.

среда, 13 июля 2011 г.

Как узнать размер папки?

Как узнать размер папки, к которой нет доступа - спросил меня коллега. Проблема в том, что при попытке сделать это привычным способом - правой кнопкой мыши, возвращается значение 0. Но при этом, сказал он, бекап ведь работает нормально. Такая себе инженерная задачка.
На самом деле тут все достаточно просто. Есть две привилегии: SeBackupPrivilege и SeRestorePrivilege. Наличие этих привилегий в токене процесса дает возможность этому процессу обходить стандартные списки доступа к файлам. Что делать? Проще всего - создать пользователя и добавить его в группу Backup Operators. Согласно настройкам в групповых политиках, эта группа получает эти привилегии. Однако тут есть тонкий момент. Новый механизм integrity level не дает добавить эти привилегии в токен любого процесса. Даже если запустить процесс, который будет  обращаться к таким файлам, от имени пользователя из группы Backup Operators с ходу ничего не получится. Нужно запускать процесс с повышенными привилегиями (elevated privileges). В этом случае все начинает работать.

Несколько статей в журналах.

Всем привет. Вот, надумал положить тут пару ссылок на мои недавние статьи в печатных изданиях.

Журналирование в Windows
Статья о возможностях подписки на события в журналах событий в Windows, и централизованного сбора этих событий.

Мониторинг своими руками
Описание технологий CIM, CIMOM, WBEM, WMI, WINRM/WINRS, WSMAN, POWERSHELL REMOTING, их возможности и немного истории.

Мониторинг основных показателей приложений. Часть 1.
О том что нужно мониторить, и как это можно делать. Будет и продолжение, но, к сожалению сейчас совсем нет времени. С другой стороны, его обязательно нужно найти.

четверг, 12 августа 2010 г.

Базовый мониторинг производительности Windows

Эти счетчики можно использовать для базового мониторинга всех рабочих станций и серверов в Windows окружении.

\Processor\% Processor Time
Основной показатель активности. Однако если наблюдать за ним, то делать это надо часто, допустим раз в секунду. Сам по себе ничего не показывает, однако если параллельно изменяются % Privileged Time или Processor Queue Length, есть смысл задуматься и понаблюдать более пристально при помощи специальных утилит.

\Processor\% Privileged Time
Показывает процентное время работы процессора в режиме ядра. В нормальном режиме это значение не должно быть высоким.
Постоянное значение более 75% говорит о проблеме

\Processor(_Total)\% User Time
Показывает процентное время работы процессора в пользовательском режиме. Это основной режим работы.

\Processor\% DPC Time
Показывает процентное время, затраченное процессором на обработку отложенных вызовов. Большие значения указывают на аппаратные проблемы, или проблемы с драйверами. Обычно этот показатель не превышает 5%. Если это значение, или выше, сохраняется длительное время - это индикатор проблемы.

\Processor(*)\% Interrupt Time
    Этот счетчик показывает процентное время, которое процессор затрачивает на обработку аппаратных прерываний. Большие значения счетчика говорят о проблеме в драйвере или аппаратных проблемах. Обычно не превышает нескольких процентов в очень короткое время.



\System\System Up Time 
Тут все ясно. Время с последней перезагрузки. Короткое время может показывать на частые перезагрузки.


\System\Processor Queue Length
Основным объектом исполнения кода являются потоки. Они же являются основным потребителем ресурсов процессора. Очередь процессора это потоки, ожидающие, пока процессор освободится. После выполнения очередного потока система передает процессор следующему из очереди. Таким образом очередь постоянной длиной в два и более потоков сигнализирует о повышеной нагрузке на процессор. Однако при анализе нужно учитывать и счетчик Processor\% Processor Time. Если его значения не высоки при наличии очередей, то это означает проблему в логике самих потоков. Это справедливо так же и для многопроцессорных систем.


\System\Context Switches/sec
    Переключение контекста происходит, когда ядро переключает процессор с одного потока на другой. В частности, это происходит если поток с более высоким приоритетом переходит в состояние готовности или рабочий поток переходит в режим ожидания операций ввода/вывода. Обычно, большие значения могут говорить о том, что в системе существует слишком много потоков.


\Memory\Pages/sec
Достаточно хитрый счетчик. Он показывает, сколько страниц в сек было прочитано или записано в рамках обработки страничного прерывания. Это прерывание возникает тогда, когда искомая страница памяти оказывается выгруженой на диск в данный момент. Однако в этот счетчик попадают так же и рабочие ситуации связанные с кэшем и memory-mapped файлами. По этому по нему нельзя однозначно сказать о недостатке памяти.


\Memory\Page Reads /sec
Счетчик, который можно использовать в дополнение к предыдущему. Он показывает сколько операций чтения в единицу времени, безотносительно к страницам, было сделано при обработке страничных прерываний.


Memory\Pages Input/sec
Количество страниц, прочитанных с диска, в рамках обработки страничных прерываний.


Memory\Page Reads/sec
Количество страниц, записанных на диск, в рамках обработки страничных прерываний.


\Memory\ Cache Faults
Счетчик учитывает промахи системного кэша.
Таким образом, если \Memory\Pages/sec, \Memory\Page Reads /sec,  Memory\Pages Input/sec, Memory\Page Reads/sec постоянно находятся на высоком уровне, а \Memory\Cache Faults на низком, то можно предположить, что операционная система активно работает с файлом подкачки, что, в свою очередь, сигнализирует о недостатке памяти. Однако, если \Memory\Cache Faults тоже высок, то скорее всего ситуация вызвана активной работой с большими файлами, отображаемыми в память. Такое поведение не должно длиться долго.


\Memory\Available MBytes
Количество доступной процессам физической памяти. Уменьшение счетчика, сопровождаемое ростом \Memory\Pages/sec и \Memory\Page Reads /sec может сигнализировать о недостатке памяти. Постоянное и равномерное уменьшение счетчика указывает на утечку памяти в одном из приложений.


Memory\Transition Faults/sec
Значение это счетчика показывает скорость, с которой обрабатываются так называемые soft faults - ошибки страниц, для разрешения которых не нужны обращения к диску. Количество Transition Faults равно количеству таких страниц.


\LogicalDisk(*)\% Free Space
Счетчик, показывающий процент свободного места на логических дисках.


LogicalDisk|PhysicalDisk\Avg. Disk Queue Length
Счетчик следит за количеством запросов, стоящих в очереди к диску. Считается, что, если очередь к диску длительное время более двух - это может быть индикатором проблемы.

среда, 4 августа 2010 г.

Случай с тормозящим компом

Van Helsing's ToolKitImage by Compound Eye via Flickr
Странные, странные, странные проблемы. Соседний компьютер, вдруг, ни с того ни с сего начал жутко тормозить. Мышка двигается рывками, дергается, замирает. Окошки отрисовываются очень медленно. Процессы запускаются долго и вдумчиво :). В общем, все совсем неприятно.

Решил немного разобраться с этим вопросом. Надо же было помочь коллеге. Короче говоря, первым делом был запущен process explorer. Он показал, что основным напрягающим фактором являются прерывания. Они поглощали от 60% процессорного времени. К сожалению, при помощи самого procexp нельзя посмотреть, какой компонент системы отвечает за возникающие прерывания. Однако для этой цели есть другой инструмент - xperf. Он является частью Windows Perfomance Toolkit, который в свою очередь можно скачать вместе с Windows SDK. В общем и целом, будем использовать его. Правда тут есть небольшой момент. Клиентская система, на которой все это происходило, работала на Windows XP. К сожалению для этой операционной системы нет xperf. Однако можно просто проинсталлировать Perfomance Toolkit на Vista или Windows 7, а затем скопировать папку с xperf на нашего клиента. Далее, на клиентской системе, в командной строке с правами администратора запускаем ETW трассировку при помощи xperf:

xperf -on latency -stackwalk profile

Теперь наша задача - добиться повторения. Ну в моем случае это не составило трудности. После того, как трассировка поработала какое-то время, делаем вот так:

xperf -d tracecpu.etl

Теперь открываем этот файл и смотрим в него. Видим мы примерно следующее:
 

Как видно из ролика, основным, так сказать, потребителем прерываний является драйвер, отвечающий за дисковую подсистему, atapi.sys.

После этого первым деля м внимательно посмотрел на винт. Оказалось, что шлейф, которым он подключен – поврежден. Замена шлейфа решила проблему.