In letzter Zeit geht bei meinem Laptop häufiger als früher der Lüfter an, was nervt. Ich habe einen Verdacht (lsp-mode im Emacs), aber definitiv kann ich es nicht sagen. Deshalb habe ich mich auf die Suche nach der Ursache begeben.

Der Anfang bei solchen Ressourcen-Problemen wie zu viel CPU-Last oder einer hohen Speicherauslastung ist für mit immer atop. Mit atop -aCc 3 zeigen sich dann auch zwei Kandidaten: Firefox und Emacs.

Verbrauchersuche im Firefox

Da ich Firefox in Profile getrennt habe, konnte ich sofort sehen, dass es die Instanz mit dem Matrix-Client Element ist und somit war klar, wer der Verursacher ist. Leider hat man aber bei solchen Sammelprozessen oft verloren, weil man ohne die Hilfe der Programme nicht einfach an die Information gelangt, wo genau der Verursacher sitzt. An Firefox ist dies schön zu erklären: Nutzt man gleichzeitig mehrere Tabs für unterschiedliche Seiten, kann man im Betriebssystem von außen dem Prozess nicht ansehen, welcher Teil die Last verursacht.

Bei Firefox ist es durch die Auftrennung in einzelne Prozesse für einzelne Tabs, um die Sicherheit zu erhöhen, leichter geworden, aber trotzdem sieht man im Betriebssystem nicht, welche Webseite sich dahinter verbirgt. Aber Firefox hat dies auch erkannt und ein about:performance eingebaut. Wenn man diese Seite aufruft, bekommt man eine Übersicht der Tabs und Addons und deren Verbrauch.

Innerhalb eines Tabs kann man dann noch die Entwicklerwerkzeuge nutzen und dort die Performance-Analyse, um etwas tiefer einzusteigen. Hierfür bieten die Entwicklerwerkzeuge auch die entsprechenden grafischen Auswertungen als Flame-Chart oder Call-Tree.

Ich habe aber an dieser Stelle nicht weitergesucht, weil Element in der Vergangenheit nicht permanent gestört hat und ich notfalls den Chat auch einfach schließen kann. Mein eigentlicher Verdacht war ja Emacs.

Verbrauchersuche im Emacs

Für Emacs habe ich im Chat #emacs:matrix.org einen Hinweis[^](Die matrix.to-Links kann man übrigens in Element mit /goto https://matrix.to/… aufrufen) auf das Thema Profiling in der Hilfe und einen Hinweis auf das Paket explain-pause-mode bekommen. Die Anleitung von explain-pause-mode sah richtig cool und genau nach dem aus, was ich gesucht hatte.

Installation von explain-pause-mode

Zur Zeit (Dez. 2020) gibt es explain-pause-mode nur zur händischen Installation oder für straight.el, wobei ich letzteres nicht nutze. Aber die Befehle für die händische Einrichtung sind leicht:

% git clone 'https://github.com/lastquestion/explain-pause-mode.git'
% cd explain-pause-mode
% make byte-compile
% cp explain-pause-mode.el* ~/.config/emacs/lib/

In der init.el müssen folgende Einträge ergänzt werden, sofern sie noch nicht existieren:

(add-to-list 'load-path "~/.config/emacs/lib")
(autoload 'explain-pause-mode "explain-pause-mode" nil t)
(explain-pause-mode)

Wer explain-pause-mode nicht dauerhaft laufen lassen will, sollte die letzte Zeile nicht verwenden, sondern die Funktion nur bei Bedarf mit M-x explain-pause-mode aufrufen.

Nutzung von explain-pause-mode

Um sich die Ergebnisse von explain-pause-mode anzusehen, braucht man nur nach einer Weile mit M-x die Funktion explain-pause-top[^](Mit Emacs 27 kann man Funktionen auch abkürzen, solang sie eindeutig sind. Bei mir genügt z. B. ex-top als Aufruf.) aufrufen und bekommt eine Auflistung ähnlich dieser:

Command                                       slow    avg ms     ms    calls
spinner--timer-function                         15      0.01   3108   324585
 ► Last 5 slow: 212, 160, 215, 214, 206 ms
desktop-auto-save                                4     42.57    298        7
 ► Last 4 slow: 56, 57, 61, 44 ms
 ► Profile: [56.00 ms]

Die Profile kann man auch aufrufen und bekommt einen Call-Graph, über den man verfolgen kann, wo die meiste Aufrufzeit gebraucht wird.

Mit explain-pause-top-clear und explain-pause-profile-clear kann man die jeweiligen Daten löschen, um wieder etwas Überblick zu bekommen. Abschalten lässt sich die Datenaufzeichnung wieder mit M-x explain-pause-mode, ggf. mit einem M-0 zuvor.

Weiter mit der Suche

Mit C-h f wird für eine Funktion die Beschreibung und die Datei, in der sie definiert ist, angezeigt. Für spinner--timer-function war dies spinner.el, die bei mir im Verzeichnis …/elpa/spinner-1.7.3 liegt. Also habe ich in der Paketliste (M-x p-l-p) die Beschreibung des Pakets angesehen und dort standen rustic und lsp-mode als Nutzer des Pakets. Somit erhärtete sich der Verdacht, dass es eine der letzten Aktualisierungen von lsp-mode waren.

Emacs-Profiling

Der andere Vorschlag war ja, das von Emacs bereitgestellte Profiling zu nutzen. Dieses lässt sich auch ganz einfach mit profiler-start starten, dann 10 Sekunden warten und mit profiler-stop wieder anhalten. Mit profiler-report bekommt man dann einen Analyse wie diese:

- timer-event-handler 162 71% - apply 162 71% - #<compiled 0x158ce57194e1> 162 71% - lsp--on-idle 162 71% - run-hooks 162 71% - lsp--document-highlight 162 71% - lsp-request-async 162 71% - lsp--send-request-async 162 71% + error 162 71% - ... 64 28% Automatic GC 64 28%

Hier fiel mir auf, dass in dem Aufrufpfad ein Fehler passierte und wieder war lsp-mode beteiligt. Ich habe mir dann die Funktion lsp--document-highlight genauer angesehen[^](Zuvor habe ich die anderen Funktionen von error ausgehend untersucht, um zu verstehen, was passiert.) und festgestellt, dass dort der Language-Server immer angefragt wird, auch wenn er die Funktion gar nicht unterstützt. Dies führt mit jedem Aufruf zu einem Fehler und Fehler sind immer teuer.

Die (Frickel‑)Lösung

Ich habe in der Funktion lsp--document-highlight noch die Prüfung (lsp-feature? "textDocument/documentHighlight") hinzugefügt und damit kehrte wieder Ruhe ein. Ich musste noch die elc-Datei mit byte-compile-file neu übersetzen, weil meine Änderungen in der el-Datei nicht beachtet werden, und habe noch Fehler dem Projekt gemeldet.

Wieder auf Suche

Die Lösung erschien mir schon beim Schreiben, wie die sprichwörtlich nächste Lage Klebeband, die man drumherum wickelt, aber nicht den eigentlichen Bruch repariert. Ich hatte zwar die Vorstellung, dass lsp, so zu sagen, im Betrieb lernt, welche Funktionen der Language-Server hat und dementsprechend sein Wissen anpasst, aber so läuft es nicht und daher ist es seltsam, dass sich während des Betriebs plötzlich der Funktionsumfangs ändert.

Wie ich später ich bemerkt habe, kommt die Fehlermeldung auch gar nicht von der Antwort des Servers, sondern von der Sendefunktion, die keine Verbindung zum Server findet und dann sagt »Server unterstützt nicht die Funktion …«

Mit trace-function Funktionsaufrufe überwachen

Also bin ich wieder auf die Suche gegangen und habe mit trace-function Überwachungen der Funktionsaufrufe eingebaut. Allerdings öffnet trace-function bei jedem Funktionsaufruf den Protokollpuffer *trace-output*, weshalb ich nur trace-function-background verwendet habe. Bei der Überwachung werden von beiden Funktionen beim Aufruf die Parameter und beim Verlassen der Rückgabewert protokolliert. Angefangen hatte ich mit lsp-feature? und lsp--find-workspaces-for (ein Alias der ersten) und mich dann immer weiter voran getastet.

Hilfreich war, dass man für die Überwachung eine Kontext-Funktion übergeben kann, die im Moment der Protokollierung ausgeführt wird. Somit konnte ich überall den aktuellen Puffer und die Zustände der zwei Variablen lsp--cur-workspace und lsp--buffer-workspaces protokollieren. Da beide Variablen mit defvar-local angelegt werden und auf ominöse Weise, der Wert immer irgendwann verschwand, habe ich die Prüfung auf den Zustand der Bindung als lokale Variable geprüft, der sich tatsächlich auch immer änderte.

Über die Zeit hin habe ich immer mehr interessante Funktionen entdeckt und diese überwacht. Das ist so immer beim Debuggen ein Herumstochern im Nebel und Ausprobieren.

``` lisp (require 'lsp-mode) (with-eval-after-load 'lsp-mode (let ((trace-buf "trace") (ctx (lambda () (format " <+<%s|+|%s|+|%s|+|%s|+|%s>+>" (buffer-name) (local-variable-p 'lsp--cur-workspace) (local-variable-p 'lsp--buffer-workspaces) lsp--cur-workspace lsp--buffer-workspaces))) )

(get-buffer-create trace-buf)

(trace-function-background 'lsp-feature? trace-buf ctx)
(trace-function-background 'lsp--find-workspaces-for trace-buf ctx)
))

```

Variablen überwachen

Irgendwann bin ich über die Funktion add-variable-watcher gestolpert, die zum Überwachen der Veränderungen einer Variable dient. Emacs hat wirklich die Vollausstattung an Werkzeugen, von der man einfach nur träumt: Bei trace-function schon den Kontext und bei add-variable-watcher jetzt auch.

Somit konnte ich die Veränderungen der beiden Variablen überwachen und an komischen Stellen einfach mal das Programm abbrechen, um einen Backtrace zu erhalten. Irgendwie geht das sicher auch schöner, aber wie weiß ich (noch) nicht und mir hat das für den Moment genügt.

``` lisp (with-eval-after-load 'lsp-mode (let ((trace-buf "trace") … (watcher (lambda (symbol newval operation where) (let ((buf (buffer-name))) (with-current-buffer "trace" (goto-char (point-max)) (insert (format "==>> %s|-|%s|-|%s|-|%s|-|%s|-|%s\n" buf where symbol (local-variable-p symbol) operation (if newval "val"))) ;; (when (and (not (string-match-p ".rs$" buf)) ;; ) ;; (error "open back trace 1")) )) ;; (when (string= operation "makunbound") ;; (error "open back trace 2")) ;; (when (and (eq symbol 'lsp--buffer-workspaces) ;; (string= operation "set") ;; (if newval t)) ;; (error "open back trace 3")) )) )

…
(add-variable-watcher 'lsp--cur-workspace watcher)
(add-variable-watcher 'lsp--buffer-workspaces watcher)
))

```

Auswertung

Am Ende sah das (mit occur gefilterte) Protokoll in etwa so aus (dies ist nur ein kleiner Ausschnitt):

======================================================================
1 -> (lsp) <+<sql.rs|+|nil|+|nil|+|nil|+|nil>+>
| 2 -> (lsp--try-project-root-workspaces nil nil) <+<sql.rs|+|nil|+|nil|+|nil|+|nil>+>
| | 3 -> (lsp--ensure-lsp-servers #s(lsp-session ("/tmp/gittest" "/home/joerg/Projekte/word-dist" "/tmp/gittest/src" "/tmp/sort-test/src" "/home/joerg/.cargo/registry/src/github.com-1ecc6299db9ec823/libgit2-sys-0.12.14+1.1.0" "/home/joerg/.cargo/registry/src/github.com-1ecc6299db9ec823/git2-0.13.12" "/home/joerg/Projekte/gitlog2rss" "/home/joerg/Projekte/daheme" "/home/joerg/Projekte/firma/riafulon" "/home/joerg/Projekte/firma/riafulon/builder" "/home/joerg/Projekte/daheme/daheme-cli" "/home/joerg/Projekte/daheme/daheme-cli/temp-2" "/home/joerg/Projekte/daheme/daheme" "/home/joerg/Projekte/daheme/daheme/build-spec-tests") nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ())) (#s(lsp--client nil nil (:connect (closure #1=((test-command) (command . #[0 "\207" [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format "*%s::stderr*" process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format "*%s*" process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test\? (closure #1# nil (lsp-server-present\? (lsp-resolve-final-function command)))) nil nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("window/progress" lsp-clients--rust-window-progress)) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data ()) nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("rls.run" lsp-rust--rls-run)) (rust-mode rustic-mode) nil -1 rls nil ((omitInitBuild . t) (cmdRun . t)) nil #[257 "\207" [lsp-rust-library-directories] 2 "
| | | 4 -> (lsp--start-connection #s(lsp-session ("/tmp/gittest" "/home/joerg/Projekte/word-dist" "/tmp/gittest/src" "/tmp/sort-test/src" "/home/joerg/.cargo/registry/src/github.com-1ecc6299db9ec823/libgit2-sys-0.12.14+1.1.0" "/home/joerg/.cargo/registry/src/github.com-1ecc6299db9ec823/git2-0.13.12" "/home/joerg/Projekte/gitlog2rss" "/home/joerg/Projekte/daheme" "/home/joerg/Projekte/firma/riafulon" "/home/joerg/Projekte/firma/riafulon/builder" "/home/joerg/Projekte/daheme/daheme-cli" "/home/joerg/Projekte/daheme/daheme-cli/temp-2" "/home/joerg/Projekte/daheme/daheme" "/home/joerg/Projekte/daheme/daheme/build-spec-tests") nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ())) #s(lsp--client nil nil (:connect (closure #1=((test-command) (command . #[0 "\207" [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format "*%s::stderr*" process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format "*%s*" process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test\? (closure #1# nil (lsp-server-present\? (lsp-resolve-final-function command)))) nil nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("window/progress" lsp-clients--rust-window-progress)) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data ()) nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("rls.run" lsp-rust--rls-run)) (rust-mode rustic-mode) nil -1 rls nil ((omitInitBuild . t) (cmdRun . t)) nil #[257 "\207" [lsp-rust-library-directories] 2 "
| | | | 5 -> (lsp--start-workspace #s(lsp-session ("/tmp/gittest" "/home/joerg/Projekte/word-dist" "/tmp/gittest/src" "/tmp/sort-test/src" "/home/joerg/.cargo/registry/src/github.com-1ecc6299db9ec823/libgit2-sys-0.12.14+1.1.0" "/home/joerg/.cargo/registry/src/github.com-1ecc6299db9ec823/git2-0.13.12" "/home/joerg/Projekte/gitlog2rss" "/home/joerg/Projekte/daheme" "/home/joerg/Projekte/firma/riafulon" "/home/joerg/Projekte/firma/riafulon/builder" "/home/joerg/Projekte/daheme/daheme-cli" "/home/joerg/Projekte/daheme/daheme-cli/temp-2" "/home/joerg/Projekte/daheme/daheme" "/home/joerg/Projekte/daheme/daheme/build-spec-tests") nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ())) #s(lsp--client nil nil (:connect (closure #1=((test-command) (command . #[0 "\207" [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format "*%s::stderr*" process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format "*%s*" process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test\? (closure #1# nil (lsp-server-present\? (lsp-resolve-final-function command)))) nil nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("window/progress" lsp-clients--rust-window-progress)) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data ()) nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("rls.run" lsp-rust--rls-run)) (rust-mode rustic-mode) nil -1 rls nil #2=((omitInitBuild . t) (cmdRun . t)) nil #[257 "\207" [lsp-rust-library-directories] 2 "
==>> sql.rs|-|sql.rs|-|lsp--cur-workspace|-|nil|-|let|-|#s(lsp--workspace nil nil nil /home/joerg/Projekte/firma/riafulon/builder #s(lsp--client nil nil (:connect (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format *%s::stderr* process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format *%s* process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test? (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) nil (lsp-server-present? (lsp-resolve-final-function command)))) nil nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data (window/progress lsp-clients--rust-window-progress)) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data ()) nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data (rls.run lsp-rust--rls-run)) (rust-mode rustic-mode) nil -1 rls nil ((omitInitBuild . t) (cmdRun . t)) nil #[257 \207 [lsp-rust-library-directories] 2 
| | | | | 6 -> (lsp--find-workspaces-for (:jsonrpc "2.0" :method "initialize" :params (:processId nil :rootPath "/home/joerg/Projekte/firma/riafulon/builder" :clientInfo (:name "emacs" :version "GNU Emacs 27.1 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.23, cairo version 1.16.0)
| | | | | 6 <- lsp--find-workspaces-for: (#s(lsp--workspace nil nil nil "/home/joerg/Projekte/firma/riafulon/builder" #s(lsp--client nil nil (:connect (closure #1=((test-command) (command . #[0 "\207" [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format "*%s::stderr*" process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format "*%s*" process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test\? (closure #1# nil (lsp-server-present\? (lsp-resolve-final-function command)))) nil nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("window/progress" lsp-clients--rust-window-progress)) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data ()) nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("rls.run" lsp-rust--rls-run)) (rust-mode rustic-mode) nil -1 rls nil ((omitInitBuild . t) (cmdRun . t)) nil #[257 "\207" [lsp-rust-library-directories] 2 "
==>> sql.rs|-|nil|-|lsp--cur-workspace|-|nil|-|set|-|nil
| | | | 5 <- lsp--start-workspace: #2=#s(lsp--workspace nil nil nil "/home/joerg/Projekte/firma/riafulon/builder" #4=#s(lsp--client nil nil #3=(:connect (closure #1=((test-command) (command . #[0 "\207" [lsp-rust-rls-server-command] 1]) . #11=(company-mode . #16=(cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t))) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format "*%s::stderr*" process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format "*%s*" process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test\? (closure #1# nil (lsp-server-present\? (lsp-resolve-final-function command)))) nil nil #6=#s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("window/progress" lsp-clients--rust-window-progress)) #7=#s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #8=#s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data (1 (#22=(closure #23=((callback closure ((errors) (results) (workspaces . #19=(#2#)) (no-merge) (method . #18="initialize") (callback . #17=(closure ((workspace-folders) (cmd-proc . #<process rls>) (proc . #<process rls>) (--dash-source-140-- . #<process rls>) (initialized-fn . #10=#[257 "\211\301\302\303!!)\207" [lsp--cur-workspace lsp--set-configuration lsp-configuration-section "rust"] 4 "
| | | 4 <- lsp--start-connection: #2=#s(lsp--workspace nil nil nil "/home/joerg/Projekte/firma/riafulon/builder" #4=#s(lsp--client nil nil #3=(:connect (closure #1=((test-command) (command . #[0 "\207" [lsp-rust-rls-server-command] 1]) . #11=(company-mode . #16=(cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t))) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format "*%s::stderr*" process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format "*%s*" process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test\? (closure #1# nil (lsp-server-present\? (lsp-resolve-final-function command)))) nil nil #6=#s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("window/progress" lsp-clients--rust-window-progress)) #7=#s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #8=#s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data (1 (#22=(closure #23=((callback closure ((errors) (results) (workspaces . #19=(#2#)) (no-merge) (method . #18="initialize") (callback . #17=(closure ((workspace-folders) (cmd-proc . #<process rls>) (proc . #<process rls>) (--dash-source-140-- . #<process rls>) (initialized-fn . #10=#[257 "\211\301\302\303!!)\207" [lsp--cur-workspace lsp--set-configuration lsp-configuration-section "rust"] 4 "
| | 3 <- lsp--ensure-lsp-servers: (#2=#s(lsp--workspace nil nil nil #5="/home/joerg/Projekte/firma/riafulon/builder" #4=#s(lsp--client nil nil #3=(:connect (closure #1=((test-command) (command . #[0 "\207" [lsp-rust-rls-server-command] 1]) . #11=(company-mode . #16=(cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t))) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format "*%s::stderr*" process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format "*%s*" process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test\? (closure #1# nil (lsp-server-present\? (lsp-resolve-final-function command)))) nil nil #6=#s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("window/progress" lsp-clients--rust-window-progress)) #7=#s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #8=#s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data (1 (#22=(closure #23=((callback closure ((errors) (results) (workspaces . #19=(#2#)) (no-merge) (method . #18="initialize") (callback . #17=(closure ((workspace-folders) (cmd-proc . #<process rls>) (proc . #<process rls>) (--dash-source-140-- . #<process rls>) (initialized-fn . #10=#[257 "\211\301\302\303!!)\207" [lsp--cur-workspace lsp--set-configuration lsp-configuration-section "rust"] 4 "
| 2 <- lsp--try-project-root-workspaces: (#2=#s(lsp--workspace nil nil nil #5="/home/joerg/Projekte/firma/riafulon/builder" #4=#s(lsp--client nil nil #3=(:connect (closure #1=((test-command) (command . #[0 "\207" [lsp-rust-rls-server-command] 1]) . #11=(company-mode . #16=(cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t))) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format "*%s::stderr*" process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format "*%s*" process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test\? (closure #1# nil (lsp-server-present\? (lsp-resolve-final-function command)))) nil nil #6=#s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("window/progress" lsp-clients--rust-window-progress)) #7=#s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #8=#s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data (1 (#22=(closure #23=((callback closure ((errors) (results) (workspaces . #19=(#2#)) (no-merge) (method . #18="initialize") (callback . #17=(closure ((workspace-folders) (cmd-proc . #<process rls>) (proc . #<process rls>) (--dash-source-140-- . #<process rls>) (initialized-fn . #10=#[257 "\211\301\302\303!!)\207" [lsp--cur-workspace lsp--set-configuration lsp-configuration-section "rust"] 4 "
==>> sql.rs|-|sql.rs|-|lsp--buffer-workspaces|-|nil|-|set|-|(#s(lsp--workspace nil nil nil /home/joerg/Projekte/firma/riafulon/builder #s(lsp--client nil nil (:connect (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format *%s::stderr* process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format *%s* process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test? (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) nil (lsp-server-present? (lsp-resolve-final-function command)))) nil nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data (window/progress lsp-clients--rust-window-progress)) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data (1 ((closure ((callback closure ((errors) (results) (workspaces #1) (no-merge) (method . initialize) (callback closure ((workspace-folders) (cmd-proc . rls) (proc . rls) (--dash-source-140-- . rls) (initialized-fn . #[257 \211\301\302\303!!)\207 [lsp--cur-workspace lsp--set-configuration lsp-configuration-section rust] 4 
| 2 -> (lsp-mode 1) <+<sql.rs|+|nil|+|t|+|nil|+|(#s(lsp--workspace nil nil nil /home/joerg/Projekte/firma/riafulon/builder #s(lsp--client nil nil (:connect (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format *%s::stderr* process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format *%s* process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test? (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) nil (lsp-server-present? (lsp-resolve-final-function command)))) nil nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data (window/progress lsp-clients--rust-window-progress)) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data (1 ((closure ((callback closure ((errors) (results) (workspaces #1) (no-merge) (method . initialize) (callback closure ((workspace-folders) (cmd-proc . rls) (proc . rls) (--dash-source-140-- . rls) (initialized-fn . #[257 \211\301\302\303!!)\207 [lsp--cur-workspace lsp--set-configuration lsp-configuration-section rust] 4 
| 2 <- lsp-mode: t <+<sql.rs|+|nil|+|t|+|nil|+|(#s(lsp--workspace nil nil nil /home/joerg/Projekte/firma/riafulon/builder #s(lsp--client nil nil (:connect (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format *%s::stderr* process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format *%s* process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test? (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) nil (lsp-server-present? (lsp-resolve-final-function command)))) nil nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data (window/progress lsp-clients--rust-window-progress)) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data (1 ((closure ((callback closure ((errors) (results) (workspaces #1) (no-merge) (method . initialize) (callback closure ((workspace-folders) (cmd-proc . rls) (proc . rls) (--dash-source-140-- . rls) (initialized-fn . #[257 \211\301\302\303!!)\207 [lsp--cur-workspace lsp--set-configuration lsp-configuration-section rust] 4 
1 <- lsp: #("LSP :: Connected to [rls:2128742 status:starting]." 0 3 (face success) 21 24 (face bold-italic) 25 32 (face italic)) <+<sql.rs|+|nil|+|t|+|nil|+|(#s(lsp--workspace nil nil nil /home/joerg/Projekte/firma/riafulon/builder #s(lsp--client nil nil (:connect (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format *%s::stderr* process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format *%s* process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test? (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) nil (lsp-server-present? (lsp-resolve-final-function command)))) nil nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data (window/progress lsp-clients--rust-window-progress)) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data (1 ((closure ((callback closure ((errors) (results) (workspaces #1) (no-merge) (method . initialize) (callback closure ((workspace-folders) (cmd-proc . rls) (proc . rls) (--dash-source-140-- . rls) (initialized-fn . #[257 \211\301\302\303!!)\207 [lsp--cur-workspace lsp--set-configuration lsp-configuration-section rust] 4 
==>> sql.rs|-|sql.rs|-|lsp--buffer-workspaces|-|nil|-|makunbound|-|nil
==>> sql.rs|-|sql.rs|-|lsp--cur-workspace|-|nil|-|let|-|#s(lsp--workspace nil #s(hash-table size 15 test equal rehash-size 1.5 rehash-threshold 0.8125 data (textDocumentSync 2 hoverProvider t completionProvider #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data (resolveProvider t triggerCharacters [. :])) definitionProvider t implementationProvider t referencesProvider t documentHighlightProvider t documentSymbolProvider t workspaceSymbolProvider t codeActionProvider t codeLensProvider #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data (resolveProvider nil)) documentFormattingProvider t documentRangeFormattingProvider nil renameProvider t executeCommandProvider #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data (commands [rls.applySuggestion-2128742 rls.deglobImports-2128742])))) nil /home/joerg/Projekte/firma/riafulon/builder #s(lsp--client nil nil (:connect (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format *%s::stderr* process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format *%s* process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test? (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) nil (lsp-server-present? (lsp-resolve-final-function command)))) nil nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data (window/progress lsp-clients--rust-window-progress)) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data (1 ((closure ((callback closure ((errors) (results (#0 . #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data (capabilities #s(hash-table size 15 test equal rehash-size 1.5 rehash-threshold 0.8125 data (textDocumentSync 2 hoverProvider t completionProvider #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data (resolveProvider t triggerCharacters [. :])) definitionProvider t implementationProvider t referencesProvider t documentHighlightProvider t documentSymbolProvider t workspaceSymbolProvider t codeActionProvider t codeLensProvider #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data (resolveProvider nil)) documentFormattingProvider t documentRangeFormattingProvider nil renameProvider t executeCommandProvider #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data (commands [rls.applySuggestion-2128742 rls.deglobImports-2128742])))))))) (workspaces #0) (no-merge) (method . initialize) (callback closure ((workspace-folders) (cmd-proc . rls) (proc . rls) (--dash-source-140-- . rls) (initialized-fn . #[257 \211\301\302\303!!)\207 [lsp--cur-workspace lsp--set-configuration lsp-configuration-section rust] 4 
==>> sql.rs|-|sql.rs|-|lsp--cur-workspace|-|nil|-|let|-|#s(lsp--workspace nil #s(hash-table size 15 test equal rehash-size 1.5 rehash-threshold 0.8125 data (textDocumentSync 2 hoverProvider t completionProvider #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data (resolveProvider t triggerCharacters [. :])) definitionProvider t implementationProvider t referencesProvider t documentHighlightProvider t documentSymbolProvider t workspaceSymbolProvider t codeActionProvider t codeLensProvider #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data (resolveProvider nil)) documentFormattingProvider t documentRangeFormattingProvider nil renameProvider t executeCommandProvider #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data (commands [rls.applySuggestion-2128742 rls.deglobImports-2128742])))) nil /home/joerg/Projekte/firma/riafulon/builder #s(lsp--client nil nil (:connect (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format *%s::stderr* process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format *%s* process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test? (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) nil (lsp-server-present? (lsp-resolve-final-function command)))) nil nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data (window/progress lsp-clients--rust-window-progress)) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data (1 ((closure ((callback closure ((errors) (results (#0 . #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data (capabilities #s(hash-table size 15 test equal rehash-size 1.5 rehash-threshold 0.8125 data (textDocumentSync 2 hoverProvider t completionProvider #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data (resolveProvider t triggerCharacters [. :])) definitionProvider t implementationProvider t referencesProvider t documentHighlightProvider t documentSymbolProvider t workspaceSymbolProvider t codeActionProvider t codeLensProvider #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data (resolveProvider nil)) documentFormattingProvider t documentRangeFormattingProvider nil renameProvider t executeCommandProvider #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data (commands [rls.applySuggestion-2128742 rls.deglobImports-2128742])))))))) (workspaces #0) (no-merge) (method . initialize) (callback closure ((workspace-folders) (cmd-proc . rls) (proc . rls) (--dash-source-140-- . rls) (initialized-fn . #[257 \211\301\302\303!!)\207 [lsp--cur-workspace lsp--set-configuration lsp-configuration-section rust] 4 
==>> sql.rs|-|nil|-|lsp--cur-workspace|-|nil|-|set|-|#s(lsp--workspace nil #s(hash-table size 15 test equal rehash-size 1.5 rehash-threshold 0.8125 data (textDocumentSync 2 hoverProvider t completionProvider #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data (resolveProvider t triggerCharacters [. :])) definitionProvider t implementationProvider t referencesProvider t documentHighlightProvider t documentSymbolProvider t workspaceSymbolProvider t codeActionProvider t codeLensProvider #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data (resolveProvider nil)) documentFormattingProvider t documentRangeFormattingProvider nil renameProvider t executeCommandProvider #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data (commands [rls.applySuggestion-2128742 rls.deglobImports-2128742])))) nil /home/joerg/Projekte/firma/riafulon/builder #s(lsp--client nil nil (:connect (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) (filter sentinel name environment-fn) (let ((final-command (lsp-resolve-final-function command)) (process-name (generate-new-buffer-name name)) (process-environment (lsp--compute-process-environment environment-fn))) (let* ((stderr-buf (format *%s::stderr* process-name)) (proc (make-process :name process-name :connection-type 'pipe :buffer (format *%s* process-name) :coding 'no-conversion :command final-command :filter filter :sentinel sentinel :stderr stderr-buf :noquery t))) (set-process-query-on-exit-flag proc nil) (set-process-query-on-exit-flag (get-buffer-process stderr-buf) nil) (save-current-buffer (set-buffer (get-buffer stderr-buf)) (special-mode)) (cons proc proc)))) :test? (closure ((test-command) (command . #[0 \207 [lsp-rust-rls-server-command] 1]) company-mode cl-struct-lsp--log-entry-tags cl-struct-lsp-session-tags cl-struct-lsp--workspace-tags cl-struct-lsp--registered-capability-tags lsp-mode-menu cl-struct-lsp--folding-range-tags cl-struct-lsp-watch-tags cl-struct-lsp--client-tags lsp--log-lines dap-ui-menu-items dap-auto-configure-mode company-backends t) nil (lsp-server-present? (lsp-resolve-final-function command)))) nil nil #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data (window/progress lsp-clients--rust-window-progress)) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8125 data (1 ((closure ((callback closure ((errors) (results (#0 . #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data (capabilities #s(hash-table size 15 test equal rehash-size 1.5 rehash-threshold 0.8125 data (textDocumentSync 2 hoverProvider t completionProvider #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data (resolveProvider t triggerCharacters [. :])) definitionProvider t implementationProvider t referencesProvider t documentHighlightProvider t documentSymbolProvider t workspaceSymbolProvider t codeActionProvider t codeLensProvider #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data (resolveProvider nil)) documentFormattingProvider t documentRangeFormattingProvider nil renameProvider t executeCommandProvider #s(hash-table size 1 test equal rehash-size 1.5 rehash-threshold 0.8125 data (commands [rls.applySuggestion-2128742 rls.deglobImports-2128742])))))))) (workspaces #0) (no-merge) (method . initialize) (callback closure ((workspace-folders) (cmd-proc . rls) (proc . rls) (--dash-source-140-- . rls) (initialized-fn . #[257 \211\301\302\303!!)\207 [lsp--cur-workspace lsp--set-configuration lsp-configuration-section rust] 4 

Auffällig war, dass die Variablen irgendwan immer Werte hatten, diese aber irgendwann immer wieder nil wurden. Der oben abgebildete Abschnitt zeigt die korrekte Initialisierung der Variablen, aber irgendwann kommt dieses makunbound und von daan sind die Variablen beim Aufruf des Timers immer leer.

Durch meine Abbruche und die entstandenen Aufrufketten konnte ich dann auch erkennen wo und bin so langsam hinter das Problem gestiegen.

Debugger entered--Lisp error: (error "do back trace")
  signal(error ("do back trace"))
  error("do back trace")
  (progn (error "do back trace"))
  (if (string= operation "makunbound") (progn (error "do back trace")))
  (lambda (symbol newval operation where) (let ((buf (buffer-name))) (save-current-buffer (set-buffer "*trace*") (goto-char (point-max)) (insert (format "==>> %s|-|%s|-|%s|-|%s|-|%s|-|%s\n" buf where symbol (local-variable-p symbol) operation newval)))) (if (string= operation "makunbound") (progn (error "do back trace"))))(lsp--buffer-workspaces nil makunbound #<buffer sql.rs>)
  kill-all-local-variables()
[Ergänzung weil nicht angezeigt: fundamental-mode]
  prog-mode()
  rust-mode()
  desktop-restore-file-buffer("/home/joerg/Projekte/firma/riafulon/builder/src/sq..." "sql.rs" nil)
  desktop-create-buffer(208 "/home/joerg/Projekte/firma/riafulon/builder/src/sq..." "sql.rs" rust-mode (auto-fill-mode abbrev-mode eldoc-mode global-whitespace-mode hl-todo-mode magit-file-mode yas-minor-mode helm-ff-cache-mode lsp-modeline-code-actions-mode lsp-modeline-diagnostics-mode lsp-modeline-workspace-status-mode lsp-completion-mode racer-mode company-mode flycheck-mode cargo-minor-mode lsp-diagnostics-mode) 1431 (nil nil) nil nil ((indent-tabs-mode) (buffer-display-time 24525 2248 875343 838000) (buffer-file-coding-system . undecided-unix)) ((mark-ring nil)))
  eval-buffer(#<buffer  *load*> nil "/home/joerg/.config/emacs/.emacs.desktop" nil t)  ; Reading at buffer position 26519
  load-with-code-conversion("/home/joerg/.config/emacs/.emacs.desktop" "/home/joerg/.config/emacs/.emacs.desktop" t t)
  load("/home/joerg/.config/emacs/.emacs.desktop" t t t)
  desktop-read()
  #f(compiled-function () #<bytecode 0x158450495b25>)()
  run-hooks(after-init-hook delayed-warnings-hook)
  command-line()
  normal-top-level()

Des Rätsels Lösung

Ich möchte noch voraus schicken, dass ich über zehn Stunden an diesem Problem gesucht habe und oft vor und zurück gelaufen bin. Wer also solche Probleme untersucht, braucht Hartnäckigkeit und Geduld.

Im Zentrum des Problems steht desktop-save-mode und die Funktion desktop-restore-file-buffer. Der entscheidende Ausschnitt ist dieser:

lisp (let* ((auto-insert nil) ; Disable auto insertion (coding-system-for-read (or coding-system-for-read (cdr (assq 'buffer-file-coding-system desktop-buffer-locals)))) (buf (find-file-noselect buffer-filename :nowarn))) (condition-case nil (switch-to-buffer buf) (error (pop-to-buffer buf))) (and (not (eq major-mode desktop-buffer-major-mode)) (functionp desktop-buffer-major-mode) (funcall desktop-buffer-major-mode))

Bei der Wiederherstellung der geöffneten Dateien öffnet es ganz normal mit find-file-noselect die Datei und lässt sie von Emacs initialisieren. Sollte dann der Major-Mode nicht mit dem in der Sicherung hinterlegten übereinstimmen, wechselte es diesen. Vor einigen Wochen hatte ich die Pakete rustic-mode und lsp-mode mit package-list-packages installiert. Rustic setzt sich selbst als Major-Mode für Rust-Dateien und startet dabei lsp. Meine alten Puffer waren aber alle noch mit rust-mode (ohne ic), weshalb desktop-save-mode zwar die Dateien mit rustic-mode und lsp startete, aber dann zu rust-mode wechselte.

Lsp startete zwar korrekt den Language-Server und initialisierte die lokale Variable lsp--buffer-workspaces, aber beim Wechsel des Major-Modes zu rust-mode wird prog-mode, fundamental-mode und dort kill-all-local-variables aufgerufen, was die lokale Variable mit den Serverdaten wieder löscht, während der Timer aktiv bleibt. Der Rest ist dann klar: Der Timer löst aus und es kracht.

Ein Loblied auf Emacs

Das, was ich über die Zeit hin und durch Aktionen wie diese gelernt habe, ist: wenn es ein Problem gibt, dann hat Emacs eine Lösung dafür (oder sie lässt sich recht einfach schaffen). Es ist wahrscheinlich das Ergebnis der Zeit, die bereits 30 Jahre an Emacs geschliffen hat, aber vermutlich auch mit ein Zeugnis guter Programmfähigkeiten. Ich könnte noch weitere Beispiele aufzählen, bei denen ich nie geglaubt hätte, sie sauber lösen zu können. Aber nach einiger Recherche hat sich die saubere Lösung gefunden.

Leider ist das auch das Problem – genauso wie in den Weiten des Internets: man muss die Lösung finden. Es gibt massenweise Informationen zu Emcas und wenn man die Funktionsliste sieht, weiß man nicht, wo man suchen soll. Vieles existiert scheinbar doppelt und erst mit der Zeit erkennt man die Unterschiede und die Vorzüge der einen oder der anderen Lösung. Die Lösung liegt leider oft unter einem riesigen Berg versteckt.

Für dieses Problem hier ist der Hinweis auf die Lösung in der Dokumentation von kill-all-local-variables[^](dessen Name meiner Meinung nach besser reset-buffer lauten sollte) zu finden: change-major-mode-hook. Damit kann sich lsp-mode darüber informieren lassen, dass sich der Typ des Puffers ändert und kann entsprechend reagieren. Es gäbe sogar noch eine zweite Lösung: Variablen lassen sich auch als permanent-local kennzeichnen. Welche davon die bessere ist, mögen die Entwickler von lsp-mode entscheiden.

Nachtrag: Allgemeines zur Lage

Wie schon eingangs gesagt, ist die Suche nach den tatsächlichen Problemstellen bei solchen Sammelprozessen schwierig. Insbesondere denke ich da auch an Prozesse, die keine Threads vom Betriebssystem, sondern eigene (Green-Threads) nutzen. Hierbei könnte aber BPFtrace helfen, wenn für die Prozesse die entsprechenden Informationen vorliegen. Dann kann man die Prozesse von außen betrachten, denn die Analyse aus den Programmen heraus führt immer wieder zu Problemen, weil im Zweifels das Problem den Zugriff auf den Debugger blockiert.

Weitere Notizen