PowerShell/src/Microsoft.PowerShell.Commands.Diagnostics/ExportCounterCommand.cs

384 lines
13 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Net;
using System.Reflection;
using System.Resources;
using System.Security;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Xml;
using Microsoft.PowerShell.Commands.Diagnostics.Common;
using Microsoft.PowerShell.Commands.GetCounter;
using Microsoft.Powershell.Commands.GetCounter.PdhNative;
namespace Microsoft.PowerShell.Commands
{
///
/// Class that implements the Get-Counter cmdlet.
///
[Cmdlet(VerbsData.Export, "Counter", DefaultParameterSetName = "ExportCounterSet", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=138337")]
public sealed class ExportCounterCommand : PSCmdlet
{
//
// Path parameter
//
[Parameter(
Mandatory = true,
Position = 0,
ValueFromPipelineByPropertyName = true,
HelpMessageBaseName = "GetEventResources")]
[Alias("PSPath")]
public string Path
{
get { return _path; }
set { _path = value; }
}
private string _path;
private string _resolvedPath;
//
// Format parameter.
// Valid strings are "blg", "csv", "tsv" (case-insensitive).
//
[Parameter(
Mandatory = false,
ValueFromPipeline = false,
ValueFromPipelineByPropertyName = false,
HelpMessageBaseName = "GetEventResources")]
[ValidateNotNull]
[ValidateSet("blg", "csv", "tsv")]
public string FileFormat
{
get { return _format; }
set { _format = value; }
}
private string _format = "blg";
//
// MaxSize parameter
// Maximum output file size, in megabytes.
//
[Parameter(
HelpMessageBaseName = "GetEventResources")]
public UInt32 MaxSize
{
get { return _maxSize; }
set { _maxSize = value; }
}
private UInt32 _maxSize = 0;
//
// InputObject parameter
//
[Parameter(
Mandatory = true,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
HelpMessageBaseName = "GetEventResources")]
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays",
Scope = "member",
Target = "Microsoft.PowerShell.Commands.ExportCounterCommand.InputObject",
Justification = "A PerformanceCounterSampleSet[] is required here because Powershell supports arrays natively.")]
public PerformanceCounterSampleSet[] InputObject
{
get { return _counterSampleSets; }
set { _counterSampleSets = value; }
}
private PerformanceCounterSampleSet[] _counterSampleSets = new PerformanceCounterSampleSet[0];
//
// Force switch
//
[Parameter(
HelpMessageBaseName = "GetEventResources")]
public SwitchParameter Force
{
get { return _force; }
set { _force = value; }
}
private SwitchParameter _force;
//
// Circular switch
//
[Parameter(
HelpMessageBaseName = "GetEventResources")]
public SwitchParameter Circular
{
get { return _circular; }
set { _circular = value; }
}
private SwitchParameter _circular;
private ResourceManager _resourceMgr = null;
private PdhHelper _pdhHelper = null;
private bool _stopping = false;
private bool _queryInitialized = false;
private PdhLogFileType _outputFormat = PdhLogFileType.PDH_LOG_TYPE_BINARY;
//
// BeginProcessing() is invoked once per pipeline
//
protected override void BeginProcessing()
{
#if CORECLR
if (Platform.IsIoT)
{
// IoT does not have the '$env:windir\System32\pdh.dll' assembly which is required by this cmdlet.
throw new PlatformNotSupportedException();
}
// PowerShell 7 requires at least Windows 7,
// so no version test is needed
_pdhHelper = new PdhHelper(false);
#else
//
// Determine the OS version: this cmdlet requires Windows 7
// because it uses new Pdh functionality.
//
Version osVersion = System.Environment.OSVersion.Version;
if (osVersion.Major < 6 ||
(osVersion.Major == 6 && osVersion.Minor < 1))
{
string msg = _resourceMgr.GetString("ExportCtrWin7Required");
Exception exc = new Exception(msg);
ThrowTerminatingError(new ErrorRecord(exc, "ExportCtrWin7Required", ErrorCategory.NotImplemented, null));
}
_pdhHelper = new PdhHelper(osVersion.Major < 6);
#endif
_resourceMgr = Microsoft.PowerShell.Commands.Diagnostics.Common.CommonUtilities.GetResourceManager();
//
// Set output format (log file type)
//
SetOutputFormat();
if (Circular.IsPresent && _maxSize == 0)
{
string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterCircularNoMaxSize"));
Exception exc = new Exception(msg);
WriteError(new ErrorRecord(exc, "CounterCircularNoMaxSize", ErrorCategory.InvalidResult, null));
}
uint res = _pdhHelper.ConnectToDataSource();
if (res != 0)
{
ReportPdhError(res, true);
}
res = _pdhHelper.OpenQuery();
if (res != 0)
{
ReportPdhError(res, true);
}
}
//
// EndProcessing() is invoked once per pipeline
//
protected override void EndProcessing()
{
_pdhHelper.Dispose();
}
///
/// Handle Control-C
///
protected override void StopProcessing()
{
_stopping = true;
_pdhHelper.Dispose();
}
//
// ProcessRecord() override.
// This is the main entry point for the cmdlet.
// When counter data comes from the pipeline, this gets invoked for each pipelined object.
// When it's passed in as an argument, ProcessRecord() is called once for the entire _counterSampleSets array.
//
protected override void ProcessRecord()
{
Debug.Assert(_counterSampleSets.Length != 0 && _counterSampleSets[0] != null);
ResolvePath();
uint res = 0;
if (!_queryInitialized)
{
if (_format.ToLowerInvariant().Equals("blg"))
{
res = _pdhHelper.AddRelogCounters(_counterSampleSets[0]);
}
else
{
res = _pdhHelper.AddRelogCountersPreservingPaths(_counterSampleSets[0]);
}
if (res != 0)
{
ReportPdhError(res, true);
}
res = _pdhHelper.OpenLogForWriting(_resolvedPath, _outputFormat, Force.IsPresent, _maxSize * 1024 * 1024, Circular.IsPresent, null);
if (res == PdhResults.PDH_FILE_ALREADY_EXISTS)
{
string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterFileExists"), _resolvedPath);
Exception exc = new Exception(msg);
ThrowTerminatingError(new ErrorRecord(exc, "CounterFileExists", ErrorCategory.InvalidResult, null));
}
else if (res == PdhResults.PDH_LOG_FILE_CREATE_ERROR)
{
string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("FileCreateFailed"), _resolvedPath);
Exception exc = new Exception(msg);
ThrowTerminatingError(new ErrorRecord(exc, "FileCreateFailed", ErrorCategory.InvalidResult, null));
}
else if (res == PdhResults.PDH_LOG_FILE_OPEN_ERROR)
{
string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("FileOpenFailed"), _resolvedPath);
Exception exc = new Exception(msg);
ThrowTerminatingError(new ErrorRecord(exc, "FileOpenFailed", ErrorCategory.InvalidResult, null));
}
else if (res != 0)
{
ReportPdhError(res, true);
}
_queryInitialized = true;
}
foreach (PerformanceCounterSampleSet set in _counterSampleSets)
{
_pdhHelper.ResetRelogValues();
foreach (PerformanceCounterSample sample in set.CounterSamples)
{
bool bUnknownKey = false;
res = _pdhHelper.SetCounterValue(sample, out bUnknownKey);
if (bUnknownKey)
{
string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterExportSampleNotInInitialSet"), sample.Path, _resolvedPath);
Exception exc = new Exception(msg);
WriteError(new ErrorRecord(exc, "CounterExportSampleNotInInitialSet", ErrorCategory.InvalidResult, null));
}
else if (res != 0)
{
ReportPdhError(res, true);
}
}
res = _pdhHelper.WriteRelogSample(set.Timestamp);
if (res != 0)
{
ReportPdhError(res, true);
}
if (_stopping)
{
break;
}
}
}
// Determines Log File Type based on FileFormat parameter
//
private void SetOutputFormat()
{
switch (_format.ToLowerInvariant())
{
case "csv":
_outputFormat = PdhLogFileType.PDH_LOG_TYPE_CSV;
break;
case "tsv":
_outputFormat = PdhLogFileType.PDH_LOG_TYPE_TSV;
break;
default: // By default file format is blg
_outputFormat = PdhLogFileType.PDH_LOG_TYPE_BINARY;
break;
}
}
private void ResolvePath()
{
try
{
Collection<PathInfo> result = null;
result = SessionState.Path.GetResolvedPSPathFromPSPath(_path);
if (result.Count > 1)
{
string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("ExportDestPathAmbiguous"), _path);
Exception exc = new Exception(msg);
ThrowTerminatingError(new ErrorRecord(exc, "ExportDestPathAmbiguous", ErrorCategory.InvalidArgument, null));
}
foreach (PathInfo currentPath in result)
{
_resolvedPath = currentPath.ProviderPath;
}
}
catch (ItemNotFoundException pathNotFound)
{
//
// This is an expected condition - we will be creating a new file
//
_resolvedPath = pathNotFound.ItemName;
}
}
private void ReportPdhError(uint res, bool bTerminate)
{
string msg;
uint formatRes = CommonUtilities.FormatMessageFromModule(res, "pdh.dll", out msg);
if (formatRes != 0)
{
msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterApiError"), res);
}
Exception exc = new Exception(msg);
if (bTerminate)
{
ThrowTerminatingError(new ErrorRecord(exc, "CounterApiError", ErrorCategory.InvalidResult, null));
}
else
{
WriteError(new ErrorRecord(exc, "CounterApiError", ErrorCategory.InvalidResult, null));
}
}
}
}