Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/Task.Monitor.Cli.Utils/PInvokeErrorHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Task.Monitor.Cli.Utils;

public static class PInvokeErrorHelpers
{
public static void AssertOnLastError(string message)
{
#if DEBUG
int error = Marshal.GetLastPInvokeError();
Debug.Assert(error == 0, GetFormattedErrorMessage(message));
#endif
}

public static void TraceOnLastError(string message) =>
Trace.WriteLine(GetFormattedErrorMessage(message));

public static void TraceOnceOnLastError(string key) =>
TraceEx.WriteLineOnce(key, GetFormattedErrorMessage());

public static void TraceOnceOnLastError(string key, string message) =>
TraceEx.WriteLineOnce(key, GetFormattedErrorMessage(message));

public static void TraceOnPInvokeError(string message, uint error) =>
Trace.WriteLine($"PINVOKE ERROR ({error}): {message}");

public static void TraceOnceOnPInvokeError(string key, string message, uint error) =>
TraceEx.WriteLineOnce(key, $"PINVOKE ERROR ({error}): {message}");

private static string GetFormattedErrorMessage() => GetFormattedErrorMessage(string.Empty);

private static string GetFormattedErrorMessage(string message)
{
int error = Marshal.GetLastPInvokeError();
string errorMessage = $"PINVOKE ERROR ({error}): {Marshal.GetPInvokeErrorMessage(error)}";

if (string.IsNullOrEmpty(message)) {
errorMessage += $" {message}";
}

return errorMessage;
}
}
29 changes: 29 additions & 0 deletions src/Task.Monitor.Cli.Utils/TraceEx.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Collections.Concurrent;
using System.Diagnostics;

namespace Task.Monitor.Cli.Utils;

public static class TraceEx
{
private static readonly ConcurrentDictionary<string, DateTime> lastSeen = new();
private static TimeSpan suppressionWindow = TimeSpan.FromMinutes(10);

public static void WriteLineOnce(string key, string message)
{
DateTime now = DateTime.UtcNow;
lastSeen.AddOrUpdate(
key,
addValueFactory: k => {
Trace.WriteLine($"Key: {key}: {message}");
return now;
},
updateValueFactory: (k, lastLoggedTime) => {
if (now - lastLoggedTime >= suppressionWindow) {
Trace.WriteLine($"Key: {key}: {message}");
return now;
}
return lastLoggedTime;
}
);
}
}
22 changes: 0 additions & 22 deletions src/Task.Monitor.System/PInvokeErrorHelpers.cs

This file was deleted.

58 changes: 48 additions & 10 deletions src/Task.Monitor.System/Process/GpuService.Windows.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using Task.Monitor.Cli.Utils;
using Task.Monitor.Interop.Win32;

namespace Task.Monitor.System.Process;
Expand All @@ -16,29 +17,60 @@ public static partial class GpuService

private static Dictionary<int, long> GetProcessStatsInternal()
{
if (Pdh.PdhOpenQuery(null, IntPtr.Zero, out IntPtr hQuery) != Pdh.ERROR_SUCCESS) {
Trace.WriteLine($"Failed PdhOpenQuery in {nameof(GpuService)}.");
uint pdhResult = 0;

if ((pdhResult = Pdh.PdhOpenQuery(
null,
IntPtr.Zero,
out IntPtr hQuery)) != Pdh.ERROR_SUCCESS) {

PInvokeErrorHelpers.TraceOnceOnPInvokeError(
nameof(Pdh.PdhOpenQuery),
$"Failed {nameof(GetProcessStatsInternal)}",
pdhResult);

return new Dictionary<int, long>(cumulativeEngineValues);
}

Pdh.PdhAddEnglishCounter(
hQuery,
GpuEngineCounterPath,
IntPtr.Zero,
out IntPtr hCounter);

Pdh.PdhCollectQueryData(hQuery);
if ((pdhResult = Pdh.PdhAddEnglishCounter(
hQuery,
GpuEngineCounterPath,
IntPtr.Zero,
out IntPtr hCounter)) != Pdh.ERROR_SUCCESS) {

PInvokeErrorHelpers.TraceOnceOnPInvokeError(
GpuEngineCounterPath,
$"Failed {nameof(Pdh.PdhAddEnglishCounter)}",
pdhResult);

Pdh.PdhCloseQuery(hQuery);
return new Dictionary<int, long>(cumulativeEngineValues);
}

if ((pdhResult = Pdh.PdhCollectQueryData(hQuery)) != Pdh.ERROR_SUCCESS) {
PInvokeErrorHelpers.TraceOnceOnPInvokeError(
nameof(Pdh.PdhCollectQueryData),
$"Failed {nameof(GetProcessStatsInternal)}",
pdhResult);

Pdh.PdhCloseQuery(hQuery);
return new Dictionary<int, long>(cumulativeEngineValues);
}

uint bufferSize = 0;
uint itemCount = 0;

Pdh.PdhGetRawCounterArray(
_ = Pdh.PdhGetRawCounterArray(
hCounter,
ref bufferSize,
ref itemCount,
IntPtr.Zero);

if (bufferSize <= 0) {
TraceEx.WriteLineOnce(
$"{nameof(Pdh.PdhGetRawCounterArray)} {hCounter}",
$"{nameof(Pdh.PdhGetRawCounterArray)} failed to calculate {nameof(bufferSize)} ");

Pdh.PdhCloseQuery(hQuery);
return new Dictionary<int, long>(cumulativeEngineValues);
}
Expand Down Expand Up @@ -88,6 +120,12 @@ private static Dictionary<int, long> GetProcessStatsInternal()
cumulativeEngineValues[pid] = cumulativeEngineValues.GetValueOrDefault(pid, 0) + maxDelta;
}
}
else {
PInvokeErrorHelpers.TraceOnceOnPInvokeError(
$"{nameof(Pdh.PdhGetRawCounterArray)} {hCounter}",
$"{nameof(Pdh.PdhGetRawCounterArray)} failed to allocate {nameof(buffer)}",
pdhResult);
}

Marshal.FreeHGlobal(buffer);
Pdh.PdhCloseQuery(hQuery);
Expand Down
1 change: 0 additions & 1 deletion src/Task.Monitor.System/Process/ModuleService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ public virtual List<ModuleInfo> GetModules(int pid)
{
if (!ProcessUtils.TryGetProcessByPid(pid, out SysDiag::Process? process) ||
process == null) {
SysDiag::Trace.WriteLine($"Failed TryGetProcessByPid for Pid {pid} in {nameof(ModuleService)}.");
return [];
}

Expand Down
30 changes: 23 additions & 7 deletions src/Task.Monitor.System/Process/ProcessService.Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ public sealed partial class ProcessService
entry.th32ProcessID);

if (hProcess == IntPtr.Zero) {
PInvokeErrorHelpers.TraceOnceOnLastError(
$"{nameof(Kernel32.OpenProcess)}_{entry.th32ProcessID}",
$"Failed to open process for pid {entry.th32ProcessID}");

return null;
}

Expand Down Expand Up @@ -103,6 +107,7 @@ private static string GetProcessCommandLine(int pid, in string defaultValue)
IntPtr hSnapshot = Kernel32.CreateToolhelp32Snapshot(Kernel32.TH32CS_SNAPPROCESS, 0);

if (hSnapshot == IntPtr.Zero) {
PInvokeErrorHelpers.TraceOnceOnLastError(nameof(Kernel32.CreateToolhelp32Snapshot));
return null;
}

Expand All @@ -111,6 +116,7 @@ private static string GetProcessCommandLine(int pid, in string defaultValue)
};

if (!Kernel32.Process32FirstW(hSnapshot, ref entry)) {
PInvokeErrorHelpers.TraceOnceOnLastError(nameof(Kernel32.Process32FirstW));
return null;
}

Expand Down Expand Up @@ -141,6 +147,7 @@ private IEnumerable<ProcessInfo> GetProcessInfosInternal()
IntPtr hSnapshot = Kernel32.CreateToolhelp32Snapshot(Kernel32.TH32CS_SNAPPROCESS, 0);

if (hSnapshot == IntPtr.Zero) {
PInvokeErrorHelpers.TraceOnLastError(nameof(Kernel32.CreateToolhelp32Snapshot));
yield break;
}

Expand All @@ -149,6 +156,7 @@ private IEnumerable<ProcessInfo> GetProcessInfosInternal()
};

if (!Kernel32.Process32FirstW(hSnapshot, ref entry)) {
PInvokeErrorHelpers.TraceOnceOnLastError(nameof(Kernel32.Process32FirstW));
yield break;
}

Expand Down Expand Up @@ -176,7 +184,7 @@ private static void GetProcessIoOperations(
writeBytes = 0;

if (!WinNt.GetProcessIoCounters(hProcess, out WinNt.IO_COUNTERS counters)) {
PInvokeErrorHelpers.AssertOnLastError(nameof(WinNt.GetProcessIoCounters));
PInvokeErrorHelpers.TraceOnceOnLastError(nameof(WinNt.GetProcessIoCounters));
return;
}

Expand All @@ -186,14 +194,14 @@ private static void GetProcessIoOperations(

private void GetProcessMemCounters(IntPtr hProcess, ref PsApi.PROCESS_MEMORY_COUNTERS counters)
{
counters.cb = (uint)Marshal.SizeOf<PsApi.PROCESS_MEMORY_COUNTERS>();
counters.cb = (uint)Marshal.SizeOf<PsApi.PROCESS_MEMORY_COUNTERS>();

if (!PsApi.GetProcessMemoryInfo(
hProcess,
ref counters,
counters.cb)) {

PInvokeErrorHelpers.AssertOnLastError(nameof(PsApi.GetProcessMemoryInfo));
PInvokeErrorHelpers.TraceOnceOnLastError(nameof(PsApi.GetProcessMemoryInfo));
}
}

Expand All @@ -211,6 +219,7 @@ private void GetProcessorTimes(
out MinWinBase.FILETIME kernel,
out MinWinBase.FILETIME user)) {

PInvokeErrorHelpers.TraceOnceOnLastError(nameof(Kernel32.GetProcessTimes));
return;
}

Expand All @@ -229,6 +238,7 @@ private string GetProcessPath(IntPtr hProcess, uint flags = Kernel32.PROCESS_NAM
sb,
ref size)) {

PInvokeErrorHelpers.TraceOnceOnLastError(nameof(Kernel32.QueryFullProcessImageNameW));
return string.Empty;
}

Expand Down Expand Up @@ -287,14 +297,17 @@ private static string GetProcessUserName(SafeProcessHandle processHandle)

private static SecurityIdentifier? GetProcessSecurityIdentifier(SafeProcessHandle processHandle)
{
if (ProcessThreadsApi.OpenProcessToken(
if (!ProcessThreadsApi.OpenProcessToken(
processHandle,
0x8u,
out SafeProcessHandle tokenHandle)) {

if (GetProcessTokenSid(tokenHandle, out SecurityIdentifier sid)) {
return sid;
}
PInvokeErrorHelpers.TraceOnceOnLastError(nameof(ProcessThreadsApi.OpenProcessToken));
return null;
}

if (GetProcessTokenSid(tokenHandle, out SecurityIdentifier sid)) {
return sid;
}

return null;
Expand Down Expand Up @@ -328,6 +341,9 @@ private static unsafe bool GetProcessTokenSid(SafeProcessHandle processHandle, o
sid = new SecurityIdentifier(new IntPtr(sidPtr));
}
}
else {
PInvokeErrorHelpers.TraceOnceOnLastError(nameof(GetProcessTokenSid));
}

return result;
}
Expand Down
29 changes: 22 additions & 7 deletions src/Task.Monitor.System/Process/ServiceUtils.Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Runtime.InteropServices;
using System.ServiceProcess;
using Microsoft.Win32;
using Task.Monitor.Cli.Utils;
using Task.Monitor.Interop.Win32;

namespace Task.Monitor.System.Process;
Expand All @@ -22,7 +23,7 @@ private static void BuildServiceMap()
IntPtr hSCM = WinService.OpenSCManager(null!, null!, WinService.SC_MANAGER_CONNECT);

if (hSCM == IntPtr.Zero) {
Trace.WriteLine($"Failed to open SCM Manager: {PInvokeErrorHelpers.GetFormattedErrorMesage()}");
PInvokeErrorHelpers.TraceOnceOnLastError(nameof(WinService.SC_MANAGER_CONNECT));
return;
}

Expand All @@ -37,6 +38,7 @@ private static void BuildServiceMap()
WinService.SERVICE_QUERY_STATUS);

if (hService == IntPtr.Zero) {
PInvokeErrorHelpers.TraceOnceOnLastError($"{nameof(WinService.OpenService)} {service.ServiceName}");
continue;
}

Expand Down Expand Up @@ -70,8 +72,13 @@ public static bool GetService(int pid, out ServiceInfo? service)
public static string? GetServiceImagePath(string serviceName)
{
const string RegPath = @"SYSTEM\CurrentControlSet\Services\";

using RegistryKey? key = Registry.LocalMachine.OpenSubKey(RegPath + serviceName);
string subKey = $"{RegPath}{serviceName}";
using RegistryKey? key = Registry.LocalMachine.OpenSubKey(subKey);

if (key == null) {
TraceEx.WriteLineOnce(subKey, "Failed to open registry key");
}

return key?.GetValue("ImagePath")?.ToString() ?? null;
}

Expand All @@ -87,7 +94,7 @@ private static int GetServiceProcessId(IntPtr hService)
(uint)Marshal.SizeOf<WinService.SERVICE_STATUS_PROCESS>(),
out _)) {

Trace.WriteLine($"GetServiceProcessId QueryServiceStatusEx: {PInvokeErrorHelpers.GetFormattedErrorMesage()}");
PInvokeErrorHelpers.TraceOnceOnLastError($"{nameof(WinService.QueryServiceStatusEx)} {hService}");
return 0;
}

Expand All @@ -112,7 +119,10 @@ internal static ServiceInfo[] GetServices()
IntPtr hSCM = WinService.OpenSCManager(null!, null!, WinService.SC_MANAGER_ENUMERATE_SERVICE);

if (hSCM == IntPtr.Zero) {
Trace.WriteLine($"Failed to open SCM Manager: {PInvokeErrorHelpers.GetFormattedErrorMesage()}");
PInvokeErrorHelpers.TraceOnceOnLastError(
nameof(WinService.OpenSCManager),
$"Failed GetServices {nameof(WinService.SC_MANAGER_ENUMERATE_SERVICE)}");

return services;
}

Expand All @@ -135,7 +145,10 @@ internal static ServiceInfo[] GetServices()
null!);

if (bytesNeeded == 0) {
Trace.WriteLine($"EnumServicesStatusEx bytesNeeded returned 0: {PInvokeErrorHelpers.GetFormattedErrorMesage()}");
PInvokeErrorHelpers.TraceOnceOnLastError(
nameof(WinService.EnumServicesStatusEx),
$"{nameof(WinService.EnumServicesStatusEx)} {nameof(bytesNeeded)} returned 0");

WinService.CloseServiceHandle(hSCM);
return services;
}
Expand All @@ -156,7 +169,9 @@ internal static ServiceInfo[] GetServices()
null!);

if (!result) {
Trace.WriteLine($"Failed to EnumServicesStatusEx: {PInvokeErrorHelpers.GetFormattedErrorMesage()}");
PInvokeErrorHelpers.TraceOnceOnLastError(
nameof(WinService.EnumServicesStatusEx),
$"{nameof(WinService.EnumServicesStatusEx)} failed to allocate {nameof(buffer)}");

if (buffer != IntPtr.Zero) {
Marshal.FreeHGlobal(buffer);
Expand Down
Loading
Loading