[AOT] Refactor Logger function to improve performance and mark managedCommon as AOT compatible (#36327)

* Use function to init static value

* Replace GetFileName with GetFileNameWithoutExtension

* Add exception catch for GetCallerInfo

* Remove sourceLineNumber

* Add kernal to allow list

* Remove unused commit

* Add new folder to place source generation context

* update

* fix build issue

* Move line number back

* Use fileName to replace full path

---------

Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
This commit is contained in:
moooyo
2025-02-26 00:12:38 +08:00
committed by GitHub
parent 9a658eb884
commit c09a5337c4
7 changed files with 54 additions and 61 deletions

View File

@@ -6,6 +6,7 @@ using System;
using System.IO; using System.IO;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using ManagedCommon.Serialization;
namespace ManagedCommon namespace ManagedCommon
{ {
@@ -35,7 +36,7 @@ namespace ManagedCommon
inputStream.Close(); inputStream.Close();
reader.Dispose(); reader.Dispose();
return JsonSerializer.Deserialize<OutGoingLanguageSettings>(data).LanguageTag; return JsonSerializer.Deserialize<OutGoingLanguageSettings>(data, SourceGenerationContext.Default.OutGoingLanguageSettings).LanguageTag;
} }
catch (Exception) catch (Exception)
{ {

View File

@@ -15,15 +15,23 @@ namespace ManagedCommon
{ {
public static class Logger public static class Logger
{ {
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
private static readonly string Version = FileVersionInfo.GetVersionInfo(Assembly.Location).ProductVersion;
private static readonly string Error = "Error"; private static readonly string Error = "Error";
private static readonly string Warning = "Warning"; private static readonly string Warning = "Warning";
private static readonly string Info = "Info"; private static readonly string Info = "Info";
private static readonly string Debug = "Debug"; private static readonly string Debug = "Debug";
private static readonly string TraceFlag = "Trace"; private static readonly string TraceFlag = "Trace";
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
/*
* Please pay more attention!
* If you want to publish it with Native AOT enabled (or publish as a single file).
* You need to find another way to remove Assembly.Location usage.
*/
#pragma warning disable IL3000 // Avoid accessing Assembly file path when publishing as a single file
private static readonly string Version = FileVersionInfo.GetVersionInfo(Assembly.Location).ProductVersion;
#pragma warning restore IL3000 // Avoid accessing Assembly file path when publishing as a single file
/// <summary> /// <summary>
/// Initializes the logger and sets the path for logging. /// Initializes the logger and sets the path for logging.
/// </summary> /// </summary>
@@ -53,18 +61,16 @@ namespace ManagedCommon
Trace.AutoFlush = true; Trace.AutoFlush = true;
} }
[MethodImpl(MethodImplOptions.NoInlining)] public static void LogError(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
public static void LogError(string message)
{ {
Log(message, Error); Log(message, Error, memberName, sourceFilePath, sourceLineNumber);
} }
[MethodImpl(MethodImplOptions.NoInlining)] public static void LogError(string message, Exception ex, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
public static void LogError(string message, Exception ex)
{ {
if (ex == null) if (ex == null)
{ {
Log(message, Error); Log(message, Error, memberName, sourceFilePath, sourceLineNumber);
} }
else else
{ {
@@ -83,38 +89,33 @@ namespace ManagedCommon
"Stack trace: " + Environment.NewLine + "Stack trace: " + Environment.NewLine +
ex.StackTrace; ex.StackTrace;
Log(exMessage, Error); Log(exMessage, Error, memberName, sourceFilePath, sourceLineNumber);
} }
} }
[MethodImpl(MethodImplOptions.NoInlining)] public static void LogWarning(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
public static void LogWarning(string message)
{ {
Log(message, Warning); Log(message, Warning, memberName, sourceFilePath, sourceLineNumber);
} }
[MethodImpl(MethodImplOptions.NoInlining)] public static void LogInfo(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
public static void LogInfo(string message)
{ {
Log(message, Info); Log(message, Info, memberName, sourceFilePath, sourceLineNumber);
} }
[MethodImpl(MethodImplOptions.NoInlining)] public static void LogDebug(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
public static void LogDebug(string message)
{ {
Log(message, Debug); Log(message, Debug, memberName, sourceFilePath, sourceLineNumber);
} }
[MethodImpl(MethodImplOptions.NoInlining)] public static void LogTrace([System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
public static void LogTrace()
{ {
Log(string.Empty, TraceFlag); Log(string.Empty, TraceFlag, memberName, sourceFilePath, sourceLineNumber);
} }
[MethodImpl(MethodImplOptions.NoInlining)] private static void Log(string message, string type, string memberName, string sourceFilePath, int sourceLineNumber)
private static void Log(string message, string type)
{ {
Trace.WriteLine("[" + DateTime.Now.TimeOfDay + "] [" + type + "] " + GetCallerInfo()); Trace.WriteLine("[" + DateTime.Now.TimeOfDay + "] [" + type + "] " + GetCallerInfo(memberName, sourceFilePath, sourceLineNumber));
Trace.Indent(); Trace.Indent();
if (message != string.Empty) if (message != string.Empty)
{ {
@@ -124,49 +125,27 @@ namespace ManagedCommon
Trace.Unindent(); Trace.Unindent();
} }
[MethodImpl(MethodImplOptions.NoInlining)] private static string GetCallerInfo(string memberName, string sourceFilePath, int sourceLineNumber)
private static string GetCallerInfo()
{ {
StackTrace stackTrace = new(); string callerFileName = "Unknown";
var callerMethod = GetCallerMethod(stackTrace);
return $"{callerMethod?.DeclaringType?.Name}::{callerMethod.Name}";
}
private static MethodBase GetCallerMethod(StackTrace stackTrace)
{
const int topFrame = 3;
var topMethod = stackTrace.GetFrame(topFrame)?.GetMethod();
try try
{ {
if (topMethod?.Name == nameof(IAsyncStateMachine.MoveNext) && typeof(IAsyncStateMachine).IsAssignableFrom(topMethod?.DeclaringType)) string fileName = Path.GetFileName(sourceFilePath);
if (!string.IsNullOrEmpty(fileName))
{ {
// Async method; return actual method as determined by heuristic: callerFileName = fileName;
// "Nearest method on stack to async state-machine's MoveNext() in same namespace but in a different type".
// There are tighter ways of determining the actual method, but this is good enough and probably faster.
for (int deepFrame = topFrame + 1; deepFrame < stackTrace.FrameCount; deepFrame++)
{
var deepMethod = stackTrace.GetFrame(deepFrame)?.GetMethod();
if (deepMethod?.DeclaringType != topMethod?.DeclaringType && deepMethod?.DeclaringType?.Namespace == topMethod?.DeclaringType?.Namespace)
{
return deepMethod;
}
}
} }
} }
catch (Exception) catch (Exception)
{ {
// Ignore exceptions in Release. The code above won't throw, but if it does, we don't want to crash the app. callerFileName = "Unknown";
#if DEBUG #if DEBUG
throw; throw;
#endif #endif
} }
return topMethod; return $"{callerFileName}::{memberName}::{sourceLineNumber}";
} }
} }
} }

View File

@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well --> <!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\Common.Dotnet.CsWinRT.props" /> <Import Project="..\..\Common.Dotnet.CsWinRT.props" />
<Import Project="..\..\Common.Dotnet.AotCompatibility.props" />
<PropertyGroup> <PropertyGroup>
<Description>PowerToys ManagedCommon</Description> <Description>PowerToys ManagedCommon</Description>

View File

@@ -53,7 +53,7 @@ namespace ManagedCommon
internal static int Size internal static int Size
{ {
get { return Marshal.SizeOf(typeof(INPUT)); } get { return Marshal.SizeOf<INPUT>(); }
} }
} }

View File

@@ -14,12 +14,11 @@ namespace ManagedCommon
{ {
public static class RunnerHelper public static class RunnerHelper
{ {
public static void WaitForPowerToysRunner(int powerToysPID, Action act) public static void WaitForPowerToysRunner(int powerToysPID, Action act, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "")
{ {
var stackTrace = new StackTrace(); var stackTrace = new StackTrace();
var assembly = Assembly.GetCallingAssembly().GetName(); var assembly = Assembly.GetCallingAssembly().GetName();
var callingMethod = stackTrace.GetFrame(1).GetMethod().Name; PowerToysTelemetry.Log.WriteEvent(new DebugEvent() { Message = $"[{assembly}][{memberName}]WaitForPowerToysRunner waiting for Event powerToysPID={powerToysPID}" });
PowerToysTelemetry.Log.WriteEvent(new DebugEvent() { Message = $"[{assembly}][{callingMethod}]WaitForPowerToysRunner waiting for Event powerToysPID={powerToysPID}" });
Task.Run(() => Task.Run(() =>
{ {
const uint INFINITE = 0xFFFFFFFF; const uint INFINITE = 0xFFFFFFFF;
@@ -29,7 +28,7 @@ namespace ManagedCommon
IntPtr powerToysProcHandle = NativeMethods.OpenProcess(SYNCHRONIZE, false, powerToysPID); IntPtr powerToysProcHandle = NativeMethods.OpenProcess(SYNCHRONIZE, false, powerToysPID);
if (NativeMethods.WaitForSingleObject(powerToysProcHandle, INFINITE) == WAIT_OBJECT_0) if (NativeMethods.WaitForSingleObject(powerToysProcHandle, INFINITE) == WAIT_OBJECT_0)
{ {
PowerToysTelemetry.Log.WriteEvent(new DebugEvent() { Message = $"[{assembly}][{callingMethod}]WaitForPowerToysRunner Event Notified powerToysPID={powerToysPID}" }); PowerToysTelemetry.Log.WriteEvent(new DebugEvent() { Message = $"[{assembly}][{memberName}]WaitForPowerToysRunner Event Notified powerToysPID={powerToysPID}" });
act.Invoke(); act.Invoke();
} }
}); });

View File

@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
using static ManagedCommon.LanguageHelper;
namespace ManagedCommon.Serialization;
[JsonSerializable(typeof(OutGoingLanguageSettings))]
internal sealed partial class SourceGenerationContext : JsonSerializerContext
{
}

View File

@@ -14,7 +14,7 @@ namespace ManagedCommon
/// <param name="sender">Sender ThemeListener</param> /// <param name="sender">Sender ThemeListener</param>
public delegate void ThemeChangedEvent(ThemeListener sender); public delegate void ThemeChangedEvent(ThemeListener sender);
public class ThemeListener : IDisposable public partial class ThemeListener : IDisposable
{ {
/// <summary> /// <summary>
/// Gets the App Theme. /// Gets the App Theme.