今天在进行一个操作时,突然出现了访问 tramp 的操作,最近有一段时间没有使用过,所以看到这个消息时比较好奇,是什么操作导致触发了 tramp。
之前有些排查 tramp 卡住的经验,知道一些看似无关的函数在调用时,会去访问已经打开的 tramp buffer,比如 file-truename
,这次又是什么函数触发了呢?
首先调高 tramp 日志级别 (setq tramp-verbose 10)
,之后重复会触发 tramp 的操作,这时 minibuffer 中应该会有类似下面的输出:
Tramp: Opening connection nil for dev using ssh...
由于 dev 已经关机,所以这里是连接不上的,此时可以 C-g
将当前操作强制取消,然后去找 *debug tramp/ssh dev*
的 buffer,这个名字中的 dev 是我 ssh config 中一台机器的别名,翻到这个 buffer 的最后面,会有触发 tramp 的调用链,如下:
20:23:33.548536 tramp-recentf-cleanup (10) #
backtrace()
tramp-error((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) wrong-type-argument "listp abbreviate-file-name")
tramp-signal-hook-function(wrong-type-argument (listp abbreviate-file-name))
recentf-apply-filename-handlers("~/.config/emacs/deps.el")
recentf-expand-file-name("~/.config/emacs/deps.el")
recentf-cleanup()
tramp-recentf-cleanup((tramp-file-name "ssh" nil nil "dev" nil "~/" nil))
run-hook-with-args(tramp-recentf-cleanup (tramp-file-name "ssh" nil nil "dev" nil "~/" nil))
tramp-cleanup-connection((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) t)
tramp-maybe-open-connection((tramp-file-name "ssh" nil nil "dev" nil "~/" nil))
tramp-send-command((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) "echo ~ 2>/dev/null; echo tramp_exit_status $?")
tramp-send-command-and-check((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) "echo ~")
tramp-sh-handle-get-home-directory((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) "")
apply(tramp-sh-handle-get-home-directory ((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) ""))
tramp-sh-file-name-handler(tramp-get-home-directory (tramp-file-name "ssh" nil nil "dev" nil "~/" nil) "")
apply(tramp-sh-file-name-handler tramp-get-home-directory ((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) ""))
tramp-file-name-handler(tramp-get-home-directory (tramp-file-name "ssh" nil nil "dev" nil "~/" nil) "")
tramp-get-home-directory((tramp-file-name "ssh" nil nil "dev" nil "~/" nil) "")
tramp-sh-handle-expand-file-name("/ssh:dev:~/" nil)
apply(tramp-sh-handle-expand-file-name ("/ssh:dev:~/" nil))
tramp-sh-file-name-handler(expand-file-name "/ssh:dev:~/" nil)
apply(tramp-sh-file-name-handler expand-file-name ("/ssh:dev:~/" nil))
tramp-file-name-handler(expand-file-name "/ssh:dev:~/" nil)
expand-file-name("/ssh:dev:~/")
read-file-name-default("Use file /dev/null: " "/dev/" "/dev/null" t "null" nil)
read-file-name("Use file /dev/null: " "/dev/" "/dev/null" t "null")
diff-find-file-name()
diff-add-log-current-defuns()
log-edit-generate-changelog-from-diff()
funcall-interactively(log-edit-generate-changelog-from-diff)
call-interactively(log-edit-generate-changelog-from-diff nil nil)
command-execute(log-edit-generate-changelog-from-diff)
从中可以看到, log-edit-generate-changelog-from-diff
是我主动调用的函数,它间接触发了 tramp 操作,其中关键的两行日志是:
expand-file-name("/ssh:dev:~/")
read-file-name-default("Use file /dev/null: " "/dev/" "/dev/null" t "null" nil)
expand-file-name
的输入是一个 tramp 文件地址,这个是怎么来的呢?从 read-file-name-default
的代码来看,是这么调用来的:
(abbreviate-file-name "/dev/")
为什么 /dev/
在进行缩写时,会得到 "/ssh:dev:~/"
这个文件地址呢?
问题排查到这里,其实可以继续看 abbreviate-file-name
的源码,但是这时我脑子中隐约对这个 ssh 地址有些印象,因为 dev 是我日常开发中需要经常用到的一台 Linux 机器,为了方便,我之前定义了这么一个缩写:
(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/
,这就是另一个问题了,感兴趣的同学可以从对照上面的堆栈,继续查看源码。