From 7609191b46d11ddfc8cb165d24bfc34d3b388e3c Mon Sep 17 00:00:00 2001 From: Gustavo Brown Rodriguez Date: Tue, 25 Feb 2025 13:08:57 -0300 Subject: [PATCH 1/2] Fix in cleanup code (exitApplication) There are some scenarios where a single process executes GeneXus objects which end up with IsMain being true For example, some GXTest/GXFlow code instantiates procedures without passing them a GxContext. This was making the coverage trace file to try a premature cleanup and the need to start another coverage session --- dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs b/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs index 088c2f917..c96629f86 100644 --- a/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs +++ b/dotnet/src/dotnetframework/GxClasses/Model/gxproc.cs @@ -109,8 +109,12 @@ protected void exitApplication() } private void exitApplication(bool flushBatchCursor) { + bool inMainExit = false; if (!(GxContext.IsHttpContext || GxContext.IsRestService) && IsMain && GxApplication.MainContext==context) + { ThreadUtil.WaitForEnd(); + inMainExit = true; + } if (flushBatchCursor) { @@ -118,8 +122,9 @@ private void exitApplication(bool flushBatchCursor) ds.Connection.FlushBatchCursors(this); } - if (IsMain) + if (inMainExit) dbgInfo?.OnExit(); + if (disconnectUserAtCleanup) { try From eb624ae58113352934d6eff54f2875327e524544 Mon Sep 17 00:00:00 2001 From: Gustavo Brown Rodriguez Date: Tue, 25 Feb 2025 13:13:35 -0300 Subject: [PATCH 2/2] Various fixes - add a specific Id for .NET code coverage sessions (Id=3) - Add a public static OnExit entry to allow for third party code which instantiates objects that end up being marked as Main in the same process to do a proper cleanup before exiting the last object. - Update OnExit/1 to be more resilient by ensuring every DbgItem with ticks not set to have a valid tick count - Add (conditionally compiled) code to generate a verbose log file of the trace being written. To enable this trace you have to build GxClasses.dll with _LOG_WRITER symbol defined Issue: 202777 --- .../GxClasses/Diagnostics/GXDebugManager.cs | 136 +++++++++++++++++- 1 file changed, 130 insertions(+), 6 deletions(-) diff --git a/dotnet/src/dotnetframework/GxClasses/Diagnostics/GXDebugManager.cs b/dotnet/src/dotnetframework/GxClasses/Diagnostics/GXDebugManager.cs index dd51e8e90..81387276e 100644 --- a/dotnet/src/dotnetframework/GxClasses/Diagnostics/GXDebugManager.cs +++ b/dotnet/src/dotnetframework/GxClasses/Diagnostics/GXDebugManager.cs @@ -1,18 +1,26 @@ +//#define _DEBUG_DEBUGGER +//#define _LOG_WRITER using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Text; using System.Threading; using GeneXus.Application; +using static GeneXus.Diagnostics.GXDebugStream; namespace GeneXus.Diagnostics { - public class GXDebugManager { + internal const string _LOG_WRITER = "_LOG_WRITER"; public const short GXDEBUG_VERSION = 2; - internal const GXDebugGenId GENERATOR_ID = GXDebugGenId.CSHARP; +#if NETCORE + internal const GXDebugGenId GENERATOR_ID = GXDebugGenId.NET; +#else + internal const GXDebugGenId GENERATOR_ID = GXDebugGenId.NETFRAMEWORK; +#endif internal const int PGM_INFO_NO_PARENT = 0; internal static double MICRO_FREQ = Stopwatch.Frequency / 10e6D; @@ -220,9 +228,25 @@ private void Save(GXDebugItem[] ToSave, int saveTop = -1, bool saveInThread = tr else mSave(new object[] { ToSave, saveTop, saveCount }); } + public static void OnExit() + { + if(initialized) + { + Instance.OnExit(null); + } + } + internal void OnExit(GXDebugInfo dbgInfo) { PushSystem((int)GXDebugMsgCode.EXIT); + lock (saveLock) + { + for (int i = 0; i < dbgIndex; i++) + { + if (Current[i].Ticks == TICKS_NOT_SET) + Current[i].Ticks = TICKS_NOT_NEEDED; + } + } Save(); } @@ -292,12 +316,14 @@ private void mSave(object state1) #if _DEBUG_DEBUGGER Console.WriteLine("mSave-" + saveTop); #endif + LogWriter.Append($"\n SaveTop = {saveTop}\n"); for (; idx < saveTop; idx++) { GXDebugItem dbgItem = Data[idx]; #if _DEBUG_DEBUGGER Console.WriteLine($"item({idx}): { dbgItem }"); #endif + LogWriter.Log($"<{idx}:{dbgItem}>"); switch (dbgItem.MsgType) { case GXDebugMsgType.SYSTEM: @@ -357,8 +383,11 @@ private void mSave(object state1) continue; } ClearDebugItem(dbgItem); + LogWriter.LogPosition(stream); } + LogWriter.Append($"\n ENDSAVE "); } + LogWriter.Flush(); } catch (Exception ex) { @@ -421,8 +450,9 @@ internal enum GXDebugMsgCode : byte internal enum GXDebugGenId : byte { - CSHARP = 1, + NETFRAMEWORK = 1, JAVA = 2, + NET = 3, INVALID = 0xF } @@ -538,6 +568,7 @@ public enum ESCAPE : byte public GXDebugStream(string FileName, FileMode fileMode) : base(FileName, fileMode, fileMode == FileMode.Open ? FileAccess.Read : (fileMode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.None) { + LogWriter.SetLogName($"{FileName}.log"); Last = 0; LastLast = 0; InitializeNewBlock(); @@ -567,8 +598,16 @@ private void WriteEpilog() } public void Write(byte[] data) => Write(data, 0, data.Length); - public void WriteRaw(byte[] data, int from, int length) => base.Write(data, from, length); - public void WriteRaw(byte value) => base.WriteByte(value); + public void WriteRaw(byte[] data, int from, int length) + { + LogWriter.LogWriteRaw(data, from, length); + base.Write(data, from, length); + } + public void WriteRaw(byte value) + { + LogWriter.LogWriteByte(value); + base.WriteByte(value); + } public override void Write(byte[] data, int offset, int count) { @@ -582,11 +621,13 @@ public override void Write(byte[] data, int offset, int count) private byte Last, LastLast; public override void WriteByte(byte value) { + LogWriter.LogWriteByte(value); base.WriteByte(value); if (value == 0xFF && value == Last && value == LastLast) { + LogWriter.Append("<3xFF> "); WriteRaw(ESCAPE.TRIPLE_FF.ToByte()); Last = LastLast = 0; } @@ -602,6 +643,7 @@ public void WriteVLUInt(int value) if (value < 0) throw new ArgumentException("Cannot handle negative values"); else if (value > 0x1FFFFFFFL) throw new ArgumentException("Cannot handle > 29bit values"); + LogWriter.LogWrite("VLUI", value); if (value < 0x80) WriteByte((byte)value); else if (value < 0x4000) @@ -616,6 +658,7 @@ public void WriteVLUInt(int value) public void WriteVLUShort(short value) { if (value < 0) throw new ArgumentException("Cannot handle negative values"); + LogWriter.LogWrite("VLUS", value); if (value < 0x80) WriteByte((byte)value); else @@ -627,6 +670,9 @@ public void WriteVLUShort(short value) public void WriteHeader(Guid sessionGuid, short version, int saveCount) { + LogWriter.Log("Header", ( "Guid", sessionGuid), ( "Version", version), ("SaveCount", saveCount)); + LogWriter.LogPosition(this); + WriteProlog(version); WriteVLUInt(saveCount); Write(sessionGuid.ToByteArray()); @@ -689,6 +735,8 @@ internal void WritePgmTrace(int SId, int line1, int col, long ticks) public void WriteScaledLong(long N) { if (N < 0) throw new ArgumentException("Cannot handle negative values"); + LogWriter.LogWrite("SCAL", N); + int m = 0; while (N > 31) { @@ -874,6 +922,82 @@ public void InitializeNewBlock() LastSId = 0; LastLine1 = 0; } + + internal class LogWriter + { + private static string LogWriterName = "GXDebugCoverage.log"; + public static object LogWriterLock = new object(); + private static StringBuilder m_Buffer = new StringBuilder(); + + [Conditional(GXDebugManager._LOG_WRITER)] + public static void SetLogName(string name) + { + LogWriterName = name; + } + + [Conditional(GXDebugManager._LOG_WRITER)] + public static void LogPosition(GXDebugStream stream) + { + Append($"\nPos: {stream.Position}\n"); + } + + [Conditional(GXDebugManager._LOG_WRITER)] + public static void Append(string msg) + { + lock (LogWriterLock) + { + m_Buffer.Append(msg); + } + } + + [Conditional(GXDebugManager._LOG_WRITER)] + public static void Flush() + { + try + { + lock (LogWriterLock) + { + File.AppendAllText(LogWriterName, m_Buffer.ToString()); + } + } + catch (Exception e) + { + Console.WriteLine(e.StackTrace); + } + m_Buffer.Clear(); + } + + [Conditional(GXDebugManager._LOG_WRITER)] + internal static void Log(string title, params (string key, object value)[] values) + { + StringBuilder sb = new StringBuilder(); + Append($"=========================================\n{title}\n"); + if (values != null) + { + foreach ((string key, object value) in values) + { + sb.Append($" {key} = {value?.ToString()}\n"); + } + } + Append(sb.ToString()); + } + + [Conditional(GXDebugManager._LOG_WRITER)] + internal static void LogWrite(string type, long value) => Append($"{type}:{value} "); + + [Conditional(GXDebugManager._LOG_WRITER)] + internal static void LogWriteByte(byte value) => Append($"({value.ToString("X2")}) "); + + [Conditional(GXDebugManager._LOG_WRITER)] + internal static void LogWriteRaw(byte[] data, int from, int length) + { + lock (LogWriterLock) + { + for (int i = 0; i < length; i++) + m_Buffer.Append($"({data[from+i].ToString("X2")}) "); + } + } + } } #if _DEBUG_DEBUGGER @@ -897,7 +1021,7 @@ public GXDebugReader(string FileName) this.FileName = FileName; } - public static int Main(String[] args) + public static int Main(string[] args) { try {