-
Notifications
You must be signed in to change notification settings - Fork 108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add API for finding sequences within a line number program and resuming at sequence boundaries #179
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Kyle!
LGTM, would like to get @philipc's opinion as well.
src/line.rs
Outdated
LineNumberProgramHeaderHolder::Complete(_) => { | ||
// All the filenames are already in the header, we don't | ||
// need to do anything (except have this branch to get | ||
// rustc not to warn about an unused 'Complete'). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the commented out version because
- If we add more variants (however unlikely) then the compiler will yell at us to match them
- The comment is nice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment about rustc warning is actually wrong. But sure, I can be explicit here.
src/line.rs
Outdated
/// ``` | ||
pub fn sequences(self) | ||
-> parser::Result<(LineNumberProgramHeader<'input, Endian>, | ||
Vec<LineNumberSequence<'input, Endian>>)> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to let the caller control allocation (maybe they know when they can reuse a vec?) by accepting an &mut Vec<LineNumberSequence<'input, Endian>>
parameter, asserting it is initially empty, and then filling it?
I'm not actually 100% sure if this would actually work or not due to the 'input
lifetime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add something like a next_sequence
method to StateMachine
instead, so that the caller completely controls allocation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the only comment I haven't addressed yet. Having the caller iterate through the sequences runs the risk of producing LineNumberSequence
s that can't actually be used yet if the StateMachine
has not run to completion.
An outparam Vec
doesn't have that problem, but we'd have to be careful to clear the contents of the Vec
if we return early due to a parser error condition.
@@ -881,7 +881,7 @@ pub enum AttributeValue<'input, Endian> | |||
/// different compilation unit from the current one. | |||
DebugInfoRef(DebugInfoOffset), | |||
|
|||
/// An offset into the `.debug_lines` section. | |||
/// An offset into the `.debug_line` section. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
src/line.rs
Outdated
/// ``` | ||
pub fn sequences(self) | ||
-> parser::Result<(LineNumberProgramHeader<'input, Endian>, | ||
Vec<LineNumberSequence<'input, Endian>>)> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add something like a next_sequence
method to StateMachine
instead, so that the caller completely controls allocation.
tests/parse_self.rs
Outdated
.expect("should parse and execute the entire line number program"); | ||
assert!(sequences.len() > 0); // Should be at least one sequence. | ||
for sequence in sequences { | ||
let mut resumed = header.clone().resume_from(&sequence); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to avoid needing to clone the header to iterate the rows in a sequence. So the caller would need to somehow obtain a StateMachine
that already has a Complete
header, and resume on that instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option would be to change the Complete
variant to only use a reference.
src/line.rs
Outdated
// in a row. | ||
start: sequence_start_addr.unwrap_or(0), | ||
end: sequence_end_addr, | ||
rest: state_machine.opcodes.clone(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the opcodes from after the sequence? Also, it would be nice to limit it to only the opcodes for the sequence (so reduce the size of the input buffer to cover only the sequence).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Er, yes, good catch.
Take a look at this one. I'm pretty happy with it, except that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, just one nit.
Regarding resume
on an incomplete iterator, I think it's fine, but if you really wanted to do something then a debug_assert would be better than returning an error.
src/line.rs
Outdated
self.input.len(), | ||
other.input.as_ptr(), | ||
other.input.len(), | ||
unsafe { self.input.as_ptr().offset(self.input.len() as isize)}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete this debug print.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, thanks.
…ng at sequence boundaries. Fixes gimli-rs#178.
I think I might want to switch the state machine to using references to the header though. As is you have to clone it to use it in a multithreaded environment, which means cloning the header. So don't merge this yet while I sleep on it :) |
Ok, I ended up changing the This allows me to make |
I like the concept of a trait for I'm not sure about making Changing |
src/line.rs
Outdated
@@ -96,32 +97,102 @@ impl<'input, Endian> From<&'input [u8]> for DebugLine<'input, Endian> | |||
} | |||
} | |||
|
|||
/// Shutup | |||
pub trait LineNumberProgramHeaderHolder<'input, Endian> : Deref<Target = LineNumberProgramHeader<'input, Endian>> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its generally frowned upon to Deref
but also add methods. Generally a thing should be a magic pointer and only deref, or if it must also have its own methods, then it should use As[Mut]Ref
instead of Deref
. The reasoning is that code becomes harder to read with the "fake inheritence" that Deref
provides, and method resolution is obscured as well.
So I would prefer:
pub trait LineNumberProgramHeaderHolder<'input, Endian> : AsRef<LineNumberProgramHeader<'input, Endian>>
src/line.rs
Outdated
where Endian: Endianity | ||
{ | ||
fn add_file(&mut self, _: FileEntry<'input>) { | ||
// Nop |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps with debug assertions here?
src/line.rs
Outdated
where Endian: Endianity | ||
#[derive(Debug)] | ||
struct StateMachineInner<'input, Holder, Endian> | ||
where Holder: LineNumberProgramHeaderHolder<'input, Endian>, Endian: Endianity |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect rustfmt
will put Endian: Endianity
on a new line.
src/line.rs
Outdated
self.row.registers.basic_block = false; | ||
self.row.registers.prologue_end = false; | ||
self.row.registers.epilogue_begin = false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did rustfmt
add this indentation??
src/line.rs
Outdated
addr: &u64) | ||
-> parser::Result<Option<(&LineNumberProgramHeader<'input, Endian>, &LineNumberRow)>> { | ||
self.inner.run_to_address(addr) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since both StateMachine
and ResumableStateMachine
have {header, next_row, run_to_address} methods, it could be nice to define them in a trait and consolidate the documentation in one place.
Either way works for me.
+1 |
Using an owned header in one variant and immutable references in the other isn't feasible without using |
Can you change |
Yeah, that's a good idea. I did essentially that, with some of the types renamed. Lmk what you think of this version. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is looking great! Just a few small changes please.
src/line.rs
Outdated
/// A `LineNumberProgramHolder` provides access to a `LineNumberProgramHeader` and | ||
/// a way to add files to the files table if necessary. Gimli consumers should | ||
/// never need to use or see this trait. | ||
pub trait LineNumberProgramHolder<'input, Endian> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename this trait to LineNumberProgram
.
src/line.rs
Outdated
/// files defined by the line number program header to be added to it. Gimli consumers | ||
/// should never need to use or see this trait. | ||
#[derive(Debug)] | ||
pub struct IncompleteLineNumberProgramHolder<'input, Endian> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete this struct, it adds no benefit. Replace its usage with IncompleteLineNumberProgram
.
src/line.rs
Outdated
/// files defined by the line number program header to be added to it. Gimli consumers | ||
/// should never need to use or see this trait. | ||
#[derive(Debug, Clone)] | ||
pub struct CompleteLineNumberProgramHolder<'program, 'input, Endian> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete this struct, it adds no benefit. Replace its usage with &'program CompleteLineNumberProgram
.
src/line.rs
Outdated
pub struct StateMachine<'input, Endian> | ||
where Endian: Endianity | ||
#[derive(Debug, Clone)] | ||
pub struct StateMachine<'input, Holder, Endian> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename Holder
to Program
.
src/line.rs
Outdated
/// Construct a new `StateMachine` for executing line programs and | ||
/// generating the line information matrix. | ||
pub fn rows(self) -> OneShotStateMachine<'input, Endian> { | ||
StateMachine::<IncompleteLineNumberProgramHolder<_>, _>::new(IncompleteLineNumberProgramHolder { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change to OneShotStateMachine::new(self)
src/line.rs
Outdated
-> ResumedStateMachine<'program, 'input, Endian> { | ||
StateMachine::<CompleteLineNumberProgramHolder<_>, _>::resume(CompleteLineNumberProgramHolder { | ||
program: self, | ||
}, sequence) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change to ResumedStateMachine::resume(self, sequence)
Review comments largely addressed. I'm not sure whether the |
Why did you need to add the enum back in? That wasn't what I intended. The changes I requested should have only been a handful of lines changed. eg when you delete |
I need to hold an |
But the point of the trait was to enable |
Ah, I didn't realize you meant to implement Changes are folded in, and this should make |
LineNumberProgramHeader is now purely concerned with the header data (and file table). Incomplete/CompleteLineNumberProgram are introduced to create StateMachines. What was LineNumberProgramHeader::rows() is now IncompleteLineNumberProgram::rows(). IncompleteLineNumberProgram::sequences() produces a (CompleteLineNumberProgram, Vec<LineNumberSequence>). And CompleteLineNumberProgram::resume_from(sequence) produces a state machine that iterates through one of those sequences. StateMachine is parameterized over the LineNumberProgram trait, implemented by both IncompleteLineNumberProgram and &CompleteLineNumberProgram, depending on its origins. Both implement Clone (which means StateMachine is still always cloneable) and implement add_file as needed. Finally, DebugLines::header is now DebugLines::program and returns an IncompleteLineNumberProgram.
@fitzgen Did you want to take another look before merging? |
Nope, I've been following along without commenting (too many cooks in the kitchen), and it looks good to me. Most recent changes A+++ Ship it! |
Wait for Travis before merging, I don't have nightly rustc installed so I don't know for sure that |
Well, Travis seems to be having a bad time today. |
The Travis error is an unrelated error due to trying to parse a Thanks for the PR! |
Thanks for the reviews! |
Fixes #178.
Something like this perhaps? I haven't actually tried modifying
addr2line
to use it yet.