MemAssertion.NoNewInstances() methods returning True always

Use this forum for questions on how to use .NET Memory Profiler and how to analyse memory usage.
Post Reply
chai
Posts: 16
Joined: Tue Feb 24, 2009 11:37 pm

MemAssertion.NoNewInstances() methods returning True always

Post by chai » Tue Feb 24, 2009 11:57 pm

Hi,

Deployment Overview:
I am currently injecting the memory profiler into my target .NET process and am controlling the snapshot comparison via IPC from an external controller process. The reason for this is that the product code cannot be currently modified to have dependencies on the MemProfiler API assemby.
I am running the process with the profiler and I am able to check this using the API call MemProfiler.IsProfiling() in my injected code - this returns true as expected.

Problem:
In the injected code, I create a snapshot (I tried both Fast as well as Full) and then wait for the external controller process to inform me to do an assertion. When the injected code gets the go-ahead to assert, it calls MemAssertion.NoNewInstances(MemSnapShot) API. Here, I expect the assertion to fail but this passes even though there are tons of objects being leaked (this is a test application for understanding the MemProfiler API behavior.) To verify this, I am saving a named, Full snapshot after the assertion -- the saved snapshot is able to clearly show the memory leaks (delta counts.)

Problem Verification:
I modified my test app to directly call the MemProfiler.FastSnapShot() and MemAssertion.NoNewInstances() APIs in the code - and in this case, the assertion fails as expected! Therefore, the problem seems to be in the way Assertions are behaving in the injected scenario - even though deltas are huge, NoNewInstances() returns true...

Can you explain what's amiss in the things I a doing or observing here?
Thanks in advance!

Remarks:
This approach (using DLL injection) would be ideal for a totally automated test solution for our daily build scenarios. I am right now one step short of clean test automation -- the tester has to manually assess the session files after the test run. If the assertions work correctly, the automation tool chain can report memory leaks directly.

Andreas Suurkuusk
Posts: 1029
Joined: Wed Mar 02, 2005 7:53 pm

Post by Andreas Suurkuusk » Wed Feb 25, 2009 9:50 pm

It should be possible to inject memory assertions dynamically into your code. Declarative assertions are implemented using code injection, so I think your approach should work as well.

You mention that you have tried to create a snapshot in the injected code using MemProfiler.FullSnapShot. Does this work? Do you see the snapshot in the profiler?

Can you provide some additional information on how you inject the code? Are you calling the NoNewInstances API from the same thread that has created the instances? Or do you provide AssertionsThread.All as the assertionsThread argument?
Best regards,

Andreas Suurkuusk
SciTech Software AB

chai
Posts: 16
Joined: Tue Feb 24, 2009 11:37 pm

Post by chai » Thu Feb 26, 2009 1:49 pm

Hi Andreas,

The technique works fine - for some quirky reason, the day that I posted this query it was failing. However, yesterday and today, I've seen it work.

To answer your other questions: Yes, the full snapshot can be opened in the profiler. The code injection is done using a mixed + fully managed dll combination (2 DLLs) that uses synchronization objects to be informed by an external process on when to take a snapshot and when to do an assertion. I am calling NoNewInstances() from a new thread that my injected code creates. I am not using the assertionsThread argument. It appears that I am perhaps losing out on some information by not using the AssertionThread.All flag -- is this true? What happens when I simply call NoNewInstances() - are all threads covered or only only some thread like main thread? the fact that I am able to see memory information for objects that were certainly not on my "prolifer" thread, it appears NonNewInstances() implicitly means AssertionsThread.All but do confirm.

Thanks,
Chai

Andreas Suurkuusk
Posts: 1029
Joined: Wed Mar 02, 2005 7:53 pm

Post by Andreas Suurkuusk » Thu Feb 26, 2009 8:29 pm

If you're not using the assertionsThread parameter, the default value AssertionsThread.Caller will be used. This will cause the assertion to only check the instances in the calling thread. In your case that means that only the new instances in your "profiler thread" will be checked by the assertion. As I understand it, the NoNewInstances assertion works as you expect, i.e. it detects memory leaks in other threads. If this is the case, something is wrong with the assertion and we will have to investigate it further. However, if a snapshot is created due to a failed assertion, instances from all threads will be included in the snapshot, but only the new instances in the caller thread will be marked as "memory leaks". And when using the NoNewInstances assertion (without any type arguments) it is very easy to falsely identify instances as memory leaks, since new instances can be created as unexpected side effects.
Best regards,

Andreas Suurkuusk
SciTech Software AB

chai
Posts: 16
Joined: Tue Feb 24, 2009 11:37 pm

Post by chai » Fri Feb 27, 2009 6:32 am

This explains why only 4 "string" objects were reported as leaking by the failed assertion (apparently getting leaked on the "profiler" thread due to lockdown by a gc handle - have to investigate this further) whereas the full snapshot showed the presence of 100's of other production code objects. I will try this .All flag and get back to you if more objects are not getting reported as leaky.
As for NoNewInstances(void) calls, it reminds me that I could not find a (simple) way to recursively exclude or include classes under a namespace hierarchy for evaluation. The user documentation of the usage of wild characters while providing namespace names shows examples like "System.Windows.Forms.*" -- in general, does applying "*" include classes under child namespaces as well? This would be very convenient as I could then pass "MyCompanyName.MyProductName.*" to specify any and all child namespaces and classes therein to be included for memory leak evaluation. Any directions?

Thanks,
Chai

Andreas Suurkuusk
Posts: 1029
Joined: Wed Mar 02, 2005 7:53 pm

Post by Andreas Suurkuusk » Fri Feb 27, 2009 5:24 pm

Yes, using the wildcard '*' will include all classes that begin with the provided name. So "MyCompanyName.*" will include all classes under the namespace MyCompanyName and its sub-namespaces, e.g. MyCompanyName.ProductName.
Best regards,

Andreas Suurkuusk
SciTech Software AB

Post Reply

Who is online

Users browsing this forum: No registered users and 16 guests