Skip to content

Fix false-positives when using ResourceLeakDetector.#6087

Closed
normanmaurer wants to merge 1 commit into
4.1from
fix_false_positive_leak_reports
Closed

Fix false-positives when using ResourceLeakDetector.#6087
normanmaurer wants to merge 1 commit into
4.1from
fix_false_positive_leak_reports

Conversation

@normanmaurer
Copy link
Copy Markdown
Member

@normanmaurer normanmaurer commented Dec 1, 2016

Motivation:

We need to ensure the tracked object can not be GC'ed before ResourceLeak.close() is called as otherwise we may get false-positives reported by the ResourceLeakDetector. This can happen as the JIT / GC may be able to figure out that we do not need the tracked object anymore and so already enqueue it for collection before we actually get a chance to close the enclosing ResourceLeak.

Modifications:

  • Add ResourceLeakTracker and deprecate the old ResourceLeak
  • Fix some javadocs to correctly release buffers.
  • Add a unit test for ResourceLeakDetector that shows that ResourceLeakTracker has not the problems.

Result:

No more false-positives reported by ResourceLeakDetector when ResourceLeakDetector.track(...) is used.

@normanmaurer normanmaurer self-assigned this Dec 1, 2016
//
// See also https://github.com/netty/netty/issues/6034
//
return leak.close() && trackedObject != null;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this may be optimized away at some point...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it can't as we use it in the return value... if I remove it the false positives start again, with it not

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@normanmaurer - I wonder if we should consider this an API bug and just change the ResourceLeak API ... if others are using this interface they may be susceptible to the same bug without knowing ... changing the API will force them to fix the bug.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Scottmitch I was considering this but decided against this as this may break code when people upgrade. False-positives are bad but not as bad as breaking code.

That said I wonder if we maybe could just extend ResourceLeak and add the method there and then do an instanceof and if so call the new method. WDYT ? We would still deprecate the old method tho.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even better we could also add another method to ResourceLeakDetector which returns this new subclass and use it in our code. Then we mark ResourceLeakDetector.open(...) as deprecated.

@Scottmitch let me do this and add as a separate commit to this pr so you can review more easily

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@normanmaurer - the new ResourceLeakTracker lgtm ... can we just delete this utility method and add the warning on the close method in ResourceLeak? Just less code to maintain.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we just return derived in this case? What benefit does wrapping in a "leak aware buffer" with NoopResourceLeak provide? Or should we add a package private method on ResourceLeakDetector so that we can force a DefaultResourceLeak to be allocated (only used in this situation when the buffer is derived from another buffer which already has leak info)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Scottmitch it was only basically to be consistent and always return the wrapper. That said I think it would also be fine to just return derived. Let me change this

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are there other types of buffers that we should do buf.unwrap() to? Also is it possible that the SwappedByteBuf will not be the "top level" and we need to go through the "unwrap" chain to properly find/unwrap it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me think a bit more about this but I think no...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reproducible

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is its here? can you link to an issue or provide more context?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we catch Throwable after the other exceptions (e.g. InterruptedException) to make sure we bubble up the exception and it is visible from the junit thread?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: final?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: final?

@normanmaurer normanmaurer force-pushed the fix_false_positive_leak_reports branch 2 times, most recently from 10c5e66 to 8927bfa Compare December 1, 2016 20:06
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Scottmitch I need to adjust the comment here if you agree how I fixed it now makes sense in terms of API changes.

@normanmaurer normanmaurer force-pushed the fix_false_positive_leak_reports branch 3 times, most recently from 6cfe500 to e016579 Compare December 1, 2016 20:54
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: kill line

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if ResourceLeakRecoder makes more sense since the action this interface provides is record ... or maybe change the methods to track?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kind of like ResourceLeakTracker more... @nmittler any preferences ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fine as is

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe consider rewording or adding the following:

After this method is called a leak associated with this ResourceLeakTracker should not be reported.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a warning as to this API (specifically close) may result in false positives.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deprecated -> @deprecated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also add a todo here to make private again once #6081 is fixed.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to NoopByteBufResourceLeakTracker or just make this class generic (preferably the latter)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

//
// See also https://github.com/netty/netty/issues/6034
//
return leak.close() && trackedObject != null;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@normanmaurer - the new ResourceLeakTracker lgtm ... can we just delete this utility method and add the warning on the close method in ResourceLeak? Just less code to maintain.

Copy link
Copy Markdown
Member

@Scottmitch Scottmitch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ResourceLeakTracker lgtm .. few more comments though

@normanmaurer normanmaurer force-pushed the fix_false_positive_leak_reports branch from e016579 to 32647a6 Compare December 1, 2016 21:36
shutdown();
if (leak != null) {
leak.close();
boolean closed = leak.close(ReferenceCountedOpenSslEngine.this);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Remove this useless assignment to local variable "closed". rule


if (leak != null) {
leak.close();
boolean closed = leak.close(this);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Remove this useless assignment to local variable "closed". rule


if (leak != null) {
leak.close();
boolean closed = leak.close(this);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Remove this useless assignment to local variable "closed". rule

final ResourceLeakTracker<DnsMessage> leak = this.leak;
if (leak != null) {
leak.close();
boolean closed = leak.close(this);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Remove this useless assignment to local variable "closed". rule

destroy();
if (leak != null) {
leak.close();
boolean closed = leak.close(ReferenceCountedOpenSslContext.this);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Remove this useless assignment to local variable "closed". rule

@normanmaurer normanmaurer added this to the 4.0.43.Final milestone Dec 2, 2016
@normanmaurer normanmaurer force-pushed the fix_false_positive_leak_reports branch 2 times, most recently from 21b5778 to 85e3420 Compare December 2, 2016 12:40
@normanmaurer
Copy link
Copy Markdown
Member Author

@Scottmitch PTAL again... I also added a few more asserts and shares some more code. I am quite happy now with the result :)

// collection before we actually get a chance to close the enclosing ResourceLeak.
boolean closed = close() && trackedObject != null;

// Ensure that the object that was tracked is the same as the one that was passed to close(...).
Copy link
Copy Markdown
Member Author

@normanmaurer normanmaurer Dec 2, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Scottmitch note this check... This also ensure we really pass in the correct object and so the GC can not collect it before we called close() :)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Asserts could be eliminated by JIT compiler (especially, when disabled), so I don't think relying on assert here would buy you much.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vlsi thi was not done to prevent GC or such, just to ensure we do the right thing. That said maybe this should be guarded by an IllegalArgumentException. @Scottmitch WDYT ?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@normanmaurer , I see. Would you probably add an explanation message then to the assert statement?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with it being an assert for now as this is typically an internal Netty concern and thus should be caught by unit tests, and it not required to be enforced by the interface. If it becomes an issue we can always make the check more strict.

Can we move the assert to the top of the method and just directly return close() && trackedObject != null after?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

// Store the hash of the tracked object to later assert it in the close(...) method.
// It's important that we not store a reference to the referent as this would disallow it from
// be collected via the PhantomReference.
trackedHash = System.identityHashCode(referent);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Scottmitch see below why I did this ....

private final ResourceLeak leak;
// This is always the original ByteBuf whichs reference-count is responsible to signal when the ResourceLeakTracker
// should be closed. This means this always must be the ByteBuf that is used when calling LeakDetector.track(...).
private final ByteBuf trackedByteBuf;
Copy link
Copy Markdown
Member Author

@normanmaurer normanmaurer Dec 2, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Scottmitch see the ResourceLeakDetector.close(...) method why I now keep track of this one as well

private void closeLeak(ByteBuf trackedByteBuf) {
// Close the ResourceLeakTracker with the tracked ByteBuf as argument. This must be the same that was used when
// calling DefaultResourceLeak.track(...).
boolean closed = leak.close(trackedByteBuf);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Remove this useless assignment to local variable "closed". rule

private void closeLeak() {
// Close the ResourceLeakTracker with the tracked ByteBuf as argument. This must be the same that was used when
// calling DefaultResourceLeak.track(...).
boolean closed = leak.close(trackedByteBuf);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Remove this useless assignment to local variable "closed". rule


// Called from within SimpleLeakAwareByteBuf and AdvancedLeakAwareByteBuf.
final void parent(ByteBuf newParent) {
assert newParent instanceof SimpleLeakAwareByteBuf || newParent instanceof AdvancedLeakAwareByteBuf;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Scottmitch I could even change this to assert newParent instanceof SimpleLeakAwareByteBuf as AdvancedLeakAwareByteBufnow extends SimpleLeakAwareByteBuf . Not sure if its better to be explicit here tho. WDYT ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just checking SimpleLeakAwareByteBuf sgtm ... its an assert meant for debugging ... if the class hierarchy changes the assert will trip and we can update it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true enough... will change :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@normanmaurer
Copy link
Copy Markdown
Member Author

@Scottmitch rest is good ?

class SimpleLeakAwareByteBuf extends WrappedByteBuf {

private final ResourceLeak leak;
// This is always the original ByteBuf whichs reference-count is responsible to signal when the ResourceLeakTracker
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/**
 * This object's is associated with the {@link ResourceLeakTracker}.
 * When {@link ResourceLeakTracker#close(Object}} is called this object will be
 * used as the argument. It is also assumed that this object is used when
 * {@link ResourceLeakDetector#track(Object)} is called to create {@link #leak}.
 */

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fine as is

// collection before we actually get a chance to close the enclosing ResourceLeak.
boolean closed = close() && trackedObject != null;

// Ensure that the object that was tracked is the same as the one that was passed to close(...).
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with it being an assert for now as this is typically an internal Netty concern and thus should be caught by unit tests, and it not required to be enforced by the interface. If it becomes an issue we can always make the check more strict.

Can we move the assert to the top of the method and just directly return close() && trackedObject != null after?

Motivation:

We need to ensure the tracked object can not be GC'ed before ResourceLeak.close() is called as otherwise we may get false-positives reported by the ResourceLeakDetector. This can happen as the JIT / GC may be able to figure out that we do not need the tracked object anymore and so already enqueue it for collection before we actually get a chance to close the enclosing ResourceLeak.

Modifications:

- Add ResourceLeakTracker and deprecate the old ResourceLeak
- Fix some javadocs to correctly release buffers.
- Add a unit test for ResourceLeakDetector that shows that ResourceLeakTracker has not the problems.

Result:

No more false-positives reported by ResourceLeakDetector when ResourceLeakDetector.track(...) is used.
@normanmaurer normanmaurer force-pushed the fix_false_positive_leak_reports branch from 85e3420 to fed6acd Compare December 3, 2016 07:57
@normanmaurer
Copy link
Copy Markdown
Member Author

@Scottmitch all addressed ... PTAL again

normanmaurer added a commit that referenced this pull request Dec 3, 2016
Motivation:

While working on #6087 some buffer leaks showed up.

Modifications:

Correctly release buffers.

Result:

No more buffer leaks in memcache and stomp codec tests.
normanmaurer added a commit that referenced this pull request Dec 3, 2016
Motivation:

While working on #6087 some buffer leaks showed up.

Modifications:

Correctly release buffers.

Result:

No more buffer leaks in memcache and stomp codec tests.
@Scottmitch
Copy link
Copy Markdown
Member

@normanmaurer - Good work ... ship it!

@normanmaurer
Copy link
Copy Markdown
Member Author

Cherry-picked into 4.1 (c2f4daa) . Will open an extra pr for 4.0 as there is a bit more work to do.

@normanmaurer normanmaurer deleted the fix_false_positive_leak_reports branch December 4, 2016 08:26
@normanmaurer normanmaurer modified the milestones: 4.1.7.Final, 4.0.43.Final Dec 4, 2016
liuzhengyang pushed a commit to liuzhengyang/netty that referenced this pull request Sep 10, 2017
Motivation:

While working on netty#6087 some buffer leaks showed up.

Modifications:

Correctly release buffers.

Result:

No more buffer leaks in memcache and stomp codec tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants