| File: Linux\Disk\DiskStatsReader.cs | Web Access |
| Project: src\src\Libraries\Microsoft.Extensions.Diagnostics.ResourceMonitoring\Microsoft.Extensions.Diagnostics.ResourceMonitoring.csproj (Microsoft.Extensions.Diagnostics.ResourceMonitoring) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Globalization; using System.IO; using Microsoft.Extensions.ObjectPool; using Microsoft.Shared.Pools; namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Disk; /// <summary> /// Handles reading and parsing of Linux procfs-diskstats file(/proc/diskstats). /// </summary> internal sealed class DiskStatsReader(IFileSystem fileSystem) : IDiskStatsReader { private static readonly FileInfo _diskStatsFile = new("/proc/diskstats"); private static readonly ObjectPool<BufferWriter<char>> _sharedBufferWriterPool = BufferWriterPool.CreateBufferWriterPool<char>(); /// <summary> /// Reads and returns all disk statistics entries. /// </summary> /// <returns>List of <see cref="DiskStats"/>.</returns> public DiskStats[] ReadAll(string[] skipDevicePrefixes) { var diskStatsList = new List<DiskStats>(); using ReturnableBufferWriter<char> bufferWriter = new(_sharedBufferWriterPool); using IEnumerator<ReadOnlyMemory<char>> enumerableLines = fileSystem.ReadAllByLines(_diskStatsFile, bufferWriter.Buffer).GetEnumerator(); while (enumerableLines.MoveNext()) { string line = enumerableLines.Current.Trim().ToString(); if (string.IsNullOrWhiteSpace(line)) { continue; } try { DiskStats stat = DiskStatsReader.ParseLine(line); if (!Array.Exists(skipDevicePrefixes, prefix => stat.DeviceName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))) { diskStatsList.Add(stat); } } catch (Exception) { // ignore parsing errors } } return diskStatsList.ToArray(); } /// <summary> /// Parses one line of text into a DiskStats object. /// </summary> /// <param name="line">one line in "/proc/diskstats".</param> /// <returns>parsed DiskStats object.</returns> private static DiskStats ParseLine(string line) { // Split by any whitespace and remove empty entries #pragma warning disable EA0009 string[] parts = line.Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries); #pragma warning restore EA0009 if (parts.Length < 14) { throw new FormatException($"Not enough fields: expected at least 14, got {parts.Length}"); } // See https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats var diskStats = new DiskStats { MajorNumber = int.Parse(parts[0], CultureInfo.InvariantCulture), MinorNumber = int.Parse(parts[1], CultureInfo.InvariantCulture), DeviceName = parts[2], ReadsCompleted = ulong.Parse(parts[3], CultureInfo.InvariantCulture), ReadsMerged = ulong.Parse(parts[4], CultureInfo.InvariantCulture), SectorsRead = ulong.Parse(parts[5], CultureInfo.InvariantCulture), TimeReadingMs = uint.Parse(parts[6], CultureInfo.InvariantCulture), WritesCompleted = ulong.Parse(parts[7], CultureInfo.InvariantCulture), WritesMerged = ulong.Parse(parts[8], CultureInfo.InvariantCulture), SectorsWritten = ulong.Parse(parts[9], CultureInfo.InvariantCulture), TimeWritingMs = uint.Parse(parts[10], CultureInfo.InvariantCulture), IoInProgress = uint.Parse(parts[11], CultureInfo.InvariantCulture), TimeIoMs = uint.Parse(parts[12], CultureInfo.InvariantCulture), WeightedTimeIoMs = uint.Parse(parts[13], CultureInfo.InvariantCulture) }; // Parse additional fields if present if (parts.Length >= 18) { diskStats.DiscardsCompleted = ulong.Parse(parts[14], CultureInfo.InvariantCulture); diskStats.DiscardsMerged = ulong.Parse(parts[15], CultureInfo.InvariantCulture); diskStats.SectorsDiscarded = ulong.Parse(parts[16], CultureInfo.InvariantCulture); diskStats.TimeDiscardingMs = uint.Parse(parts[17], CultureInfo.InvariantCulture); } if (parts.Length >= 20) { diskStats.FlushRequestsCompleted = ulong.Parse(parts[18], CultureInfo.InvariantCulture); diskStats.TimeFlushingMs = uint.Parse(parts[19], CultureInfo.InvariantCulture); } return diskStats; } }