This is an archive of the discontinued LLVM Phabricator instance.

[trace][intel pt] Implement the Intel PT cursor
ClosedPublic

Authored by wallace on Jul 6 2021, 11:16 PM.

Details

Summary

D104422 added the interface for TraceCursor, which is the main way to traverse instructions in a trace. This diff implements the corresponding cursor class for Intel PT and deletes the now obsolete code.

Besides that, the logic for the "thread trace dump instructions" was adapted to use this cursor (pretty much I ended up moving code from Trace.cpp to TraceCursor.cpp). The command by default traverses the instructions backwards, and if the user passes --forwards, then it's not forwards. More information about that is in the Options.td file. An interesting change is that I'm printing positive indices when traversing forwards, and negative ones when doing it backwards. They do help readability and don't require to know the total instruction count. You can check the tests to see that.

Regarding the Intel PT cursor. All Intel PT cursors for the same thread share the same DecodedThread instance. I'm not yet implementing lazy decoding because we don't need it. That'll be for later. For the time being, the entire thread trace is decoded when the first cursor for that thread is requested.

Diff Detail

Event Timeline

wallace created this revision.Jul 6 2021, 11:16 PM
wallace requested review of this revision.Jul 6 2021, 11:16 PM
Herald added a project: Restricted Project. · View Herald TranscriptJul 6 2021, 11:16 PM
wallace updated this revision to Diff 356875.Jul 6 2021, 11:25 PM

improve documentation

wallace edited the summary of this revision. (Show Details)Jul 6 2021, 11:30 PM
clayborg requested changes to this revision.Jul 7 2021, 2:00 PM
clayborg added inline comments.
lldb/include/lldb/Target/TraceCursor.h
68–69

move this above destructor above.

108–119

Some of these are out of order (skip and count), or missing (direction).

120

Should we just use a bool for direction? There can't be any direction other than forward or backward.

120–121

Remove the "size_t skip" from this API. It isn't needed because the user can move the cursor around before they call this API to skip or position the cursor as needed, then call this function.

130–137

This description is pretty vague. It should probably be removed or fully documented to state exactly what is expected of trace clients. This also seems like something that might be returned when moving the cursor around instead of a separate method? From the description, I am unclear as to what I should expect from traversing backwards. What happens when I call SeekToEnd(), or SeekToBegin()? What happens if I call Next(...) or Prev(...) when the granularity is set in a specific way?

134

Probably best to store this as a: ExecutionContextRef m_exe_ref. See description of ExecutionContextRef in ExecutionContext.h for reasons why. The main issue is we don't want a TraceCursor to keep a strong reference to a thread and keep the thread and possibly the process object alive. Keeping a weak reference is safer and allows you to try to fetch the thread when needed by calling ExecutionContextRef::GetThreadSP()

lldb/include/lldb/lldb-enumerations.h
962–966 ↗(On Diff #356875)

Unless there is another direction, I would just switch this to a bool and remove this from the public API. Fine to use it internally if needed.

lldb/source/Commands/CommandObjectThread.cpp
2103–2107

take care of skip on the cursor _before_ calling this API, and remove skip argument.

2104–2105

just pass a bool? Enum seems a bit much for a simple bool argument. Unless there is going to be another direction in the future, but I can't think of one.

lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
121

You only need to inherit from "std::enable_shared_from_this" if you ever need to take a "DecodedThread *" and get ahold of the original std::shared_ptr<DecodedThread>. Is that the case here?

This revision now requires changes to proceed.Jul 7 2021, 2:00 PM
wallace added inline comments.Jul 8 2021, 12:52 PM
lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
121

i'm using it for calling shared_from_this inside DecodedThread.cpp, which is very handy

wallace updated this revision to Diff 357331.Jul 8 2021, 12:54 PM

Addressed all issues

  • Created a TraceInstructionDumper class that keeps the index to print, which leaves the TraceCursor unaffected. It also keeps some other useful state.
clayborg requested changes to this revision.Jul 8 2021, 1:51 PM
clayborg added inline comments.
lldb/source/Commands/CommandObjectThread.cpp
2111

"Thread *" isn't a stable thing to use, I would suggest using the "tid" of the thread. Any reason you are making a map of thread to unique pointers? Can't this just be:

std::map<lldb::tid_t, TraceInstructionDumper> m_dumpers;
lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
41

Shouldn't the m_pt_insn.ip be set to LLDB_INVALID_ADDRESS already?

lldb/source/Target/TraceInstructionDumper.cpp
1

Filename should be TraceInstructionDumper.cpp

22

Seems like the "forwards" and "skip" parameter shouldn't be in this class at all? Just setup the cursor before giving it to this class?

25–30

Remove and have user set this up prior to creating one of these classes?

35

Should we modify the TraceCursor API for Next(...) to be able to take a count? Seems like that might be much more efficient than recursively calling this function?

41

Ditto, should Prev(...) be able to take a count as well?

156–159

to match llvm coding guidelines, add '=' after variable name

167–171

Ditto about adding '=', same as above comment.

291

You should be watching the return value of this right? What if this returns false?

This revision now requires changes to proceed.Jul 8 2021, 1:51 PM
wallace marked 9 inline comments as done.Jul 8 2021, 2:01 PM
wallace added inline comments.
lldb/source/Commands/CommandObjectThread.cpp
2111

I would suggest using the "tid" of the thread -> makes sense

The cursor is a Unique pointer, which renders the TraceInstructionDumper not copyable.

lldb/source/Target/TraceInstructionDumper.cpp
22

the direction is useful to knowing whether to call Next or Prev in the iterations

the skip is also useful to have inside, because that way it's easier to know if all the data has been consumed by the skip. That's stored in the m_no_more_data flag. When a trace has been fully consumed, it's still pointing to the last instruction, so another variable is needed to prevent more iterations

35

that's a pretty good idea

wallace added inline comments.Jul 8 2021, 2:50 PM
lldb/source/Target/TraceInstructionDumper.cpp
291

that's taken care of by

if (m_no_more_data) {
   s.Printf("    no more data\n");
   break;
 }

in the beginning of the iteration

wallace updated this revision to Diff 357371.Jul 8 2021, 3:17 PM

Address comments.

  • I ended up creating new method Next(int count) and Prev(int count) in the cursor for the skip operation. It might be useful for other things. I didn't add the count parameter to the existing Prev and Next methods because it makes it ambiguous because of the
  • I got skip out of the constructor of the Dumper and now the dumper doesn't reset the initial position of the cursor. The cursor is set up outside.
  • Other improvements here and there

Check my current comments before I proceed to look at the rest. Now that we are using the API for something, it is showing some issues with the usability of the API in TraceCursor. Let me know what you think

lldb/include/lldb/Target/TraceCursor.h
85–94

Since we haven't made the trace cursor API public yet, I wonder if this would be a better interface:

virtual void SetGranularity(lldb::TraceInstructionControlFlowType granularity);
virtual void SetIgnoreErrors(bool ignore_errors);
virtual void SetDirection(bool forward);
virtual size_t Move(size_t count);

Then we don't need Next(...) and Prev(...) now since the direction is set via an accessor. The Move(...) function would advance "count" times and it would obey the the granularity that is currently active. It also simplifies all of the arguments that used to be needed to be passed to the Next(...) and Prev(...) functions and gets rid of the default parameters.

94–100

Wondering if we want to replace these with more of a file like seek with a offset and whence?:

enum class SeekType { Set, Current, End };
bool Seek(int64_t offset, SeekType whence);

Then SeekToEnd() would become:

Seek(0, SeekType::End);

And SeekToBegin():

Seek(0, SeekType::Set);

And to skip some instructions:

Seek(10, SeekType::Current); // Advance in the direction by 10 granularity units
Seek(-10, SeekType::Current); // Reverse in the direction by 10 granularity units
97

clarify that this would move "count" times the granularity right?

wallace updated this revision to Diff 359200.Jul 15 2021, 8:29 PM

Followed the recommendations. Now I'm making sure that TraceInstructionDumper uses the state of the Cursor and doesn't modify it, this makes the class reusable for other purposes. Having the direction and granularity as part of the cursor makes it more useful.

I also made the Seek method resemble the fseek function.

wallace marked 13 inline comments as done.Jul 15 2021, 8:31 PM
clayborg requested changes to this revision.Jul 15 2021, 8:46 PM

Very close! Just a few nits.

lldb/include/lldb/Target/TraceCursor.h
77

If there is no other direction other than forward to backward, should this just be a bool? Then we won't have to add the enum to the public API when we expose this.

81

bool? Fine if you think the enum should be used.

101–102

If we get an error, we should return less than the requested amount. If you ask for 10, and only get 7, but get an error, you don't want to return 8. So I would vote for documentation and implementation like:

The number of successful moves that occurred. If the returned value is less than \a count, then check for an error condition by calling XXX. If there is no error and less than \a count was returned, then the cursor is at the end or beginnning of the data.
lldb/source/Target/TraceCursor.cpp
20–35

All these can be moved to the header file. We don't do this in the public API, but we can do this in private classes.

This revision now requires changes to proceed.Jul 15 2021, 8:46 PM
wallace updated this revision to Diff 359471.Jul 16 2021, 4:08 PM
  • Nwo using a single-step iterator method Next
  • Changed the direction to a boolean flag called "forwards"
clayborg accepted this revision.Jul 16 2021, 4:34 PM

Just switch all "-f" to the long option format as mentioned in inlined comments and this is good to go.

lldb/test/API/commands/trace/TestTraceDumpInstructions.py
38–40

use long form of option for clarity

63

use long form of option for clarity

146–147

use long form of option for clarity

165–168

use long form of option for clarity

173–175

use long form of option for clarity

195–196

use long form of option for clarity

340

use long form of option for clarity

lldb/test/API/commands/trace/TestTraceStartStop.py
114–115

use long form of option for clarity

121–122

use long form of option for clarity

152

use long form of option for clarity

This revision is now accepted and ready to land.Jul 16 2021, 4:34 PM
This revision was landed with ongoing or failed builds.Jul 16 2021, 4:47 PM
This revision was automatically updated to reflect the committed changes.