Skip to content
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

Use caller method name from ruby stack #78

Merged
merged 4 commits into from
Mar 19, 2019

Conversation

dylanahsmith
Copy link
Contributor

Problem

We have been seeing more problems with the caller location not matching the entity and method name, beyond the issues already reported in #57 and #59.

Solution

Since these problems seem to stem from trying to keep a mirror of the stack in sync with the ruby stack, we should just try to rely on the ruby stack itself. This PR does that, but using rb_profile_frames to get the method name and to check if it is a singleton method.

There is a notable change to the tests due to rb_profile_frames skipping C frames to get the next ruby frame. This means that calls from C code show up as calls to the ruby code that called that C code. However, this is also more consistent with the filename and line number that was being used for the caller.

If this makes sense, then I think we should also deprecate the caller entity then remove that in a follow-up PR.

  • bin/fmt was successfully run

as well as determining whether or not it was an instance method.

The reason for this is that the rotoscope stack isn't as reliable.

rs_callsite_t c_callsite(rb_trace_arg_t *trace_arg) {
VALUE path = rb_tracearg_path(trace_arg);
int line;
VALUE frame = caller_frame(&line, false);
Copy link
Contributor

Choose a reason for hiding this comment

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

You are using pass-by-reference essentially to be able to return two things from caller_frame, the frame and the line. Correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct

return (rs_callsite_t){
.filepath = path, .lineno = FIX2INT(rb_tracearg_lineno(trace_arg)),
.filepath = rb_tracearg_path(trace_arg),
.lineno = FIX2INT(rb_tracearg_lineno(trace_arg)),
Copy link
Contributor

Choose a reason for hiding this comment

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

is this different from line?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question. I tried to continue using the tracepoint functions (i.e. rb_tracearg_*) to minimize how much is changing at once. However, I do think they should be the same and would like to have this file only use rb_profile_frames in a follow-up PR.

.filepath = rb_tracearg_path(trace_arg),
.lineno = FIX2INT(rb_tracearg_lineno(trace_arg)),
.method_name = rb_profile_frame_method_name(frame),
.singleton_p = rb_profile_frame_singleton_method_p(frame),
};
}

rs_callsite_t ruby_callsite() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a minimal comment explaining the difference between ruby_callsite and c_callsite?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would like to get rid of them as two separate code paths, but I think the bool ruby_call parameter still deserves documenting.

The reason they diverged historically was because rb_tracearg_ API for the callsite was working for C but not ruby. That tracepoint API was trying to returning the code location just after the call (i.e. inside the called function) but it would also skip non-ruby stack frames. rb_profile_frames replaced some earlier dirty hack we had isolated to ruby_callsite and allows us to get more than just the current code location, allowing us to use index 0 to continue getting the correct location for C but 1 to skip a frame and get the caller location for ruby.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a comment to caller_frame on this key difference.

if call.caller_method_name.nil?
caller_class_name = '<ROOT>'
Copy link
Contributor

Choose a reason for hiding this comment

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

How is this change related?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When tracing just starts, we won't know the caller class name and didn't know the caller method name. However, now that we are using the ruby stack to get the caller method name, these two are orthogonal, so we can assume that just because we don't have a caller method name that we do have a class name. As such, I needed to move this out of this conditional block.

The rename of the default value isn't as significant. It is just a bit confusing to say that it is the root when thinking in the context of a ruby stack, so '<UNKNOWN>' seems like a more appropriate default value.

// causes the start argument to effectively always
// act as if it were 0, so we need to also get the top
// frame. (https://bugs.ruby-lang.org/issues/14607)
rb_profile_frames(0, frame_index + 1, frames, lines);
Copy link
Contributor

Choose a reason for hiding this comment

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

For future reference: rb_profile_frames is currently undocumented, but appears ina a header file https://github.com/ruby/ruby/blob/6f2c516d8031e1c7f4704bd59c5349fab7f24187/include/ruby/debug.h#L26

Copy link
Contributor

@jahfer jahfer left a comment

Choose a reason for hiding this comment

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

This looks great! No concerns from me. Apologies for the delay 🤐

@exterm
Copy link
Contributor

exterm commented Mar 19, 2019

:shipit:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants