-
-
Notifications
You must be signed in to change notification settings - Fork 453
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 Backtrace Screen #1270
base: main
Are you sure you want to change the base?
Add Backtrace Screen #1270
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.
Thank you very much for your PR. The feature itself looks interesting and fits quite nicely with existing functionality.
But, the use of external tools is a bit problematic and is best to be avoided. For the case of retrieving stack traces, there are several libraries available, that might be worth a look (e.g. libunwind
). Also I'm not sure if the code as-is would properly run on e.g. FreeBSD or Darwin. Thus I strongly prefer a solution that allows to split out these platform-dependent parts where necessary (in the case of lsof
, things are portable enough across all our target platforms so it's not an issue there; not sure about eu-stack
though).
While reading through your PR I noticed that this seems to handle debug information. As such, it would be nice to have the module, source file and line be available separately (where available). Also the setting of hiding path names should be respected for module filenames in order to be consistent with the rest of the UI. Taking the highlight basename setting into account would be nice too.
Another code refactoring task is related to a recent addition of the generalized columns code that @natoscott recently worked on. Please have a look there if you like.
Also there's a few further notes regarding the code which you can find below. Please feel free to rebase the fixes to those issues directly into the existing commits as you see fit.
If you need further assistance feel free to ask.
Thank you for your reactivity and your review. I agree with you when you say Moreover, I think also the library I saw very quickly the work of @natoscott and I will wait until his work is finished. |
You're welcome.
There seems to be some patches re FreeBSD, but I'd not actually call this support … ;-) That's with Darwin aside entirely … ;-)
Sure, go ahead. Maybe check for similar places elsewhere in case something similar was missed in other places of this PR.
If you prepare your PR to anticipate that change, e.g. by preparing the data structures in a way that makes this move easy, you could even prepare things now. Given the remaining aspects in that other PR will take some time to sort out, don't feel obliged to wait for those changes to land. If the structures introduced in this PR are clean enough there's not too much work later to move things over to this new interface. Most of the work actually has already been done. |
Please don't merge the |
The flake commit is just for me. I will remove it at the end. |
Hey, I didn't have a lot of time recently for this PR. But I have some questions.
I'm looking for |
The |
Thank you very much for the clarification. I was not sure I would be able to modify it (I mean If my changes would be accepted). |
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.
Please no custom build system files from e.g. Flake …
Hi @BenBE , |
Don't worry. No need to apologize. The PR is marked as draft anyway, thus the only things I remarked upon was what I noticed immediately. I also ticked off some of the previous comments that no longer apply to the current state of this PR. Basically some maintenance. NB: This PR would be scheduled for 3.4.x earliest anyway given that 3.3.0 is about to ship anytime soon. |
Hi, This PR is ready for review. Currently, I add only the support of Linux. |
c53af06
to
792bdba
Compare
Given that this feature will be available on multiple platforms and requires changes in different places I'd suggest to have a top-level feature flag for backtrace support and additional flags for the various libraries (iberty, eu-stack, …) The actual implementation for the stack traces should go in the platform code for each system, leaving the actual screen implementation mostly platform independent. |
Ok I see what you mean, is it a good idea to be inspired by for example |
Maybe a better candidate may be the way in which |
Okay thank you, I'll take a look |
Hello, I've improved the option used by |
c9b2068
to
5e79cdb
Compare
2fc69d5
to
05dd74b
Compare
@@ -681,6 +695,115 @@ bool Platform_getNetworkIO(NetworkIOData* data) { | |||
return true; | |||
} | |||
|
|||
void Platform_getBacktrace(Vector* frames, const BacktracePanel* panel, char** error) { |
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 wouldn't hurt to initialize *error
at the start of the function.
void Platform_getBacktrace(Vector* frames, const BacktracePanel* panel, char** error) { | |
void Platform_getBacktrace(Vector* frames, const BacktracePanel* panel, char** error) { | |
*error = NULL; | |
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.
What do you think about adding an assert that checks if *error
is not null? Is it a good idea?
assert(*error != NULL)
The goal is to prevent an error on another error.
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 issue with output parameters is always how you handle pre-initialized values. As there is just a single caller, an assert(error && !*error);
should be fine.
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 point is, you are using the error
parameter for output only, not for input. It should be a good practice for the callee to initialize the output value to something stable. Expecting the caller to do it is more prone to errors. And notice that you only call this function in one place, that extra line of initialization shouldn't add any extra code from the compiler when it inlines it.
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.
@Explorer09 I agree with adding the initializer inside the function. My point with the assert
covering both error
and *error
is about catching a mal-behaved caller that passes some non-NULL value for the error return value.
An alternative would be change the API for void Platform_getBacktrace(Vector* frames, const BacktracePanel* panel, char** error);
to char* Platform_getBacktrace(Vector* frames, const BacktracePanel* panel);
(ignoring my comment below regarding the BacktracePanel*
for a moment).
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 note. I believe the whole function should be wrapped in a #ifdef HAVE_BACKTRACE
... #endif
conditional.
Also we can replace the "The backtrace screen is not implemented"
dummy screen and dummy message with a compule time error (#error "Backtrace is enabled without engine selected."
).
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.
My point with the
assert
covering botherror
and*error
is about catching a mal-behaved caller that passes some non-NULL value for the error return value.
I was looking at asprintf(3)
that would return the output string in a similar way. If asprintf
doesn't need an assertion for this, we won't need it either.
assert(error)
can be covered by ATTR_NONNULL
to the whole function. assert(!*error)
won't do really much here, because whether *error
is a malloc
'd buffer should be tracked by the compiler through other means.
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.
An alternative would be change the API for void
Platform_getBacktrace(Vector* frames, const BacktracePanel* panel, char** error);
tochar* Platform_getBacktrace(Vector* frames, const BacktracePanel* panel);
(ignoring my comment below regarding theBacktracePanel*
for a moment).
I'm not a fan of this alternative. I chose char **error
because that indicates to the developer this variable is for error. I found this pattern when I learned Objective-C and NSError
. Returning char *
that returns an error, I think, it's not very clear on the data returned.
Also we can replace the "The backtrace screen is not implemented" dummy screen and dummy message with a compule time error (
#error "Backtrace is enabled without engine selected.")
.
I don't understand the compute time error. If during the configuration, the backtrace
feature is disabled, Does it produce a compute time error? However, it's a good suggestion to change the message...
I just realized that I prefer that error
should be an optional parameter. If the developer that uses Plateform_getBacktrace
doesn't want to check the error, it's his choice. But it involves checking if error
is null.
Currently, it's easier to add an assert, it will make the code more readable than adding an if
to every error set.
Co-authored-by: Benny Baumann <[email protected]> Co-authored-by: Kang-Che Sung <[email protected]>
Co-authored-by: Benny Baumann <[email protected]> Co-authored-by: Kang-Che Sung <[email protected]>
Co-authored-by: Benny Baumann <[email protected]> Co-authored-by: Kang-Che Sung <[email protected]>
Co-authored-by: Benny Baumann <[email protected]> Co-authored-by: Kang-Che Sung <[email protected]>
Co-authored-by: Benny Baumann <[email protected]> Co-authored-by: Kang-Che Sung <[email protected]>
Looking at the source a bit closer, I noticed something that is kinda bad from an architecture PoV: For |
frame->offset = offset; | ||
frame->isSignalFrame = unw_is_signal_frame(&cursor); | ||
|
||
frame->functionName = xStrndup(procName, 2048); |
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.
One thing to be verified: Does unw_get_proc_name
always NUL-terminate the string even when the length of the procName
string to print is exactly 2048 bytes?
This is to prevent a potential off-by-one error. And I don't like length coded as a magic number here (should have been using sizeof()
).
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.
One thing to be verified: Does unw_get_proc_name always NUL-terminate the string even when the length of the procName string to print is exactly 2048 bytes?
It's a good question. And I just checked. Yes, the unw_get_proc_name
null terminates the string.
Moreover, writing a sizeof
here is a good suggestion. Thank you!
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.
@MrCirdo According to my tracing just now:
- src/mi/Gget_proc_name.c: line 100
unw_get_proc_name
calls - src/mi/Gget_proc_name.c: line 49
unw_get_proc_name_by_ip
unw_get_proc_name_by_ip
has two paths. If it goes to intern_string
(src/mi/Gget_proc_name.c line 30), then we can ensure the string is null terminated. But there is another path that calls *(unw_accessors_t *)a->get_proc_name
(a function pointer), and the path would go to...
- src/coredump/_UCD_accessors.c: line 35
_UCD_accessors.get_proc_name = _UCD_get_proc_name
- src/coredump/_UCD_get_proc_name.c: line 131
_UCD_get_proc_name
calls - src/coredump/_UCD_get_proc_name.c: line 83
elf_w (CD_get_proc_name)
calls - src/elfxx.c: line 593
elf_w (get_proc_name_in_image)
calls - src/elfxx.c: line 337
elf_w (lookup_symbol)
calls - src/elfxx.c: line 313
elf_w (lookup_symbol_callback)
calls strncpy
from libc (strncpy
does not guarantee the string is always null terminated.)
EDIT: My mistake. I missed the line d->buf[d->buf_len - 1] = '\0';
just below elf_w (lookup_symbol_callback)
. So the string can be safely assumed to be null terminated.
if (unw_get_elf_filename(&cursor, elfFileName, sizeof(elfFileName), &offsetElfFileName) == 0) { | ||
frame->objectPath = xStrndup(elfFileName, 2048); | ||
|
||
char *lastSplash = strrchr(frame->objectPath, '/'); |
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 thought you mean "slash".
char *lastSplash = strrchr(frame->objectPath, '/'); | |
char* lastSlash = strrchr(frame->objectPath, '/'); |
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.
Yes 😄 . Thank you!
The goal of passing a |
Oversight on my side. The prototype for My point regarding architecture was publishing the |
Hello everyone!
This is my first big pull request 😃
The goal of this PR is to add a backtrace screen for process or thread.

Here's what it looks like for a thread :
And for a process:

Behind, I use the tool called
eu-stack
provided by elf-utils.The standard output is parsed and printed to the screen.
Currently, I have implemented only the
Refresh
button. And my world is inspired byTraceScreen
andOpenFilesScreen
.I still have some work to do before my work is ready (Formatting, bug fixes, ...).
Currently, this is more of a demonstration than a cool feature 😄 .
What do you think about my work? Is it a feature that can be added?