Try   HackMD

coc.nvim で Language Server のログを確認する方法

結論

coc-settings.json に以下の設定を追記します(ここでは clangd を例にしています)。これを追記することで、Language Server のログを全て出力するようにします。

{
  "clangd.arguments": ["--log", "verbose"],
  "clangd.trace.server": "verbose"
}

次に、以下の Vim コマンドを入力します。

:CocCommand workspace.showOutput

すると、Language Server のリストが現れるので、clangd を選択すると、clangd の出力を見ることができます。

他の Language Server の出力も同様の操作で確認することができます。

前提

Neovim と coc.nvim

僕は普段エディタとして Neovim を利用しており、coc.nvim を利用して Nodejs プラグインの管理を行なっています。coc.nvim を利用することで、例えば VSCode で利用されている Nodejs で書かれたプラグインを、少ない労力で Neovim で利用できるものにすることができます。また、coc.nvim は Language Server のクライアントとして動作するので、既存の Language Server を Neovim で利用することができます。

coc-clangd

C/C++ のプログラムの読み書きをする際、coc-clangd というものを利用しています。これは、clangd の Language Server を Neovim から利用可能にする coc.nvim プラグインです。

問題との遭遇

とある OSS プロジェクトを coc-clangd を利用して開いた際に、Neovim を開いてから 10 秒程度経過した時に、突然 coc-clangd が動作を停止してしまう現象に遭遇しました。何度 Neovim を開きなおしても同じ現象が起きました。

試したこと

coc.nvim のログを確認

とりあえずログを見ようと、:CocOpenLog コマンドを打ちました。これは、coc.nvim のログファイルを開くコマンドです。すると、coc-clangd がクラッシュしたであろうところに以下のようなログがありました。

2022-03-23T00:55:00.128 INFO (pid:50517) [services] - clangd state change: running => stopped

どうやら clangd が動作を突然停止したため、coc-clangd 自体が動作を停止したようです。しかし、このログからはなぜ clangd が動作を停止したか分かりません。clangd のログを確認する方法を探ります。

coc-clangd の設定変更

coc-clangd では clangd に渡すコマンドを coc-settings.json の clangd.arguments で設定することができます。また、clangd には --log verbose という引数を渡すことで、標準エラー出力に詳細なログを吐くように設定することができます。そこで、clangd.arguments: ["--log", "verbose"] という設定を行い、Neovim を再起動してみました。しかし、:CocOpenLog でログを確認したところ、それらしき出力は見当たりませんでした。どうやら、Language Server のログは :CocOpenLog からは確認できないようです。困りました。

Language Server のプロセスから出力を盗む

本来こういう試行は最終手段として利用するものですが、なぜか早い段階で思いついてしまいました。結論から言うと、今回の問題は MacOS で発生したことから、MacOS には procfs や strace などのプロセスを解析するための方法が少ないため、結局この試行は失敗しました。とりあえず、初心に戻って coc.nvim のドキュメントを読むことにしました。

coc.nvim のドキュメントを調査

coc.nvim には以下のようにドキュメントが複数あります。

これらのドキュメントを確認していくと、どうやらそれぞれの Language Server は output channel なるものをもっており、そこにログやエラーの出力を吐いているようです。

help ページでは :help coc-config-languageserver の章に、Wiki では Debug language server - Using output channel の章に、output channel を確認する方法が書いてありました。

解決方法

まずはデフォルトだと Language Server のログ出力は off になっています。そのため、coc-settings.json に以下のような設定を加え、LSP のコミュニケーションを含め、Languge Server の出力を output channel に流すようにします。

"clangd.trace.server": "verbose"

そして :CocCommand workspace.showOutput を用いて、Language Server のログを確認します。コマンドを打った後、Language Server のリストが出てくるので、clangd を選択します。

すると、以下のような clangd がクラッシュした時のログを確認することができました!やった〜

LLVM ERROR: SmallVector unable to grow. Requested capacity (18446744073709551614) is larger than maximum value for size type (4294967295)
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  clangd                   0x0000000102c868b7 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 39
1  clangd                   0x0000000102c858f6 llvm::sys::RunSignalHandlers() + 198
2  clangd                   0x0000000102c86f70 SignalHandler(int) + 272
3  libsystem_platform.dylib 0x00007ff8166aee2d _sigtramp + 29
4  libsystem_platform.dylib 0x6b732f73736f2f00 _sigtramp + 7742443653779702000
5  libsystem_c.dylib        0x00007ff8165e5d10 abort + 123
6  clangd                   0x0000000102bf33e2 llvm::report_fatal_error(llvm::Twine const&, bool) + 434
7  clangd                   0x0000000102bf340b llvm::report_fatal_error(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool) + 27
8  clangd                   0x0000000102c28747 report_size_overflow(unsigned long, unsigned long) + 199
9  clangd                   0x0000000102c2857e llvm::SmallVectorBase<unsigned int>::grow_pod(void*, unsigned long, unsigned long) + 222
10 clangd                   0x0000000102c970e0 llvm::opt::InputArgList::InputArgList(char const* const*, char const* const*) + 192
11 clangd                   0x0000000102c9ad5f llvm::opt::OptTable::ParseArgs(llvm::ArrayRef<char const*>, unsigned int&, unsigned int&, unsigned int, unsigned int) const + 47
12 clangd                   0x0000000103ac9908 clang::clangd::CommandMangler::adjust(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, llvm::StringRef) const + 552
13 clangd                   0x0000000103ad52b7 std::__1::__function::__func<clang::clangd::CommandMangler::operator std::__1::function<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > (std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, llvm::StringRef)>() &&::$_4, std::__1::allocator<clang::clangd::CommandMangler::operator std::__1::function<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > (std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, llvm::StringRef)>() &&::$_4>, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > (std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, llvm::StringRef)>::operator()(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, llvm::StringRef&&) + 215
14 clangd                   0x0000000103b42df4 clang::clangd::OverlayCDB::getCompileCommand(llvm::StringRef) const + 468
15 clangd                   0x0000000103cece40 std::__1::__function::__func<clang::clangd::BackgroundIndex::indexFileTask(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)::$_3, std::__1::allocator<clang::clangd::BackgroundIndex::indexFileTask(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)::$_3>, void ()>::operator()() + 336
16 clangd                   0x0000000103cf1384 clang::clangd::BackgroundQueue::work(std::__1::function<void ()>) + 420
17 clangd                   0x0000000103ceb347 void llvm::detail::UniqueFunctionBase<void>::CallImpl<clang::clangd::BackgroundIndex::BackgroundIndex(clang::clangd::ThreadsafeFS const&, clang::clangd::GlobalCompilationDatabase const&, llvm::unique_function<clang::clangd::BackgroundIndexStorage* (llvm::StringRef)>, clang::clangd::BackgroundIndex::Options)::$_1>(void*) + 151
18 clangd                   0x0000000103d7ac17 void* llvm::thread::ThreadProxy<std::__1::tuple<clang::clangd::AsyncTaskRunner::runAsync(llvm::Twine const&, llvm::unique_function<void ()>)::$_1> >(void*) + 71
19 libsystem_pthread.dylib  0x00007ff8166994f4 _pthread_start + 125
20 libsystem_pthread.dylib  0x00007ff81669500f thread_start + 15

残る課題

clangd のクラッシュログを確認しましたが、なぜクラッシュしたのか検討がつきません。clangd の issue を探したら、似たようなログを貼り付けている issue を見つけたので、とりあえず「似たような現象に遭遇したよ」とコメントしておきました。時間がある時にもう少し問題の深掘りをしたいです。

この問題解決から得たこと

  • ドキュメントをに目を通すのは大事
    • 最初にドキュメントを確認していれば、割とすぐに解決する問題だったと思います
    • clangd のログ確認に辿り着くまで 2日程度かかりました