Lean  $LEAN_TAG$
OS.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using System.IO;
18 using System.Threading;
19 using System.Reflection;
20 using QuantConnect.Util;
21 using System.Diagnostics;
22 using System.Collections.Generic;
23 using static QuantConnect.StringExtensions;
24 
25 namespace QuantConnect
26 {
27  /// <summary>
28  /// Operating systems class for managing anything that is operation system specific.
29  /// </summary>
30  /// <remarks>Good design should remove the need for this function. Over time it should disappear.</remarks>
31  public static class OS
32  {
33  /// <summary>
34  /// CPU performance counter measures percentage of CPU used in a background thread.
35  /// </summary>
36  private static CpuPerformance CpuPerformanceCounter;
37 
38  /// <summary>
39  /// Global Flag :: Operating System
40  /// </summary>
41  public static bool IsLinux
42  {
43  get
44  {
45  var p = (int)Environment.OSVersion.Platform;
46  return (p == 4) || (p == 6) || (p == 128);
47  }
48  }
49 
50  /// <summary>
51  /// Global Flag :: Operating System
52  /// </summary>
53  public static bool IsWindows => !IsLinux;
54 
55  /// <summary>
56  /// Character Separating directories in this OS:
57  /// </summary>
58  public static string PathSeparation => Path.DirectorySeparatorChar.ToStringInvariant();
59 
60  /// <summary>
61  /// Get the drive space remaining on windows and linux in MB
62  /// </summary>
63  public static long DriveSpaceRemaining
64  {
65  get
66  {
67  var d = GetDrive();
68  return d.AvailableFreeSpace / (1024 * 1024);
69  }
70  }
71 
72  /// <summary>
73  /// Get the drive space remaining on windows and linux in MB
74  /// </summary>
75  public static long DriveSpaceUsed
76  {
77  get
78  {
79  var d = GetDrive();
80  return (d.TotalSize - d.AvailableFreeSpace) / (1024 * 1024);
81  }
82  }
83 
84  /// <summary>
85  /// Total space on the drive
86  /// </summary>
87  public static long DriveTotalSpace
88  {
89  get
90  {
91  var d = GetDrive();
92  return d.TotalSize / (1024 * 1024);
93  }
94  }
95 
96  /// <summary>
97  /// Get the drive.
98  /// </summary>
99  /// <returns></returns>
100  private static DriveInfo GetDrive()
101  {
102  var assembly = Assembly.GetExecutingAssembly();
103  var drive = Path.GetPathRoot(assembly.Location);
104  return new DriveInfo(drive);
105  }
106 
107  /// <summary>
108  /// Gets the amount of private memory allocated for the current process (includes both managed and unmanaged memory).
109  /// </summary>
110  public static long ApplicationMemoryUsed
111  {
112  get
113  {
114  var proc = Process.GetCurrentProcess();
115  return proc.PrivateMemorySize64 / (1024 * 1024);
116  }
117  }
118 
119  /// <summary>
120  /// Get the RAM used on the machine:
121  /// </summary>
122  public static long TotalPhysicalMemoryUsed => GC.GetTotalMemory(false) / (1024 * 1024);
123 
124  /// <summary>
125  /// Total CPU usage as a percentage
126  /// </summary>
127  public static decimal CpuUsage
128  {
129  get
130  {
131  if(CpuPerformanceCounter != null)
132  {
133  return (decimal)CpuPerformanceCounter.CpuPercentage;
134  }
135  return 0m;
136  }
137  }
138 
139  /// <summary>
140  /// Gets the statistics of the machine, including CPU% and RAM
141  /// </summary>
142  public static Dictionary<string, string> GetServerStatistics()
143  {
144  return new Dictionary<string, string>
145  {
146  { Messages.OS.CPUUsageKey, Invariant($"{CpuUsage:0.0}%")},
147  { Messages.OS.UsedRAMKey, TotalPhysicalMemoryUsed.ToStringInvariant() },
148  { Messages.OS.TotalRAMKey, "" },
149  { Messages.OS.HostnameKey, Environment.MachineName },
150  { Messages.OS.LEANVersionKey, $"v{Globals.Version}"}
151  };
152  }
153 
154  /// <summary>
155  /// Initializes the OS internal resources
156  /// </summary>
157  public static void Initialize()
158  {
159  CpuPerformanceCounter = new CpuPerformance();
160  }
161 
162  /// <summary>
163  /// Disposes of the OS internal resources
164  /// </summary>
165  public static void Dispose()
166  {
167  CpuPerformanceCounter.DisposeSafely();
168  }
169 
170  /// <summary>
171  /// Calculates the CPU usage in a background thread
172  /// </summary>
173  private class CpuPerformance : IDisposable
174  {
175  private readonly CancellationTokenSource _cancellationToken;
176  private readonly Thread _cpuThread;
177 
178  /// <summary>
179  /// CPU usage as a percentage (0-100)
180  /// </summary>
181  /// <remarks>Float to avoid any atomicity issues</remarks>
182  public float CpuPercentage { get; private set; }
183 
184  /// <summary>
185  /// Initializes an instance of the class and starts a new thread.
186  /// </summary>
187  public CpuPerformance()
188  {
189  _cancellationToken = new CancellationTokenSource();
190  _cpuThread = new Thread(CalculateCpu) { IsBackground = true, Name = "CpuPerformance" };
191  _cpuThread.Start();
192  }
193 
194  /// <summary>
195  /// Event loop that calculates the CPU percentage the process is using
196  /// </summary>
197  private void CalculateCpu()
198  {
199  var process = Process.GetCurrentProcess();
200  while (!_cancellationToken.IsCancellationRequested)
201  {
202  var startTime = DateTime.UtcNow;
203  var startCpuUsage = process.TotalProcessorTime;
204 
205  if (_cancellationToken.Token.WaitHandle.WaitOne(startTime.GetSecondUnevenWait(5000)))
206  {
207  return;
208  }
209 
210  var endTime = DateTime.UtcNow;
211  var endCpuUsage = process.TotalProcessorTime;
212 
213  var cpuUsedMs = (endCpuUsage - startCpuUsage).TotalMilliseconds;
214  var totalMsPassed = (endTime - startTime).TotalMilliseconds;
215  var cpuUsageTotal = cpuUsedMs / totalMsPassed;
216 
217  CpuPercentage = (float)cpuUsageTotal * 100;
218  }
219  }
220 
221  /// <summary>
222  /// Stops the execution of the task
223  /// </summary>
224  public void Dispose()
225  {
226  _cpuThread.StopSafely(TimeSpan.FromSeconds(5), _cancellationToken);
227  _cancellationToken.DisposeSafely();
228  }
229  }
230  }
231 }