Pinta icon indicating copy to clipboard operation
Pinta copied to clipboard

Windows only - Random crash, for version 2.2

Open iksi4prs opened this issue 1 year ago • 71 comments

Description App crashes. See more information about optional cause in discussion/comment: https://github.com/PintaProject/Pinta/discussions/654#discussioncomment-8014885

To Reproduce Random. I've noticed that after first install, it works fine for few seconds. But trying to run app again fails, namely crash is even before first window is created (or at least before it becomes visible)

Additional Info Logs from "Windows's events viewer"

Application: Pinta.exe CoreCLR Version: 8.0.23.53103 .NET Version: 8.0.0 Description: The process was terminated due to an unhandled exception. Exception Info: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. Stack: at GObject.Internal.Object.RemoveToggleRef(IntPtr, GObject.Internal.ToggleNotify, IntPtr) at GObject.Internal.Object.RemoveToggleRef(IntPtr, GObject.Internal.ToggleNotify, IntPtr) at GObject.Internal.ObjectMapper.Unmap(IntPtr) at GObject.Internal.ObjectHandle.ReleaseHandle() at System.Runtime.InteropServices.SafeHandle.InternalRelease(Boolean) at System.Runtime.InteropServices.SafeHandle.Finalize()

Faulting application name: Pinta.exe, version: 2.2.0.0, time stamp: 0x65410000 Faulting module name: coreclr.dll, version: 8.0.23.53103, time stamp: 0x65411660 Exception code: 0xc0000005 Fault offset: 0x00000000001e337a Faulting process id: 0x0xBC80 Faulting application start time: 0x0x1DA3F1C8D463DE9 Faulting application path: C:\Program Files\Pinta\bin\Pinta.exe Faulting module path: C:\Program Files\Pinta\bin\coreclr.dll Report Id: fa0399c5-aff9-4e9b-b43c-0b1429429ed9 Faulting package full name: Faulting package-relative application ID:

Faulting application name: Pinta.exe, version: 2.1.1.0, time stamp: 0x63c9df9f Faulting module name: KERNELBASE.dll, version: 10.0.22621.2792, time stamp: 0x3091b6fb Exception code: 0x80000003 Fault offset: 0x000000000010cd82 Faulting process id: 0x0x6A54 Faulting application start time: 0x0x1DA3F1CC7555BDF Faulting application path: C:\Program Files\Pinta\Pinta.exe Faulting module path: C:\WINDOWS\System32\KERNELBASE.dll Report Id: 8d3cb891-26e0-42d4-943e-630d861f029b Faulting package full name: Faulting package-relative application ID:

Version Operating System: Windows 11 Pro Version 22H2 OS build 22621.2861

Pinta: Pinta 2.2, from this branch: (not all dev branch have this problem) https://github.com/PintaProject/Pinta/actions/runs/7405489857

iksi4prs avatar Jan 09 '24 18:01 iksi4prs

For Pinta 2.2, from this branch: (not all dev branch have this problem) does that mean that you haven't hit the crashes with the regular build from the master branch?

cameronwhite avatar Jan 10 '24 03:01 cameronwhite

@cameronwhite, the first version that I used with for 2.2, with narrow toolbar for tools and scroll bar, didn't crash. I think it was from master (all versions show 2.2 in about, without date of build or branch information). If you have list of branches/builds that you want me to check/compare, put the urls here and I will check.

iksi4prs avatar Jan 10 '24 09:01 iksi4prs

Thanks, yeah there isn't any additional info in the About window unfortunately

I think the main one to just compare against is the regular master branch, so the latest build there is https://github.com/PintaProject/Pinta/actions/runs/7470015712 I'm still trying to figure out why I'm not hitting these crashes on my Windows machine - another user had similar reports too

cameronwhite avatar Jan 11 '24 03:01 cameronwhite

Those things are hard to debug as you are on the mercy of the gc.

For example in debug mode or with a debugger attached the gc will behave differently.

Perhaps your machine is more beefy and the gc does not need to collect objects as frequently.

You can try to force collection but from my experience this is not guaranteed to result in collection of the wanted object.

You can try to call:

GC.Collect();
GC.WaitForPendingFinalizers();

To force collection.

badcel avatar Jan 11 '24 04:01 badcel

test 1: master, latest build 7470015712 (narrow toolbar with scrollbar) result - runs fine test 2: test branch, 7405489857 (with 2 columns toolbar) result: surprise... runs fine about 2 minutes and then crash. ok, at least I wrote in the subject "Random crash." I've checked the system memory, nothing special. A few days I wasn't able to start the app at all (exited before window was visible) Maybe as suggested by @badcel, this is GC dependent. I will try use it and see if crashes again, based on general system memory, or some peaks/swaps when opening more apps.

iksi4prs avatar Jan 11 '24 19:01 iksi4prs

In 13ebeda2 I added a --debug command line argument that runs the GC more frequently

cameronwhite avatar Jan 12 '24 20:01 cameronwhite

From what I saw, the "-debug" was added in master, but I've encountered the problem in build from branch "toolbox-layout". Can you please merge it into https://github.com/PintaProject/Pinta/tree/fix/toolbox-layout ? (If possible, better also to merge/rebases all other changes from master)

iksi4prs avatar Jan 14 '24 12:01 iksi4prs

Sure, I've rebased it on the latest master branch: https://github.com/PintaProject/Pinta/actions/runs/7523936006

cameronwhite avatar Jan 15 '24 03:01 cameronwhite

still getting crash in exe from 7523936006 Application: Pinta.exe CoreCLR Version: 8.0.123.58001 .NET Version: 8.0.1 Description: The process was terminated due to an unhandled exception. Exception Info: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. Stack: at GObject.Internal.Object.RemoveToggleRef(IntPtr, GObject.Internal.ToggleNotify, IntPtr) at GObject.Internal.Object.RemoveToggleRef(IntPtr, GObject.Internal.ToggleNotify, IntPtr) at GObject.Internal.ObjectMapper.Unmap(IntPtr) at GObject.Internal.ObjectHandle.ReleaseHandle() at System.Runtime.InteropServices.SafeHandle.InternalRelease(Boolean) at System.Runtime.InteropServices.SafeHandle.Finalize()

iksi4prs avatar Jan 17 '24 10:01 iksi4prs

Perhaps an idea is to have some print to the console somewhere in the stacktrace and print the name of the class which creates the problem?

badcel avatar Jan 17 '24 13:01 badcel

@badcel , as you wrote it maybe issue with GC, so I don't think it will help. I've downloaded this app just to try and use, so I don't have a dev env. I will try to download, build and debug on my machine as soon as I can.

iksi4prs avatar Jan 17 '24 13:01 iksi4prs

The collection happens asynchronous, so there is no way back to the creating code. But if there is a way to detect the type of the handle which gets freed it is perhaps possible to narrow down the classes which could cause the problem.

badcel avatar Jan 17 '24 15:01 badcel

Perhaps an idea is to have some print to the console somewhere in the stacktrace and print the name of the class which creates the problem?

Yeah I think that would be really helpful for tracking down issues 👍

cameronwhite avatar Jan 18 '24 03:01 cameronwhite

I'm not familiar with GC to that level. Most examples from MS required usage of windbg. I've done some tests with creating fatal errors during "Dispose" of some objects, and the stack/error looks different from the errors in Pinta, so needs more time to investigate. @cameronwhite, is the there a way release a version with pdb of external libs (like GtkSharp.dll, etc) ? currently I see .pdb only for Pinta files.

iksi4prs avatar Jan 18 '24 14:01 iksi4prs

@iksi4prs see: https://github.com/gircore/gir.core/pull/1004

Pinta uses GirCore which is compiled with source link support. So you can actually take a look into every file from pintas source.

badcel avatar Jan 18 '24 14:01 badcel

I'm not familiar with GC to that level. Most examples from MS required usage of windbg. I've done some tests with creating fatal errors during "Dispose" of some objects, and the stack/error looks different from the errors in Pinta, so needs more time to investigate. @cameronwhite, is the there a way release a version with pdb of external libs (like GtkSharp.dll, etc) ? currently I see .pdb only for Pinta files.

There also shouldn't be a GtkSharp.dll since that's from the GTK3 version of Pinta (we use gir.core now) - maybe you have some leftover files from an old installation?

cameronwhite avatar Jan 18 '24 17:01 cameronwhite

There also shouldn't be a GtkSharp.dll since that's from the GTK3 version of Pinta (we use gir.core now) - maybe you have some leftover files from an old installation?

I just run the installer every time. Looks like it didn't clean previous installation during upgrade. Now I've deleted the folder manually and did a "clean install".

iksi4prs avatar Jan 18 '24 18:01 iksi4prs

@cameronwhite,

I've installed dev env. And I see the crash in "unmap" with this stack:

image

I see that's GObject is external to Pinta, so I guess I should start by adding logs in dispose/dtors of objects that inherit from "GObject.Object" ?

iksi4prs avatar Jan 18 '24 20:01 iksi4prs

A lot of the subclasses of GObject.Object are also external to Pinta (from the generated GTK bindings in the gir.core library), so I'm not sure how easy it'll be to add logs in those We do have a few Object subclasses in Pinta for custom bindings etc though

You could also see if you can inspect value.Object when that crash happens, which I think is the corresponding managed object and that might let you see its type

cameronwhite avatar Jan 19 '24 00:01 cameronwhite

@cameronwhite , @badcel . The value.Object is null, but handle has value, I can see it has some value in call to GObject.Internal.Object.RemoveToggleRef in Dispose

I saw pr in https://github.com/gircore/gir.core/pull/1004 with logs ( https://github.com/gircore/gir.core/compare/main...release-handle-exception )

When it is expected to available in Pinta's build ?

iksi4prs avatar Jan 19 '24 10:01 iksi4prs

It makes sense that object is null. The handle gets released because there are no instances anymore in the first place, which requires the instance to be null.

The problem is that @cameronwhite raised the concern that, reading data from a pointer if an AccessViolationException happens could result in a new AccessViolationException.

I would not like to merge this without confirmation that it does not result in a new exception.

This would either require you to build GirCore yourself with the fix and afterwards build Pinta using your own local GirCore build to confirm it works or I find another solution. But this is not yet very obvious to me. There are lots of possible ways around this, but I want the code to be as clean and simple as possible.

badcel avatar Jan 19 '24 14:01 badcel

@iksi4prs i think you need to manually find the problem as the Microsoft docs read like the exception can't be caught.

badcel avatar Jan 22 '24 06:01 badcel

@badcel,

I'm trying to aim for a more global solution, in case occurs again in future.

What about adding logs in ObjectHandle.cs ?

eg, some "pseudo" code, with names that may not be accurate:

public class ObjectHandle : SafeHandle
{
    private static bool _verboseMode = Environment.GetCommandLineArgs.Contains("--gircore-verbose-gobject");

    public IntPtr Handle => IsInvalid ? IntPtr.Zero : DangerousGetHandle();

    public ObjectHandle(IntPtr handle, object obj, bool ownedRef) : base(IntPtr.Zero, true)
    {
       if (_verboseMode)
       {
           var obejctDetails = string that contains runtime type, and other details that can be obtained;
            Console.WriteLine($"{timestamp}, {nameof(ObjectHandle)}, handle: {handle}, obj: {obejctDetails}");
       }
        ObjectMapper.Map(handle, obj, ownedRef);
        SetHandle(handle);
    }

    public sealed override bool IsInvalid => handle == IntPtr.Zero;

    protected sealed override bool ReleaseHandle()
    {
       if (_verboseMode)
       {
            Console.WriteLine($"{timestamp}, {nameof(ReleaseHandle)}, handle: {handle}" ");
       }
        ObjectMapper.Unmap(handle);
        return true;
    }
}

iksi4prs avatar Jan 22 '24 11:01 iksi4prs

@iksi4prs there are already debug logs in place in ObjectMapper.Map and ObjectMapper.Unmap. But as the packages are compiled in release mode they get omitted.

I think it is fine to compile GirCore locally in debug mode if a project has such kind of problems which requires actual debug output.

(The log in Unmap should probably be moved to the top of the method.)

badcel avatar Jan 22 '24 16:01 badcel

@badcel ,maybe in this case (since exception cant be caught) it will be nice in the future to add "stopwatch" style logger, using disposable. eg:

#ifdef DEBUG
      using (new Logger())
#endif
      {
        lock (WrapperObjects)
        {
            if (WrapperObjects.Remove(handle, out var toggleRef))
                toggleRef.Dispose();
        }
     }

Where ctor of Logger logs the "Enter" and "Dispose" logs the "Exit", then easier to find places where no matching "Exit" and "Enter" pairs. Anyway, I will try build the debug version with logs and see if I can find anything.

iksi4prs avatar Jan 22 '24 18:01 iksi4prs

@cameronwhite ,@badcel,

I've built Pinta with Gir.Core and added some logs. Much harder to reproduce bug now. But looks to be consistent and related to the tooltip of the tools container on the left. I don't manage to create a reproducible steps, I just "play" with ui till crash. In all times the mapped object is "Gtk.Tooltip' So later I started to hover all buttons of tools, so all tooltips will show/open, and then start "play" with ui. eg;

Mapped Object: Handle '1776277271200' Object 'Gtk.Tooltip' OwnedRef 'False'.
Initialising Object: Address 1776277271200, Type Gtk.Tooltip.

Where for other case, this is also the stack of the mapping itself:


Mapped Object: Handle '2865452951200' Object 'Gtk.Tooltip' OwnedRef 'False'. StackTrace:   
at GObject.Internal.ObjectMapper.Map(IntPtr handle, Object obj, Boolean ownedRef) 
in D:\GirCore\gir.core\src\Libs\GObject-2.0\Internal\ObjectMapper.cs:line 37, 
at GObject.Internal.ObjectHandle..ctor(IntPtr handle, Object obj, Boolean ownedRef) 
in D:\GirCore\gir.core\src\Libs\GObject-2.0\Internal\ObjectHandle.cs:line 12,  
at GObject.Object..ctor(IntPtr handle, Boolean ownedRef) 
in D:\GirCore\gir.core\src\Libs\GObject-2.0\Public\Object.cs:line 29,   
at Gtk.Tooltip..ctor(IntPtr ptr, Boolean ownedRef) 
in D:\GirCore\gir.core\src\Libs\Gtk-4.0\Public\Tooltip.Framework.Generated.cs:line 16,  
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor),   
at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(
Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr), 
at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(
Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture),
at System.Reflection.RuntimeConstructorInfo.Invoke(
BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture),  
at System.Reflection.ConstructorInfo.Invoke(Object[] parameters),  
at GObject.Internal.ObjectWrapper.WrapHandle[T](IntPtr handle, Boolean ownedRef) 
in D:\GirCore\gir.core\src\Libs\GObject-2.0\Internal\ObjectWrapper.cs:line 47,  
at GObject.Internal.ObjectWrapper.WrapNullableHandle[T](IntPtr handle, Boolean ownedRef) 
in D:\GirCore\gir.core\src\Libs\GObject-2.0\Internal\ObjectWrapper.cs:line 13, 
at GObject.Value.GetObject() 
in D:\GirCore\gir.core\src\Libs\GObject-2.0\Public\Value.Generated.cs:line 259, 
at GObject.Value.CheckComplexTypes(UIntPtr gtype)
in D:\GirCore\gir.core\src\Libs\GObject-2.0\Public\Value.cs:line 74, 
at GObject.Value.Extract() 
in D:\GirCore\gir.core\src\Libs\GObject-2.0\Public\Value.cs:line 67,  
at GObject.Value.Extract[T]() 
in D:\GirCore\gir.core\src\Libs\GObject-2.0\Public\Value.cs:line 96,  
at GObject.SignalArgs.Extract[T](Value value)
in D:\GirCore\gir.core\src\Libs\GObject-2.0\Public\SignalArgs.cs:line 22,  
at Gtk.Widget.QueryTooltipSignalArgs.get_Tooltip()
in D:\GirCore\gir.core\src\Libs\Gtk-4.0\Public\Widget.Signals.Generated.cs:line 190,  
at Pinta.Gui.Widgets.StatusBarColorPaletteWidget.HandleQueryTooltip(Object o, QueryTooltipSignalArgs args) 
in D:\PintaWithGirCore\Pinta.Gui.Widgets\Widgets\StatusBarColorPaletteWidget.cs:line 268,  
at GObject.ReturningSignal`3.<>c__DisplayClass11_0.<Connect>b__0(Value returnValue, Value[] parameters)
in D:\GirCore\gir.core\src\Libs\GObject-2.0\Public\ReturningSignalTSenderTSignalArgs.cs:line 50,
at GObject.Closure.InternalCallback(
IntPtr closure, IntPtr returnValuePtr, UInt32 nParamValues, IntPtr paramValuesData, IntPtr invocationHint, IntPtr userData) 
in D:\GirCore\gir.core\src\Libs\GObject-2.0\Public\Closure.cs:line 40,  
at Gio.Internal.Application.Run(IntPtr application, Int32 argc, String[] argv), 
at Gio.Internal.Application.Run(IntPtr application, Int32 argc, String[] argv),  
at Gio.Application.Run(Int32 argc, String[] argv)
in D:\GirCore\gir.core\src\Libs\Gio-2.0\Public\Application.Methods.Generated.cs:line 215, 
at Gio.Application.RunWithSynchronizationContext(String[] args)
in D:\GirCore\gir.core\src\Libs\Gio-2.0\Public\Application.cs:line 23, 
at Pinta.MainClass.OpenMainWindow(Int32 threads, IEnumerable`1 files, Boolean debug) 
in D:\PintaWithGirCore\Pinta\Main.cs:line 117,
at Pinta.MainClass.<>c.<Main>b__0_1(Int32 threads, String[] files, Boolean debug) 
in D:\PintaWithGirCore\Pinta\Main.cs:line 74,  

My familiarity with the code is 0. Need some suggestions what to look for.

iksi4prs avatar Jan 23 '24 12:01 iksi4prs

@iksi4prs my advice would be to do the following steps:

  1. Move the debug log statement before the ToggleRef.Dispose call.
  2. If the crash occurs the moved debug log should trigger and tell you which handle caused the problem.
  3. Search all debug logs for the handle to detect the class of the handle.

Or did you already do this and the result is that it's GTK.Tooltip?

badcel avatar Jan 23 '24 12:01 badcel

@badcel , its consistent GTK.Tooltip, with stacktrace of mapping as mentioned in previous comment.

iksi4prs avatar Jan 23 '24 12:01 iksi4prs

Then this looks like a problem in either GirCore or a C library. Pinta should not be at fault here.

@cameronwhite perhaps there is or was a bug in GObject under windows? Do you know which version of GObject is used for the app under windows?

badcel avatar Jan 23 '24 13:01 badcel

I don’t know if it applies but perhaps this fix is related?

it looks like it is only available in 2.79.1 of glib/gobject.

badcel avatar Jan 23 '24 16:01 badcel