From d4877538af321498633cd5fa772a5dd1929351e4 Mon Sep 17 00:00:00 2001 From: "Michael Bernstein (DPLAT)" Date: Fri, 30 Aug 2024 18:54:13 -0700 Subject: [PATCH 1/3] Add support for querying CPU rate controls - Add support for querying the job object on CPU rate control limits - Add a wrapper function to convert this to a simple integer - Add a script call type: CpuRateControl --- .../Query-JobLimits/Query-JobLimits.ps1 | 110 +++++++++++++++++- 1 file changed, 106 insertions(+), 4 deletions(-) diff --git a/helpful_tools/Query-JobLimits/Query-JobLimits.ps1 b/helpful_tools/Query-JobLimits/Query-JobLimits.ps1 index 595e285..0b54f3d 100644 --- a/helpful_tools/Query-JobLimits/Query-JobLimits.ps1 +++ b/helpful_tools/Query-JobLimits/Query-JobLimits.ps1 @@ -30,7 +30,7 @@ [CmdletBinding(DefaultParameterSetName="Standard")] param( [Parameter(Mandatory=$True)] - [ValidateSet("JobMemoryLimit", "PeakJobMemoryUsed")] + [ValidateSet("JobMemoryLimit", "PeakJobMemoryUsed", "CpuRateControl")] [string]$LimitType ) @@ -55,6 +55,11 @@ Add-Type @" JobObjectBasicAndIoAccountingInformation, JobObjectExtendedLimitInformation, JobObjectJobSetInformation, + JobObjectGroupInformation, + JobObjectNotificationLimitInformation, + JobObjectLimitViolationInformation, + JobObjectGroupInformationEx, + JobObjectCpuRateControlInformation, MaxJobObjectInfoClass, } @@ -80,6 +85,14 @@ Add-Type @" public const UInt32 JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000; public const UInt32 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000; + // + // CPU Rate Control Flags + // + public const UInt32 JOB_OBJECT_CPU_RATE_CONTROL_ENABLE = 0x00000001; + public const UInt32 JOB_OBJECT_CPU_RATE_CONTROL_WEIGHT_BASED = 0x00000002; + public const UInt32 JOB_OBJECT_CPU_RATE_CONTROL_HARD_CAP = 0x00000004; + public const UInt32 JOB_OBJECT_CPU_RATE_CONTROL_NOTIFY = 0x00000008; + [StructLayout(LayoutKind.Sequential)] public struct JOBOBJECT_BASIC_LIMIT_INFORMATION { @@ -116,6 +129,13 @@ Add-Type @" public UIntPtr PeakJobMemoryUsed; } + [StructLayout(LayoutKind.Sequential)] + public struct JOBOBJECT_CPU_RATE_CONTROL_INFORMATION + { + public UInt32 ControlFlags; + public UInt32 Data; + } + [DllImport("kernel32.dll", CharSet = CharSet.Auto, EntryPoint = "QueryInformationJobObject", SetLastError = true)] public static extern bool QueryInformationJobObject( IntPtr handleJob, @@ -137,7 +157,7 @@ Add-Type @" { // Marshal.AllocHGlobal will throw on failure, so we do not need to // check for allocation failure. - ptrData = Marshal.AllocHGlobal( inSize ); + ptrData = Marshal.AllocHGlobal( inSize ); UInt32 outSize = 0; // Query the job object for its extended limits @@ -168,21 +188,103 @@ Add-Type @" } } } + + // Query the CPU rate control info for the running job + // If this is run outside of an executing job, or if the CPU rates are not set, + // the script will return zero. + public static JOBOBJECT_CPU_RATE_CONTROL_INFORMATION QueryCPURateControlInformation() + { + // Allocate an JOBOBJECT_CPU_RATE_CONTROL_INFORMATION + int inSize = Marshal.SizeOf(typeof(Api.JOBOBJECT_CPU_RATE_CONTROL_INFORMATION)); + IntPtr ptrData = IntPtr.Zero; + try + { + // Marshal.AllocHGlobal will throw on failure, so we do not need to + // check for allocation failure. + ptrData = Marshal.AllocHGlobal( inSize ); + UInt32 outSize = 0; + + // Query the job object for its extended limits + bool result = Api.QueryInformationJobObject(IntPtr.Zero, + Api.JOBOBJECTINFOCLASS.JobObjectCpuRateControlInformation, + ptrData, + (UInt32)inSize, + ref outSize); + if (result) + { + // Marshal the result data into a .NET structure + Api.JOBOBJECT_CPU_RATE_CONTROL_INFORMATION jobinfo = + (Api.JOBOBJECT_CPU_RATE_CONTROL_INFORMATION)Marshal.PtrToStructure(ptrData, typeof(Api.JOBOBJECT_CPU_RATE_CONTROL_INFORMATION)); + + // Return the extended limit information to the caller + return jobinfo; + } + else + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } + finally + { + if (ptrData != IntPtr.Zero) + { + Marshal.FreeHGlobal( ptrData ); + } + } + } + + public static UInt32 GetCpuLimit() + { + JOBOBJECT_CPU_RATE_CONTROL_INFORMATION cpuRateControl = QueryCPURateControlInformation(); + + // Is CPU rate control enabled? + if ((cpuRateControl.ControlFlags & JOB_OBJECT_CPU_RATE_CONTROL_ENABLE) == 0) + { + Console.WriteLine("# CPU rate control is not enabled."); + return 0; + } + + // Is CPU rate control weight-based? + if ((cpuRateControl.ControlFlags & JOB_OBJECT_CPU_RATE_CONTROL_WEIGHT_BASED) != 0) + { + Console.WriteLine("# CPU rate control is weight-based."); + return cpuRateControl.Data; + } + + // Is CPU rate control hard cap? + if ((cpuRateControl.ControlFlags & JOB_OBJECT_CPU_RATE_CONTROL_HARD_CAP) != 0) + { + Console.WriteLine("# CPU rate control is hard cap."); + return cpuRateControl.Data; + } + + // Otherwise, default to zero. + return 0; + } } "@ -$result = [Api]::QueryExtendedLimitInformation() - switch ($LimitType) { + # Returns the job memory limit in bytes "JobMemoryLimit" { + $result = [Api]::QueryExtendedLimitInformation() $result.JobMemoryLimit } + # Returns the peak job memory used in bytes "PeakJobMemoryUsed" { + $result = [Api]::QueryExtendedLimitInformation() $result.PeakJobMemoryUsed } + # Returns the CPU rate control, if enabled, + # which is generally a cycle count out of a total of 10000 + "CpuRateControl" { + $result = [Api]::GetCpuLimit() + $result + } + Default { Write-Error "Limit type unknown: $LimitType" } From 4b5a7c329cac3e42a37aa856a60fb50cb4774bd7 Mon Sep 17 00:00:00 2001 From: "Michael Bernstein (DPLAT)" Date: Mon, 9 Sep 2024 11:29:13 -0700 Subject: [PATCH 2/3] Improve script parameter documentation --- helpful_tools/Query-JobLimits/Query-JobLimits.ps1 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/helpful_tools/Query-JobLimits/Query-JobLimits.ps1 b/helpful_tools/Query-JobLimits/Query-JobLimits.ps1 index 0b54f3d..103951f 100644 --- a/helpful_tools/Query-JobLimits/Query-JobLimits.ps1 +++ b/helpful_tools/Query-JobLimits/Query-JobLimits.ps1 @@ -19,8 +19,11 @@ .DESCRIPTION Queries the job limits from inside a process-isolated container - .PARAMETER Verbose - If passed, dump verbose output + .PARAMETER LimitType + Determines the type of limit to query. The following values are supported: + - JobMemoryLimit: Returns the job memory limit in bytes + - PeakJobMemoryUsed: Returns the peak job memory used in bytes + - CpuRateControl: Returns the CPU rate control, if enabled, which is generally a cycle count out of a total of 10000 .EXAMPLE .\Query-JobLimits.ps1 From 52cc47d53861f7ddaaded40bec80354112d4189a Mon Sep 17 00:00:00 2001 From: "Michael Bernstein (DPLAT)" Date: Wed, 25 Sep 2024 15:54:30 -0700 Subject: [PATCH 3/3] Add a README.md file to describe the utility: --- .../Query-JobLimits/Query-JobLimits.ps1 | 1 + helpful_tools/Query-JobLimits/README.md | 56 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 helpful_tools/Query-JobLimits/README.md diff --git a/helpful_tools/Query-JobLimits/Query-JobLimits.ps1 b/helpful_tools/Query-JobLimits/Query-JobLimits.ps1 index 103951f..362c023 100644 --- a/helpful_tools/Query-JobLimits/Query-JobLimits.ps1 +++ b/helpful_tools/Query-JobLimits/Query-JobLimits.ps1 @@ -1,6 +1,7 @@ ############################################################ # Script to query the job limits inside a process-isolated container ############################################################ + <# .NOTES Copyright (c) Microsoft Corporation. All rights reserved. diff --git a/helpful_tools/Query-JobLimits/README.md b/helpful_tools/Query-JobLimits/README.md new file mode 100644 index 0000000..fb4a81b --- /dev/null +++ b/helpful_tools/Query-JobLimits/README.md @@ -0,0 +1,56 @@ +## Query-JobLimits.ps1 + +#### NAME + Query-JobLimits.ps1 + +#### SYNOPSIS + Queries the job limits from inside a process-isolated container + +#### SYNTAX + Query-JobLimits.ps1 [-LimitType ] [] + + +#### DESCRIPTION + Queries the job limits from inside a process-isolated container + + Once the container platform has set a CPU or Memory limit, this utility + can be used to query those limits and potentially pass them to processes + or workloads inside the container. + +#### PARAMETERS + -LimitType [] + The limit type to query from within the container + + Required? True + Position? named + Default value + Accept pipeline input? false + Accept wildcard characters? false + + Determines the type of limit to query. The following values are supported: + - JobMemoryLimit: Returns the job memory limit in bytes + - PeakJobMemoryUsed: Returns the peak job memory used in bytes + - CpuRateControl: Returns the CPU rate control, if enabled, which is generally a cycle count out of a total of + 10000 + +#### NOTES + Copyright (c) Microsoft Corporation. All rights reserved. + + Use of this sample source code is subject to the terms of the Microsoft + license agreement under which you licensed this sample source code. If + you did not accept the terms of the license agreement, you are not + authorized to use this sample source code. For the terms of the license, + please see the license agreement between you and Microsoft or, if applicable, + see the LICENSE.RTF on your install media or the root of your tools installation. + THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES. + +#### Examples + + PS C:\>.\Query-JobLimits.ps1 -LimitType JobMemoryLimit + + PS C:\>.\Query-JobLimits.ps1 -LimitType PeakJobMemoryUsed + + PS C:\>.\Query-JobLimits.ps1 -LimitType CpuRateControl + +#### Prerequisites +Requires PowerShell version 5.0