Malabarba / aggressive-indent-mode

Emacs minor mode that keeps your code always indented. More reliable than electric-indent-mode.
850 stars 36 forks source link

Extreme CPU usage and long delay when saving VHDL-files #105

Open adrspa opened 7 years ago

adrspa commented 7 years ago

Hi,

I just started out using the aggressive-indent mode and it looks very promising. However, I quickly noticed that it suddenly takes a very, very long time (>30 seconds) to save a .vhd-file. I did a CPU profile in emacs and have attached that here. It seems that the 'before-save-hook calls aggressive-indent, which then uses lots of CPU to do something.

I currently have this in my .emacs: ;; MELPA packages (require 'package) (add-to-list 'package-archives '("MELPA Stable" . "https://stable.melpa.org/packages/") t) (package-initialize)

;; Bootstrap `use-package' (unless (package-installed-p 'use-package) (package-refresh-contents) (package-install 'use-package))

;;; Bootstrap aggressive-indent (unless (package-installed-p 'aggressive-indent) (package-refresh-contents) (package-install 'aggressive-indent))

;; aggressive-indent setup (global-aggressive-indent-mode 1)

Any suggestions here? Should I disable the before-save-hook (and if so how?)?

Best regards, Adrian

emacs_aggressive_indent_slow_save_debug.txt

seagle0128 commented 7 years ago

I guess it's duplicated with #73.

eggsyntax commented 6 years ago

I'm seeing the same behavior with ClojureScript files, but only when CIDER is running. Emacs essentially freezes for anything up to five minutes or so, with CPU maxed out (on one core). Ctrl-g will interrupt, but then you're left with an unsaved file. Behavior is very consistent.

Not a dupe of #73 in my opinion, because a) it's only on save, and b) it's not only on large files (I'm seeing it on a 190-line .cljs file).

I had problems with aggressive-indent-mode slowdown, and then they seemed to resolve on their own, but now, after a Spacemacs reinstall, they're back and worse than ever.

I love, love, love what aggressive-indent-mode does, but not at the cost of extended freeze-up :(

Incidentally, I attempted to identify with the profiler, but 95% of cpu use was from command-execute (or execute-command?) and it wouldn't let me expand that (Folding is not supported) to get any more detail.

Malabarba commented 6 years ago

It helps to know what was running when Emacs hangs like that. Could you try the following to debug it?

  1. Do M-x toggle-debug-on-quit.
  2. Reproduce the hang (or wait for it to happen)
  3. Hit C-g while it's hanged.

You'll get a backtrace you can paste here.

Alexander-Miller commented 6 years ago

Sending emacs a SIGUSR2 will also show a backtrace of what its doing.

eggsyntax commented 6 years ago

Bingo, just captured one:

Debugger entered--Lisp error: (quit)
  abbreviate-file-name("/home/egg/Documents/Work/harmonium/src/harmonium/ui/client/forms/")
  locate-dominating-file("/home/egg/Documents/Work/harmonium/src/harmonium/ui/client/forms/" "build.gradle")
  #[257 "\301\300\"\207" ["/home/egg/Documents/Work/harmonium/src/harmonium/ui/client/forms/" locate-dominating-file] 4 "\n\n(fn FNAME)"]("build.gradle")
  mapcar(#[257 "\301\300\"\207" ["/home/egg/Documents/Work/harmonium/src/harmonium/ui/client/forms/" locate-dominating-file] 4 "\n\n(fn FNAME)"] ("project.clj" "build.boot" "build.gradle"))
  clojure-project-dir("/home/egg/Documents/Work/harmonium/src/harmonium/ui/client/forms/")
  cider-find-connection-buffer-for-project-directory(nil :all-connections)
  cider-current-connection()
  cider-resolve--get-in("clojure.core" "refers" #("if" 0 2 (help-echo cider--help-echo fontified t cider-locals ("rules" "data" "result" "ok?" "data" "rule-names" "rules" "data" "remote-fail-infos" "local-fail-infos" "remote" "local" "opts" "extra-fail-infos" "val" "field-path" "field-data-path" "form-path" "_" "val" "field-path" "model-name" "new-validation-infos" "previous-validation-infos" "validation-path" "actions" "validation-results" "valid?" "attr-validation-results" "_" "rules" "restricted-attr-validations" "root-entity" "_" "restrictions" "current-entity" "context" "base-path" "form-key") face font-lock-keyword-face)))
  cider-resolve-var("clojure.core" #("if" 0 2 (help-echo cider--help-echo fontified t cider-locals ("rules" "data" "result" "ok?" "data" "rule-names" "rules" "data" "remote-fail-infos" "local-fail-infos" "remote" "local" "opts" "extra-fail-infos" "val" "field-path" "field-data-path" "form-path" "_" "val" "field-path" "model-name" "new-validation-infos" "previous-validation-infos" "validation-path" "actions" "validation-results" "valid?" "attr-validation-results" "_" "rules" "restricted-attr-validations" "root-entity" "_" "restrictions" "current-entity" "context" "base-path" "form-key") face font-lock-keyword-face)))
  cider-resolve-var("harmonium.ui.client.forms.validation" #("if" 0 2 (help-echo cider--help-echo fontified t cider-locals ("rules" "data" "result" "ok?" "data" "rule-names" "rules" "data" "remote-fail-infos" "local-fail-infos" "remote" "local" "opts" "extra-fail-infos" "val" "field-path" "field-data-path" "form-path" "_" "val" "field-path" "model-name" "new-validation-infos" "previous-validation-infos" "validation-path" "actions" "validation-results" "valid?" "attr-validation-results" "_" "rules" "restricted-attr-validations" "root-entity" "_" "restrictions" "current-entity" "context" "base-path" "form-key") face font-lock-keyword-face)))
  cider--get-symbol-indent(#("if" 0 2 (help-echo cider--help-echo fontified t cider-locals ("rules" "data" "result" "ok?" "data" "rule-names" "rules" "data" "remote-fail-infos" "local-fail-infos" "remote" "local" "opts" "extra-fail-infos" "val" "field-path" "field-data-path" "form-path" "_" "val" "field-path" "model-name" "new-validation-infos" "previous-validation-infos" "validation-path" "actions" "validation-results" "valid?" "attr-validation-results" "_" "rules" "restricted-attr-validations" "root-entity" "_" "restrictions" "current-entity" "context" "base-path" "form-key") face font-lock-keyword-face)))
  clojure--get-indent-method(#("if" 0 2 (help-echo cider--help-echo fontified t cider-locals ("rules" "data" "result" "ok?" "data" "rule-names" "rules" "data" "remote-fail-infos" "local-fail-infos" "remote" "local" "opts" "extra-fail-infos" "val" "field-path" "field-data-path" "form-path" "_" "val" "field-path" "model-name" "new-validation-infos" "previous-validation-infos" "validation-path" "actions" "validation-results" "valid?" "attr-validation-results" "_" "rules" "restricted-attr-validations" "root-entity" "_" "restrictions" "current-entity" "context" "base-path" "form-key") face font-lock-keyword-face)))
  clojure--find-indent-spec-backtracking()
  clojure--find-indent-spec-backtracking()
  clojure--find-indent-spec-backtracking()
  clojure--find-indent-spec()
  clojure-indent-function(5584 (9 5547 5576 nil nil nil 0 nil nil (2401 3360 3459 3468 3473 5354 5361 5543 5547)))
  calculate-lisp-indent()
  lisp-indent-line()
  clojure-indent-line()
  indent-according-to-mode()
  indent-region(2401 7108)
  clojure-indent-region(2401 7108)
  indent-region(2401 7108)
  aggressive-indent-indent-defun(5311 5312)
  aggressive-indent--softly-indent-defun(5311 5312)
  apply(aggressive-indent--softly-indent-defun (5311 5312))
  aggressive-indent--proccess-changed-list-and-indent()
  run-hooks(before-save-hook)
  basic-save-buffer(t)
  save-buffer(1)
  funcall-interactively(save-buffer 1)
  call-interactively(save-buffer nil nil)
  command-execute(save-buffer)
Alexander-Miller commented 6 years ago

I dont do cider, but to me nothing in there looks suspicious. You can also try profiling your CPU with profiler-start & profiler-report.

eggsyntax commented 6 years ago

@Alexander-Miller good thought, but the profiler was the 1st thing I tried, no luck -- see my 1st comment above.

Alexander-Miller commented 6 years ago

I suppose you can try hooking up some of the expensive parts to trace-function, see how many times they're being called and with what values. Maybe theres room for improvement.

Should I disable the before-save-hook (and if so how?)?

That'd be something like (remove-hook 'before-save-hook #'the-name-of the-aggressive-indent-function t).

Malabarba commented 6 years ago

Yes, and the name of the function is aggressive-indent--proccess-changed-list-and-indent.

I think I see what's happening here. Clojure allows other packages to hook into the indentation process, and the way that Cider hooks in is poorly optimised. It's doing some moderately heavy operations for every symbol that it runs into.

eggsyntax commented 6 years ago

We're nearing the limits of my emacs-fu here, I fear. But: Called trace-function on aggressive-indent--proccess-changed-list-and-indent. The only output I get from that is:

1 <- aggressive-indent--proccess-changed-list-and-indent: nil

which gets called every time aggressive-indent is triggered (whether saving or not). I've gotten a slightly different output a couple of times (a few percent of the times):

1 <- aggressive-indent--proccess-changed-list-and-indent: !non-local\ exit!

However, I waited until I hit the problematic delay on save, and that time it still just output nil.

Maybe I'm misreading y'all's suggestions. As I read back now, it seems like maybe @Malabarba meant that aggressive-indent--proccess-changed-list-and-indent was the fn that I would include in remove-hook, not the fn that should be traced.

Could one of you clarify? I'm happy to try tracing some stuff if that would be helpful (ideally I could use a suggestion on which fn to add the trace to). Otherwise I'll probably just remove-hook.

Thanks as always!