blob: b797b09e837678dd482d3881c397a2c46fbeb302 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Apache.Ignite.Core.Impl.Common
{
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Apache.Ignite.Core.Impl.Unmanaged;
/// <summary>
/// Reads cgroup limits for the current process.
/// <para />
/// Based on cgroup handling in CLR:
/// https://github.com/dotnet/runtime/blob/master/src/coreclr/src/gc/unix/cgroup.cpp
/// </summary>
internal static class CGroup
{
/// <summary>
/// Gets cgroup memory limit in bytes.
/// </summary>
public static readonly ulong? MemoryLimitInBytes = GetMemoryLimitInBytes();
/** */
private const string MemorySubsystem = "memory";
/** */
private const string MemoryLimitFileName = "memory.limit_in_bytes";
/** */
private const string ProcMountInfoFileName = "/proc/self/mountinfo";
/** */
private const string ProcCGroupFileName = "/proc/self/cgroup";
/// <summary>
/// Gets memory limit in bytes.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
private static ulong? GetMemoryLimitInBytes()
{
if (Os.IsWindows)
{
return null;
}
try
{
var memMount = FindHierarchyMount(MemorySubsystem);
if (memMount == null)
{
return null;
}
var cgroupPathRelativeToMount = FindCGroupPath(MemorySubsystem);
if (cgroupPathRelativeToMount == null)
{
return null;
}
var hierarchyMount = memMount.Value.Key;
var hierarchyRoot = memMount.Value.Value;
// Host CGroup: append the relative path
// In Docker: root and relative path are the same
var groupPath =
string.Equals(hierarchyRoot, cgroupPathRelativeToMount, StringComparison.Ordinal)
? hierarchyMount
: hierarchyMount + cgroupPathRelativeToMount;
var memLimitFilePath = Path.Combine(groupPath, MemoryLimitFileName);
var memLimitText = File.ReadAllText(memLimitFilePath);
ulong memLimit;
if (ulong.TryParse(memLimitText, out memLimit))
{
return memLimit;
}
}
catch (Exception e)
{
Console.Error.WriteLine("Failed to determine cgroup memory limit: " + e);
}
return null;
}
/// <summary>
/// Finds the hierarchy mount and root for the current process.
/// </summary>
private static KeyValuePair<string, string>? FindHierarchyMount(string subsystem)
{
foreach (var line in File.ReadAllLines(ProcMountInfoFileName))
{
var mount = GetHierarchyMount(line, subsystem);
if (mount != null)
{
return mount;
}
}
return null;
}
/// <summary>
/// Get the hierarchy mount and root.
/// </summary>
private static KeyValuePair<string, string>? GetHierarchyMount(string mountInfo, string subsystem)
{
// Example: 41 34 0:35 / /sys/fs/cgroup/memory rw,nosuid,nodev shared:17 - cgroup cgroup rw,memory
const string cGroup = " - cgroup ";
var cgroupIdx = mountInfo.IndexOf(cGroup, StringComparison.Ordinal);
if (cgroupIdx < 0)
return null;
var optionsIdx = mountInfo.LastIndexOf(" ", cgroupIdx + cGroup.Length, StringComparison.Ordinal);
if (optionsIdx < 0)
return null;
var memIdx = mountInfo.IndexOf(subsystem, optionsIdx + 1, StringComparison.Ordinal);
if (memIdx < 0)
return null;
var parts = mountInfo.Split(' ');
if (parts.Length < 5)
return null;
return new KeyValuePair<string, string>(parts[4], parts[3]);
}
/// <summary>
/// Finds the cgroup path for the current process.
/// </summary>
private static string FindCGroupPath(string subsystem)
{
var lines = File.ReadAllLines(ProcCGroupFileName);
foreach (var line in lines)
{
var parts = line.Split(new[] {':'}, 3);
if (parts.Length == 3 && parts[1].Split(',').Contains(subsystem, StringComparer.Ordinal))
{
return parts[2];
}
}
return null;
}
}
}