如何排查 Tramp 卡住问题
文章目录
今天在进行一个操作时,突然出现了访问 tramp 的操作,最近有一段时间没有使用过,所以看到这个消息时比较好奇,是什么操作导致触发了 tramp。
之前有些排查 tramp 卡住的经验,知道一些看似无关的函数在调用时,会去访问已经打开的 tramp buffer,比如 file-truename ,这次又是什么函数触发了呢?
首先调高 tramp 日志级别 (setq tramp-verbose 10) ,之后重复会触发 tramp 的操作,这时 minibuffer 中应该会有类似下面的输出:
1Tramp: Opening connection nil for dev using ssh...由于 dev 已经关机,所以这里是连接不上的,此时可以 C-g 将当前操作强制取消,然后去找 *debug tramp/ssh dev* 的 buffer,这个名字中的 dev 是我 ssh config 中一台机器的别名,翻到这个 buffer 的最后面,会有触发 tramp 的调用链,如下:
120:23:33.548536 tramp-recentf-cleanup (10) #
2 backtrace()
3 tramp-error((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) wrong-type-argument "listp abbreviate-file-name")
4 tramp-signal-hook-function(wrong-type-argument (listp abbreviate-file-name))
5 recentf-apply-filename-handlers("~/.config/emacs/deps.el")
6 recentf-expand-file-name("~/.config/emacs/deps.el")
7 recentf-cleanup()
8 tramp-recentf-cleanup((tramp-file-name "ssh" nil nil "dev" nil "~/" nil))
9 run-hook-with-args(tramp-recentf-cleanup (tramp-file-name "ssh" nil nil "dev" nil "~/" nil))
10 tramp-cleanup-connection((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) t)
11 tramp-maybe-open-connection((tramp-file-name "ssh" nil nil "dev" nil "~/" nil))
12 tramp-send-command((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) "echo ~ 2>/dev/null; echo tramp_exit_status $?")
13 tramp-send-command-and-check((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) "echo ~")
14 tramp-sh-handle-get-home-directory((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) "")
15 apply(tramp-sh-handle-get-home-directory ((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) ""))
16 tramp-sh-file-name-handler(tramp-get-home-directory (tramp-file-name "ssh" nil nil "dev" nil "~/" nil) "")
17 apply(tramp-sh-file-name-handler tramp-get-home-directory ((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) ""))
18 tramp-file-name-handler(tramp-get-home-directory (tramp-file-name "ssh" nil nil "dev" nil "~/" nil) "")
19 tramp-get-home-directory((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) "")
20 tramp-sh-handle-expand-file-name("/ssh:dev:~/" nil)
21 apply(tramp-sh-handle-expand-file-name ("/ssh:dev:~/" nil))
22 tramp-sh-file-name-handler(expand-file-name "/ssh:dev:~/" nil)
23 apply(tramp-sh-file-name-handler expand-file-name ("/ssh:dev:~/" nil))
24 tramp-file-name-handler(expand-file-name "/ssh:dev:~/" nil)
25 expand-file-name("/ssh:dev:~/")
26 read-file-name-default("Use file /dev/null: " "/dev/" "/dev/null" t "null" nil)
27 read-file-name("Use file /dev/null: " "/dev/" "/dev/null" t "null")
28 diff-find-file-name()
29 diff-add-log-current-defuns()
30 log-edit-generate-changelog-from-diff()
31 funcall-interactively(log-edit-generate-changelog-from-diff)
32 call-interactively(log-edit-generate-changelog-from-diff nil nil)
33 command-execute(log-edit-generate-changelog-from-diff)从中可以看到, log-edit-generate-changelog-from-diff 是我主动调用的函数,它间接触发了 tramp 操作,其中关键的两行日志是:
1 expand-file-name("/ssh:dev:~/")
2 read-file-name-default("Use file /dev/null: " "/dev/" "/dev/null" t "null" nil)expand-file-name 的输入是一个 tramp 文件地址,这个是怎么来的呢?从 read-file-name-default 的代码来看,是这么调用来的:
1 (abbreviate-file-name "/dev/")为什么 /dev/ 在进行缩写时,会得到 "/ssh:dev:~/" 这个文件地址呢?
问题排查到这里,其实可以继续看 abbreviate-file-name 的源码,但是这时我脑子中隐约对这个 ssh 地址有些印象,因为 dev 是我日常开发中需要经常用到的一台 Linux 机器,为了方便,我之前定义了这么一个缩写:
1(setq directory-abbrev-alist '(("^/dev" . "/ssh:dev:~")))这样的话我可以通过在 find-file 中,直接输入 /dev 来直接打开 tramp 链接。
所以到这里问题基本上就清楚了,是 abbreviate-file-name 在内部使用了 directory-abbrev-alist ,所以导致了本文的问题,解决方法也很简单,直接从 directory-abbrev-alist 去掉这个选项好了,用的也不是很多,而且通过 /ssh:dev 来打开也不是很麻烦。
希望通过本文的这个案例分享,让读者了解如何排查 tramp 相关的问题。
PS:至于为何
log-edit-generate-changelog-from-diff为什么会去访问/dev/,这就是另一个问题了,感兴趣的同学可以从对照上面的堆栈,继续查看源码。
收听方式

反馈
- 对节目有想法或发现内容错误?欢迎来信交流️