This is an archive of the discontinued LLVM Phabricator instance.

[XRay][compiler-rt] xray::Array Freelist and Iterator Updates
ClosedPublic

Authored by dberris on Jun 27 2018, 9:08 AM.

Details

Summary

We found a bug while working on a benchmark for the profiling mode which
manifests as a segmentation fault in the profiling handler's
implementation. This change adds unit tests which replicate the
issues in isolation.

We've tracked this down as a bug in the implementation of the Freelist
in the xray::Array type. This happens when we trim the array by a
number of elements, where we've been incorrectly assigning pointers for
the links in the freelist of chunk nodes. We've taken the chance to add
more debug-only assertions to the code path and allow us to verify these
assumptions in debug builds.

In the process, we also took the opportunity to use iterators to
implement both front() and back() which exposes a bug in the
iterator decrement operation. In particular, when we decrement past a
chunk size boundary, we end up moving too far back and reaching the
SentinelChunk prematurely.

This change unblocks us to allow for contributing the non-crashing
version of the benchmarks in the test-suite as well.

Diff Detail

Repository
rL LLVM

Event Timeline

dberris created this revision.Jun 27 2018, 9:08 AM
dberris updated this revision to Diff 153433.Jun 28 2018, 8:55 PM
  • fixup: Add a test to an additional edge-case; propagate extents into iterator
dberris updated this revision to Diff 153887.Jul 3 2018, 2:44 AM
  • Rebase to master.
    • fixup: Add a test to an additional edge-case; propagate extents into iterator
    • fixup: play with alignment, flags, and testing properly
    • fixup: fix bug in trim implementation in isolation
    • fixup: all changes to fix benchmark run
dberris planned changes to this revision.Jul 3 2018, 6:20 AM

This change is now bigger than the original posted, and I'm going to spend a bit more time cleaning it up. In particular I intend to:

  • Explore more edge cases in the unit tests for the segmented array.
  • Document and test the assumptions for the allocator implementation.
  • Reduce the memory requirements by changing the sizing algorithm used for the chunks in the segmented array.
  • Reduce the size of the segmented array by attempting to remove the 'FreeElements' member.

Let's determine whether I can split it up further to make it easier to review when all the changes are done (if it's even possible to break it up).

This change is now bigger than the original posted, and I'm going to spend a bit more time cleaning it up. In particular I intend to:

  • Explore more edge cases in the unit tests for the segmented array.

This didn't seem to be blocking, so I ended up not doing it.

  • Document and test the assumptions for the allocator implementation.

It also looked like this could be done after the fact.

  • Reduce the memory requirements by changing the sizing algorithm used for the chunks in the segmented array.

I tried going down this route and it was clear that we needed to get the alignment correct, so being conservative here for correctness seems fine for now.

  • Reduce the size of the segmented array by attempting to remove the 'FreeElements' member.

I've done this, which simplifies the code a lot.

Let's determine whether I can split it up further to make it easier to review when all the changes are done (if it's even possible to break it up).

I thought about this more and it seems easier to get this in as a batch instead of trying to do this in smaller batches.

dberris updated this revision to Diff 154042.Jul 3 2018, 7:55 PM
  • fixup: clean up some more, now ready for a look.
dberris updated this revision to Diff 154160.Jul 4 2018, 6:00 PM
dberris edited the summary of this revision. (Show Details)
dberris removed a reviewer: eizan.
dberris added a subscriber: hiraditya.

Update description, rebase on top of bug-fix change in profiling mode.

dberris updated this revision to Diff 154162.Jul 4 2018, 6:03 PM
dberris edited the summary of this revision. (Show Details)
dberris removed a subscriber: hiraditya.

Reverting change, restoring "good" state.

kpw accepted this revision.Jul 9 2018, 10:15 PM

Seems like half of this change is DCHECKS. Much better coverage! Was that just a result of your strategy for diagnosing what actually caused the problems and leaving those in?

compiler-rt/lib/xray/tests/unit/function_call_trie_test.cc
177 ↗(On Diff #154162)

No EXPECT statement for the time tracking?

compiler-rt/lib/xray/tests/unit/segmented_array_test.cc
129–130 ↗(On Diff #154162)

You can replace "ChunkX2 / 2" with the equivalent "Chunk" here and below.

150 ↗(On Diff #154162)

Why Append here and AppendEmplace above?

compiler-rt/lib/xray/xray_allocator.h
153 ↗(On Diff #154162)

So trim of segmented array was free-ing the "block" with the wrong allocator when trimming the first element?

compiler-rt/lib/xray/xray_function_call_trie.h
367 ↗(On Diff #154162)

Should the NewRoots point back to the old roots as a parent? Don't we want to keep this isolated?

compiler-rt/lib/xray/xray_segmented_array.h
44 ↗(On Diff #154162)

I assume this is all alignment related?

171 ↗(On Diff #154162)

This size check is applicable when decrementing end()?

204–205 ↗(On Diff #154162)

Lol. Checks out.

289–290 ↗(On Diff #154162)

This is common enough you might want to put it into an always inlined private member function. Maybe currentElementData()

303 ↗(On Diff #154162)

I would comment operator-- with the fact that it must not mutate the iterator. It's counterintuitive that it's non-destructive while ++ advances state.

331 ↗(On Diff #154162)

This feels like it probably simplifies to an equivalent expression.

This revision is now accepted and ready to land.Jul 9 2018, 10:15 PM
dberris marked 9 inline comments as done.Jul 10 2018, 12:21 AM
In D48653#1156953, @kpw wrote:

Seems like half of this change is DCHECKS. Much better coverage! Was that just a result of your strategy for diagnosing what actually caused the problems and leaving those in?

Yep! Turns out that using assertions is a great way to... well... assert things. :)

compiler-rt/lib/xray/tests/unit/segmented_array_test.cc
150 ↗(On Diff #154162)

No good reason. :) Fixed.

compiler-rt/lib/xray/xray_allocator.h
153 ↗(On Diff #154162)

Yeah, this one was hard to track. We were running into undefined behaviour in a lot of places given alignment issues with reinterpret_cast's.

compiler-rt/lib/xray/xray_function_call_trie.h
367 ↗(On Diff #154162)

No, but what we're doing here is a parallel traversal of the tries -- we add the root from the original trie alongside the new root in the new new/destination trie. That way we can recreate the structure from the source trie into the new trie.

compiler-rt/lib/xray/xray_segmented_array.h
44 ↗(On Diff #154162)

Yes, we'd like to have a power-of-two size for the structures, to allow easier pointer alignment calculations.

171 ↗(On Diff #154162)

Yes -- we know that when PreviousOffset == Size and neither are 0, that the offset is now pointing to a valid object.

204–205 ↗(On Diff #154162)

Yeah, ensuring correct alignment is hard. :P

303 ↗(On Diff #154162)

operator-- actually does mutate the iterator. What we're doing here is being explicit about storing the state of the iterator into It first, mutating It, and then de-referencing it.

331 ↗(On Diff #154162)

Actually turns out the math was slightly wrong, updated and fixed.

dberris updated this revision to Diff 154761.Jul 10 2018, 12:23 AM
dberris marked 7 inline comments as done.

Address comments, rebase, before landing.

This revision was automatically updated to reflect the committed changes.