namhyung / uftrace

Function graph tracer for C/C++/Rust/Python
https://uftrace.github.io/slide/
GNU General Public License v2.0
3.05k stars 474 forks source link

tui: Open editor and jump to the definition of function using tags file #401

Open honggyukim opened 6 years ago

honggyukim commented 6 years ago

It'd be very useful if tui mode can open an editor and jump to the definition of the currently pointing function just like vim or emacs.

Since the source location is not supported yet, we can simply provide the list of tag files generated by ctags or global for example.

Even if the source location is supported by dwarf info, it may be difficult to use it for kernel functions. So I think using the tag information can also be very useful.

namhyung commented 6 years ago

Why do you think DWARF is difficult for kernel than tag files?

honggyukim commented 6 years ago

i think sometimes it may be difficult to download debug information supported kernel depending on the environments. One more thing is that if the other user downloads already recorded data from somewhere else, they cannot find the symbol to jump to the source code unless the uftrace.data is fully self-contained.

namhyung commented 6 years ago

Did you mean 'self-contained' by having all source codes? Anyway source could be changed or not exist whether it uses debug info or tag file. So it's hard to guarantee the correctness of the source.

I've just pushed review/dwarf-line-v1 branch including source location info. The tui command shows the info for each graph node.

honggyukim commented 6 years ago

I mean that we also have to include source location of kernel functions in that case. Even if we include the source location of kernel functions, the path may not be exactly the same, so I wanted to provide a way to jump to the source using the tags file (in vim).

I'm not sure about emacs yet but in vim, the editor can open the source with vim -t (function_name).

namhyung commented 6 years ago

Although it doesn't use the tag files, I implemented it to use debug info using O key. It's based on the "review/dwarf-line-v3" branch. Please check below patch:

diff --git a/cmds/tui.c b/cmds/tui.c
index c27b3d94..90ef093f 100644
--- a/cmds/tui.c
+++ b/cmds/tui.c
@@ -7,6 +7,8 @@
 #include <ncurses.h>
 #include <locale.h>
 #include <inttypes.h>
+#include <unistd.h>
+#include <sys/wait.h>

 #include "uftrace.h"
 #include "version.h"
@@ -68,6 +70,7 @@ struct tui_window_ops {
        void (*display)(struct tui_window *win, void *node);
        bool (*search)(struct tui_window *win, void *node, char *str);
        bool (*longest_child)(struct tui_window *win, void *node);
+       struct debug_location * (*location)(struct tui_window *win, void *node);
 };

 struct tui_window {
@@ -1020,7 +1023,7 @@ static void win_footer_graph(struct tui_window *win,
        else {
                struct debug_location *dloc;

-               dloc = find_file_line(&sess->symtabs, node->n.addr);
+               dloc = win->ops->location(win, win->curr);

                if (dloc != NULL && dloc->file != NULL) {
                        snprintf(buf, COLS, "uftrace graph: %s [line:%d]",
@@ -1183,6 +1186,16 @@ static bool win_longest_child_graph(struct tui_window *win, void *node)
        return true;
 }

+static struct debug_location *win_location_graph(struct tui_window *win,
+                                                void *node)
+{
+       struct tui_graph *graph = (struct tui_graph *)win;
+       struct tui_graph_node *curr = node;
+       struct uftrace_session *sess = graph->ug.sess;
+
+       return find_file_line(&sess->symtabs, curr->n.addr);
+}
+
 static const struct tui_window_ops graph_ops = {
        .prev = win_prev_graph,
        .next = win_next_graph,
@@ -1199,6 +1212,7 @@ static const struct tui_window_ops graph_ops = {
        .display = win_display_graph,
        .search = win_search_graph,
        .longest_child = win_longest_child_graph,
+       .location = win_location_graph,
 };

 /* some default (no-op) window operations */
@@ -1325,6 +1339,24 @@ static void win_display_report(struct tui_window *win, void *node)
                printw("%*s", COLS - width, "");
 }

+static struct debug_location *win_location_report(struct tui_window *win,
+                                                 void *node)
+{
+       struct tui_report_node *curr = node;
+       struct tui_graph_node *gnode;
+       struct uftrace_session *sess;
+       struct debug_location *dloc;
+
+       list_for_each_entry(gnode, &curr->head, link) {
+               sess = gnode->graph->sess;
+               dloc = find_file_line(&sess->symtabs, gnode->n.addr);
+
+               if (dloc != NULL && dloc->file != NULL)
+                       return dloc;
+       }
+       return NULL;
+}
+
 static const struct tui_window_ops report_ops = {
        .prev = win_prev_report,
        .next = win_next_report,
@@ -1337,6 +1369,7 @@ static const struct tui_window_ops report_ops = {
        .footer = win_footer_report,
        .display = win_display_report,
        .search = win_search_report,
+       .location = win_location_report,
 };

 /* per-window operations for list window */
@@ -2101,6 +2134,52 @@ static bool tui_window_longest_child(struct tui_window *win)
        return win->ops->longest_child(win, win->curr);
 }

+static bool tui_window_open_editor(struct tui_window *win)
+{
+       struct debug_location *dloc;
+       const char *editor = getenv("EDITOR");
+       struct strv editor_strv;
+       int pid, status;
+
+       if (win->ops->location == NULL)
+               return false;
+
+       dloc = win->ops->location(win, win->curr);
+       if (dloc == NULL || dloc->file == NULL)
+               return false;
+
+       if (editor == NULL)
+               editor = "vi";
+
+       endwin();
+
+       strv_split(&editor_strv, editor, " ");
+       if (!strncmp(editor, "vi", 2) || !strncmp(editor, "emacs", 5)) {
+               char buf[16];
+
+               /* run 'vi +line file' */
+               snprintf(buf, sizeof(buf), "+%d", dloc->line);
+               strv_append(&editor_strv, buf);
+               strv_append(&editor_strv, dloc->file->name);
+       }
+       else {
+               /* I don't know what to do */
+               strv_append(&editor_strv, dloc->file->name);
+       }
+
+       pid = fork();
+       if (pid == 0) {
+               execvp(editor_strv.p[0], editor_strv.p);
+               exit(1);
+       }
+
+       waitpid(pid, &status, 0);
+       strv_free(&editor_strv);
+
+       refresh();
+       return true;
+}
+
 static void tui_window_help(void)
 {
        WINDOW *win;
@@ -2254,6 +2333,9 @@ static void tui_main_loop(struct opts *opts, struct ftrace_file_handle *handle)
                                full_redraw = true;
                        }
                        break;
+               case 'O':
+                       full_redraw = tui_window_open_editor(win);
+                       break;
                case 'c':
                        full_redraw = tui_window_collapse(win);
                        break;
honggyukim commented 6 years ago

Superb! This is what I expected. Thanks for this great work!

honggyukim commented 6 years ago

One minor concern is that I think vim would be better than vi. In some environments, vi is used only the limited features than vim as far as I remember.

namhyung commented 6 years ago

That's a choice for safety. If you want to use vim, you'd be better set it as EDITOR

honggyukim commented 6 years ago

Okay. It's okay then. Thanks!

honggyukim commented 6 years ago

If it's possible, I think we better add -c 'normal z.' as an option to vi to make the focus to the center of editor.

z. Center the screen on the cursor
zt Scroll the screen so the cursor is at the top
zb Scroll the screen so the cursor is at the bottom

https://www.fprintf.net/vimCheatSheet.html

honggyukim commented 6 years ago

Hmm.. thinking it again. Let's leave it now. I think normal vi may be smart enough to place the cursor. I will tell you if it's really needed.

honggyukim commented 6 years ago

There is a bug in the below execution sequence.

  1. open TUI mode and navigate a function that has source location info
  2. open a source code with O key
  3. resize the current terminal
  4. then the output looks messy together with TUI and editor windows
  5. the key binding gets also messy and easily be crashed.
namhyung commented 6 years ago

Could you please check check/tui-fix branch?

honggyukim commented 6 years ago

The above problem is gone with it. Thanks!