Skip to content

Commit 5ff4bf2

Browse files
authored
Merge pull request #3717 from ReeceGoding/dev
sp_BlitzFirst: Made the sys.dm_os_ring_buffers CPU checks respect Linux always setting system_idle to 0.
2 parents 6cccd74 + 44d3f81 commit 5ff4bf2

File tree

1 file changed

+56
-17
lines changed

1 file changed

+56
-17
lines changed

sp_BlitzFirst.sql

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,18 @@ DECLARE @StringToExecute NVARCHAR(MAX),
145145
@get_thread_time_ms NVARCHAR(MAX) = N'',
146146
@thread_time_ms FLOAT = 0,
147147
@logical_processors INT = 0,
148-
@max_worker_threads INT = 0;
148+
@max_worker_threads INT = 0,
149+
@is_windows_operating_system BIT = 1;
150+
151+
IF EXISTS
152+
(
153+
SELECT 1
154+
FROM sys.all_objects
155+
WHERE name = 'dm_os_host_info'
156+
)
157+
BEGIN
158+
SELECT @is_windows_operating_system = CASE WHEN host_platform = 'Windows' THEN 1 ELSE 0 END FROM sys.dm_os_host_info;
159+
END;
149160

150161
/* Sanitize our inputs */
151162
SELECT
@@ -2399,19 +2410,28 @@ If one of them is a lead blocker, consider killing that query.'' AS HowToStopit,
23992410
RAISERROR('Running CheckID 24',10,1) WITH NOWAIT;
24002411
END
24012412

2413+
/* Traditionally, we use 100 - SystemIdle here.
2414+
However, SystemIdle is always 0 on Linux.
2415+
So if we are on Linux, we use ProcessUtilization instead.
2416+
This is the approach found in
2417+
https://techcommunity.microsoft.com/blog/sqlserver/sql-server-cpu-usage-available-in-sys-dm-os-ring-buffers-dmv-starting-sql-server/825361 */
24022418
INSERT INTO #BlitzFirstResults (CheckID, Priority, FindingsGroup, Finding, Details, DetailsInt, URL)
2403-
SELECT 24, 50, 'Server Performance', 'High CPU Utilization', CAST(100 - SystemIdle AS NVARCHAR(20)) + N'%.', 100 - SystemIdle, 'https://www.brentozar.com/go/cpu'
2419+
SELECT 24, 50, 'Server Performance', 'High CPU Utilization', CAST(CpuUsage AS NVARCHAR(20)) + N'%.', CpuUsage, 'https://www.brentozar.com/go/cpu'
2420+
FROM (
2421+
SELECT CASE WHEN @is_windows_operating_system = 1 THEN 100 - SystemIdle ELSE ProcessUtilization END AS CpuUsage
24042422
FROM (
24052423
SELECT record,
2406-
record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') AS SystemIdle
2424+
record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') AS SystemIdle,
2425+
record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') AS ProcessUtilization
24072426
FROM (
24082427
SELECT TOP 1 CONVERT(XML, record) AS record
24092428
FROM sys.dm_os_ring_buffers
24102429
WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR'
24112430
AND record LIKE '%<SystemHealth>%'
24122431
ORDER BY timestamp DESC) AS rb
2413-
) AS y
2414-
WHERE 100 - SystemIdle >= 50;
2432+
) AS ShreddedCpuXml
2433+
) AS OsCpu
2434+
WHERE CpuUsage >= 50;
24152435

24162436
/* CPU Utilization - CheckID 23 */
24172437
IF (@Debug = 1)
@@ -2423,7 +2443,8 @@ If one of them is a lead blocker, consider killing that query.'' AS HowToStopit,
24232443
WITH y
24242444
AS
24252445
(
2426-
SELECT CONVERT(VARCHAR(5), 100 - ca.c.value('.', 'INT')) AS system_idle,
2446+
/* See earlier comments about SystemIdle on Linux. */
2447+
SELECT CONVERT(VARCHAR(5), CASE WHEN @is_windows_operating_system = 1 THEN 100 - ca.c.value('.', 'INT') ELSE ca2.p.value('.', 'INT') END) AS cpu_usage,
24272448
CONVERT(VARCHAR(30), rb.event_date) AS event_date,
24282449
CONVERT(VARCHAR(8000), rb.record) AS record,
24292450
event_date as event_date_raw
@@ -2436,19 +2457,20 @@ If one of them is a lead blocker, consider killing that query.'' AS HowToStopit,
24362457
WHERE dorb.ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR'
24372458
AND record LIKE '%<SystemHealth>%' ) AS rb
24382459
CROSS APPLY rb.record.nodes('/Record/SchedulerMonitorEvent/SystemHealth/SystemIdle') AS ca(c)
2460+
CROSS APPLY rb.record.nodes('/Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization') AS ca2(p)
24392461
)
24402462
INSERT INTO #BlitzFirstResults (CheckID, Priority, FindingsGroup, Finding, Details, DetailsInt, URL, HowToStopIt)
24412463
SELECT TOP 1
24422464
23,
24432465
250,
24442466
'Server Info',
24452467
'CPU Utilization',
2446-
y.system_idle + N'%. Ring buffer details: ' + CAST(y.record AS NVARCHAR(4000)),
2447-
y.system_idle ,
2468+
y.cpu_usage + N'%. Ring buffer details: ' + CAST(y.record AS NVARCHAR(4000)),
2469+
y.cpu_usage ,
24482470
'https://www.brentozar.com/go/cpu',
24492471
STUFF(( SELECT TOP 2147483647
24502472
CHAR(10) + CHAR(13)
2451-
+ y2.system_idle
2473+
+ y2.cpu_usage
24522474
+ '% ON '
24532475
+ y2.event_date
24542476
+ ' Ring buffer details: '
@@ -2479,7 +2501,12 @@ If one of them is a lead blocker, consider killing that query.'' AS HowToStopit,
24792501
AND record LIKE '%<SystemHealth>%'
24802502
ORDER BY timestamp DESC) AS rb
24812503
) AS y
2482-
WHERE 100 - (y.SQLUsage + y.SystemIdle) >= 25;
2504+
WHERE 100 - (y.SQLUsage + y.SystemIdle) >= 25
2505+
/* SystemIdle is always 0 on Linux, as described earlier.
2506+
We therefore cannot distinguish between a totally idle Linux server and
2507+
a Linux server where SQL Server is being crushed by other CPU-heavy processes.
2508+
We therefore disable this check on Linux. */
2509+
AND @is_windows_operating_system = 1;
24832510

24842511
END; /* IF @Seconds < 30 */
24852512

@@ -3593,18 +3620,24 @@ If one of them is a lead blocker, consider killing that query.'' AS HowToStopit,
35933620
END
35943621

35953622
INSERT INTO #BlitzFirstResults (CheckID, Priority, FindingsGroup, Finding, Details, DetailsInt, URL)
3596-
SELECT 24, 50, 'Server Performance', 'High CPU Utilization', CAST(100 - SystemIdle AS NVARCHAR(20)) + N'%. Ring buffer details: ' + CAST(record AS NVARCHAR(4000)), 100 - SystemIdle, 'https://www.brentozar.com/go/cpu'
3623+
SELECT 24, 50, 'Server Performance', 'High CPU Utilization', CAST(CpuUsage AS NVARCHAR(20)) + N'%. Ring buffer details: ' + CAST(record AS NVARCHAR(4000)), CpuUsage, 'https://www.brentozar.com/go/cpu'
3624+
FROM (
3625+
SELECT record,
3626+
CASE WHEN @is_windows_operating_system = 1 THEN 100 - SystemIdle ELSE ProcessUtilization END AS CpuUsage
35973627
FROM (
35983628
SELECT record,
3599-
record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') AS SystemIdle
3629+
record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') AS SystemIdle,
3630+
/* See earlier comments about SystemIdle on Linux. */
3631+
record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') AS ProcessUtilization
36003632
FROM (
36013633
SELECT TOP 1 CONVERT(XML, record) AS record
36023634
FROM sys.dm_os_ring_buffers
36033635
WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR'
36043636
AND record LIKE '%<SystemHealth>%'
36053637
ORDER BY timestamp DESC) AS rb
3606-
) AS y
3607-
WHERE 100 - SystemIdle >= 50;
3638+
) AS ShreddedCpuXml
3639+
) AS OsCpu
3640+
WHERE CpuUsage >= 50;
36083641

36093642
/* Server Performance - CPU Utilization CheckID 23 */
36103643
IF (@Debug = 1)
@@ -3613,17 +3646,23 @@ If one of them is a lead blocker, consider killing that query.'' AS HowToStopit,
36133646
END
36143647

36153648
INSERT INTO #BlitzFirstResults (CheckID, Priority, FindingsGroup, Finding, Details, DetailsInt, URL)
3616-
SELECT 23, 250, 'Server Info', 'CPU Utilization', CAST(100 - SystemIdle AS NVARCHAR(20)) + N'%. Ring buffer details: ' + CAST(record AS NVARCHAR(4000)), 100 - SystemIdle, 'https://www.brentozar.com/go/cpu'
3649+
SELECT 23, 250, 'Server Info', 'CPU Utilization', CAST(CpuUsage AS NVARCHAR(20)) + N'%. Ring buffer details: ' + CAST(record AS NVARCHAR(4000)), CpuUsage, 'https://www.brentozar.com/go/cpu'
3650+
FROM (
3651+
SELECT record,
3652+
CASE WHEN @is_windows_operating_system = 1 THEN 100 - SystemIdle ELSE ProcessUtilization END AS CpuUsage
36173653
FROM (
36183654
SELECT record,
3619-
record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') AS SystemIdle
3655+
record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') AS SystemIdle,
3656+
/* See earlier comments about SystemIdle on Linux. */
3657+
record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') AS ProcessUtilization
36203658
FROM (
36213659
SELECT TOP 1 CONVERT(XML, record) AS record
36223660
FROM sys.dm_os_ring_buffers
36233661
WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR'
36243662
AND record LIKE '%<SystemHealth>%'
36253663
ORDER BY timestamp DESC) AS rb
3626-
) AS y;
3664+
) AS ShreddedCpuXml
3665+
) AS OsCpu;
36273666

36283667
END; /* IF @Seconds >= 30 */
36293668

0 commit comments

Comments
 (0)