0

My goal is to save in a file the callstack state when an exception is raised. But I have one major restraint: I cannot touch any of the existing code. My goal is to implement that in a context where I'm unaware of the current workflow. So I can use it in any C# Project.

To do so I need to have access to all the nested methods that lead to the exception but also all the method's arguments.

Until now I used postsharp OnMethodBoundaryAspect which allow me to define OnEntry, OnSucess, and OnException method that is called whenever a method: is entered, returns, or raise an exception. What is great is that I can apply this aspect with a postsharp.config file in the project I want. No source modification, I'm only adding my project to the solution, a config file, and reference to my project in every project of the solution and I'm good to go. Using that I could maintain a callstack of my own, and I could save references to methods' name, and ref to methods' arguments.

Now let's say a methodA calls a methodB which calls a methodC.

What I've done works well except that obviously I use references. So if methodA argument is modified in methodC just before raising an exception, when I serialize the call stack, We'll see a call stack where methodA has an argument with a wrong value (we'll see the value set in methodC).

I want to fix this issue and I considered using:

  1. deepcloning in the OnEntry method to copy all the methods parameters but that would lead to awful performances
  2. PostSharp LocationInterceptorAspect but that requires a paid licence
  3. VEH Hooking but it is incredibly slow and it would probably be faster to use deepcloning

Is there any other way that would allow me to save the state of an object in time with greater performances than deepcloning, or at least let me intercept all modifications brought to a specified object (like the VEH hooking or the LocationInterceptor Aspect)?

PS: I can't use IOnPropertyChange or things like that because I can't touch any of the existing sourceCode except if I can implement them at runtime but I didn't see anywhere that it was possible.

Useme Alehosaini
  • 2,998
  • 6
  • 18
  • 26
Amon
  • 296
  • 1
  • 14
  • 1
    You could hook into the [`AppDomain.UnhandledException `](https://learn.microsoft.com/en-us/dotnet/api/system.appdomain.unhandledexception?view=netcore-3.1) event and then use the `ExceptionObject` property (cast/ converted to an exception) to access the `StackTrace` property to get a stack trace, as well as the `TargetSite` property to get access to the `MethodBase` of the method that threw the exception. With that information you may be able to get the values you want, [not likely though](https://stackoverflow.com/a/1867496/9363973) – MindSwipe Nov 09 '20 at 11:08
  • 1
    At the JIT level objects cease existing and it's all memory locations, and what the object's "state" is is up to the beholder. Copying the state (deep cloning if you must) and applying delta compression on the fly to eliminate unmodified state would be one way of keeping the cost down. There's no free lunch there; no native mechanism or debugger could do substantially better than this, given that memory hardware doesn't implement immutable semantics or cheap notification triggers. VS has IntelliTrace/historical debugging for this, which snapshots a bunch and is likewise expensive. – Jeroen Mostert Nov 09 '20 at 11:09

1 Answers1

0

Considering that I didn't want to implement any specific code in the object that needed to be monitered I had only 2 options :

  1. Serialization or any deepcloning method whenever a state need to be saved (I used JSON.NET for this solution)

  2. Logging all modification brought to the object in order to reverse them if necessary => to be notified when a modification will occur I explored all ths techniques:

    • Postsharp with the LocationInterceptionAspect (not free)
    • Harmony 2 which allow to intercept any function call (and in my case the setter function of properties), simplest and easiest working solution if you can discard any field changes
    • VEH hooking, would work in theory but extremely slow and I didn't explore it that much because of its complexity
    • Editing the MSIL code of a function to hook all the stfld instruction.
Amon
  • 296
  • 1
  • 14