This is an archive of the discontinued LLVM Phabricator instance.

[XRay] FDR Trace Dump Tool (FDR Trace Loading Refactor)
AbandonedPublic

Authored by dberris on Aug 8 2018, 5:51 AM.

Details

Reviewers
kpw
eizan
Summary

This change implements a FDR trace dumping tool which allows us to
inspect the contents of an FDR mode trace in isolation. This is the
beginning of a larger refactoring of the FDR mode trace loading
implementation.

This change breaks up the FDR trace loading implementation into a number
of parts, allowing for better composition, testing, and extension.

The first part of this change is defining an abstract base type "Record"
which branches off into either a set of "MetadataRecord" types or
"FunctionRecord" instances. This allows us to then to isolate the logic
of reading data from a DataExtractor from interpreting the structure of
the log.

Doing this allows us to then more faithfully represent the data from a
trace as we see it from a log, independent of the semantic structure of
the trace.

The other part of this change involves defining a visitor abstraction,
which we use to process the log at a higher level of abstraction. It
allows us to process blocks of the trace more correctly, by grouping
individual records as blocks and arranging them by process+thread, and
interpreting the execution traces on a per-thread basis. To this end, we
implement a number of visitors which isolate the various higher-level
functions that operate on FDR traces. Some of these are:

  • BlockIndexer: A visitor that generates an index grouping of blocks.
  • RecordPrinter: A visitor that can print an individual Record instance.
  • BlockPrinter: A visitor which defines a block-specific

pretty-printer.

  • BlockValidator: A visitor that validates whether the log's structure is consistent with the expectations of the FDR mode log format.

We're starting the refactoring on the FDR mode implementation, then
performing a similar refactoring on the basic mode trace loading
implementation.

In this change we also take a chance to define a builder for each of the
record types, to allow us to generate valid and invalid sequences of
records in unit-testing. The builder interface is solely for testing
purposes.

NOTE: This change is rather large, suggestions for potential partitions are most welcome.

Event Timeline

dberris created this revision.Aug 8 2018, 5:51 AM
kpw added a comment.Aug 8 2018, 11:13 PM

Suggestion for splitting this up.

Patch 1: FDRRecords and RecordVisitor and the Record subclasses.
Patch 2: Visitor implementations
Patch 3: fdr-dump command

I only read code from the hypothetical Patch 1 for these comments.

llvm/include/llvm/XRay/BlockPrinter.h
61

newline?

llvm/include/llvm/XRay/BlockVerifier.h
26

If you forward declare this and move the TransitionTable to the cc file, you could use the .inc macro trick to automatically get two string from the list.

45–67

It's hard to tell whether this is right. I didn't notice any problems, but it is dense.

Is there a better way? Especially with regards to new record types?

llvm/include/llvm/XRay/FDRRecords.h
30–34

Could be confusing for name lookup that this has the same name (modulo qualifiers) as BlockVerifier::RecordKind

51

It kind of seems like this member function should be const qualified and the visit method should take a const reference to Record.

229

FnType? Since there are already a few RecordKind types to possibly confuse.

244–246

Mixed casing convention. You have like_this() and likeThat().

258

I think the Record references can and should be const.

284

push_back(&&) of wrap_unique(new R(std::forward<T>(A)...)) would be my preference.

Emplacement coupled with the unique pointer within the vector template parameter is equivalent but less obvious to me.

299–300

Does this replace or append to the Records? Can it even be called multiple times?

llvm/include/llvm/XRay/RecordPrinter.h
49

No newline.

llvm/lib/XRay/Common.h
1 ↗(On Diff #159695)

This is almost never a good name and this file is doomed to become a miscellaneous catch all.

llvm/lib/XRay/FDRRecords.cpp
2

I'm going to only skim this and ask you whether you made any changes other than just copy and paste to the logic that lived elsewhere other than the state transition map and assigning to fields instead of structs directly.

277–278

Meh. This seems harmless to me. You could declare an enum with the constants if you like as an easy fix.

kpw added a comment.Aug 9 2018, 10:22 AM

Thought that occurred to me this morning:

This models a sequence of records and not a more complex structure like a tree. Instead of greedily filling a vector and then calling visitors, this could be better done with a streaming model.

The "driver" code would maintain a sequence of visitors and call them on each record that it reads.

It should be a small refactoring and could dramatically improve memory use especially for stateless visitors like the printer.

In D50441#1194024, @kpw wrote:

Thought that occurred to me this morning:

This models a sequence of records and not a more complex structure like a tree. Instead of greedily filling a vector and then calling visitors, this could be better done with a streaming model.

Unfortunately, it's not that simple.

Blocks that belong to the same threads/processes have a well-defined order and model a sequence of operations bound to time. This is the reason the indexer is a key visitor implementation, and why we can implement a validator that applies to logical blocks. In the process of re-constituting a single execution trace, we need to model the execution context as individual streams of execution that interlace over time in multiple dimensions (cpu, process, thread).

The complication here is that at runtime, since the implementation can be re-using buffers in a circular buffer, we cannot assume that the blocks which appear at the beginning always come before the blocks that follow in the time dimension. The spatial ordering doesn't imply temporal ordering, so we need to somehow re-construct the temporal order (hence the indexer).

What this patch doesn't show is how we're really supposed to sort the blocks in the indexes first, in temporal order (using the block's wallclock record), before reconstituting a full Trace object (which is the ultimate goal anyway) which requires the logical verification first. That's a separate algorithm which we don't implement yet.

There's a bit of foreshadowing going on, which I really should have explained somewhere in the first place. :)

The "driver" code would maintain a sequence of visitors and call them on each record that it reads.

It should be a small refactoring and could dramatically improve memory use especially for stateless visitors like the printer.

For the purposes of printing a log, I agree.

That seems like a simple algorithm which applies visitors for every record we encounter as we encounter it, without validating, i.e. a true "dump" tool. The missing abstraction seems to be a pipelining mechanism, or a sequencing visitor that determines the order of the visitors to apply.

That doesn't obviate the need for a logical in-memory structure for the reconstitution algorithm though for other uses. I suppose those can come later when we end up actually moving the current convert implementation to using this infrastructure for handling FDR mode logs.

dberris updated this revision to Diff 160293.Aug 12 2018, 11:49 PM
dberris marked 5 inline comments as done.
  • fixup: optimize storage of transition table
  • checkpoint: implement producer API and migrate record initialization to a visitor
  • checkpoint: implement non-verifying dump mode
dberris planned changes to this revision.Aug 12 2018, 11:49 PM

I'll be breaking this up into smaller patches, please bear with me. I've also in the process added a few more abstractions, which makes the types cleaner to operate with -- for instance, I've moved the initialization of records into a visitor which is a friend of each of the record types. This way we isolate the logic of loading the data from a DataExtractor from the Record types. There will be more moving around of types and filenames, so please hold off on further review.

llvm/include/llvm/XRay/BlockVerifier.h
26

Unfortunately, it seems I can't really do that with C++11 enum classes.

45–67

I moved it to the implementation file, and added a few comments to represent the transitions. I'm not exactly sure if it's better, but worth the shot.

llvm/include/llvm/XRay/FDRRecords.h
51

Unfortunately, that contract is much tighter and severely limits the kinds of visitors that could be implemented -- there may be some that end up needing to modify a record when it's visited (you can imagine a visitor that adjusts the deltas for FunctionRecord instances, or those that process the data associated with a custom event, etc.).

244–246

fixed -- using camelCase() more consistently for member functions.

258

Explained elsewhere, that limits the kinds of visitors that could be written. I rather keep it like this to allow visitor implementations to modify Record instances should they need to.

llvm/lib/XRay/Common.h
1 ↗(On Diff #159695)

Good point. Renamed.

llvm/lib/XRay/FDRRecords.cpp
2

This is mostly copy-paste, although the custom event loading implementation also reads the data from the log.

dberris abandoned this revision.Sep 12 2018, 7:55 AM

All changes here have now landed in a series of patches. Abandoning this revision now.