Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

git prompt causes FISH to start very slowly on OS X #6625

Closed
duganchen opened this issue Feb 18, 2020 · 35 comments
Closed

git prompt causes FISH to start very slowly on OS X #6625

duganchen opened this issue Feb 18, 2020 · 35 comments
Labels
macos macOS-specific issue performance Purely performance-related enhancement without any changes in black box output
Milestone

Comments

@duganchen
Copy link

duganchen commented Feb 18, 2020

dugan ) fish --version
fish, version 3.1.0
dugan ) echo $version
3.1.0
dugan ) uname -a
Darwin MacBook-Pro.local 19.3.0 Darwin Kernel Version 19.3.0: Thu Jan  9 20:58:23 PST 2020; root:xnu-6153.81.5~1/RELEASE_X86_64 x86_64
dugan ) echo $TERM
xterm-256color-italic
dugan ) sh -c 'env HOME=$(mktemp -d) fish'

I had the common OS X issue where FISH would take seconds to load. It would only happen once per reboot. The first time I tried to start FISH after a reboot, it would take seconds to start. If you tried to start it again, it would start instantly. At least, until the next reboot.

I profiled it: and the cause seems to be having a prompt with VCS information in it? This absolutely jumped out at me:

356	7369742	--> fish_vcs_prompt
360	379	---> source /usr/local/Cellar/fish/3.1.0/share/fish/functions/fish_vcs_prompt.fish
19	19	----> function fish_vcs_prompt --description "Print the prompts for all available vcsen"
    # If a prompt succeeded, we assume that it's printed the correct info.
    # This is so we don't try svn if git already worked.
    fish_git_prompt
    or fish_hg_prompt
    or fish_svn_prompt
...
394	5204357	---> fish_git_prompt

I tried switching to a prompt without VCS information, and now FISH starts instantly. Even right after a reboot.

Both profile results ("FISH Profile with git prompt" and "FISH profile with minimalist prompt" are here:

https://gist.github.com/duganchen/7467164add3bcfa6cae0e2f2741ca8bc

This, more precisely, is what's taking so much time on startup:

277	5194027	----> set -l repo_info (command git rev-parse --git-dir --is-inside-git-dir --is-bare-repository --is-inside-work-tree HEAD 2>/dev/null)
5193750	5193750	-----> command git rev-parse --git-dir --is-inside-git-dir --is-bare-repository --is-inside-work-tree HEAD 2>/dev/null

And no, I'm (obviously) not in a git repository when FISH starts.

If I change fish_prompt_git to call git with its absolute path: well, that makes no difference:

327	4283632	----> set -l repo_info (/usr/bin/git rev-parse --git-dir --is-inside-git-dir --is-bare-repository --is-inside-work-tree HEAD 2>/dev/null)
4283305	4283305	-----> /usr/bin/git rev-parse --git-dir --is-inside-git-dir --is-bare-repository --is-inside-work-tree HEAD 2>/dev/null

An as for why git is so slow the first time it starts on OS X: I don't know. This takes several seconds, the first time after a reboot it runs:

dugan@MacBook-Pro ~ % GIT_TRACE=1 /usr/bin/git rev-parse --git-dir --is-inside-git-dir --is-bare-repository --is-inside-work-tree HEAD
15:25:54.711033 exec-cmd.c:139          trace: resolved executable path from Darwin stack: /Applications/Xcode.app/Contents/Developer/usr/bin/git
15:25:54.727587 exec-cmd.c:236          trace: resolved executable dir: /Applications/Xcode.app/Contents/Developer/usr/bin
15:25:54.739887 git.c:419               trace: built-in: git rev-parse --git-dir --is-inside-git-dir --is-bare-repository --is-inside-work-tree HEAD
fatal: not a git repository (or any of the parent directories): .git
dugan@MacBook-Pro ~ %
@duganchen
Copy link
Author

It's a lot better (but not perfect) after I edit fish_git_prompt and have it launch git from its absolute path in Xcode.app:

277	95933	----> set -l repo_info (/Applications/Xcode.app/Contents/Developer/usr/bin/git rev-parse --git-dir --is-inside-git-dir --is-bare-repository --is-inside-work-tree HEAD 2>/dev/null)
95656	95656	-----> /Applications/Xcode.app/Contents/Developer/usr/bin/git rev-parse --git-dir --is-inside-git-dir --is-bare-repository --is-inside-work-tree HEAD 2>/dev/null

@duganchen
Copy link
Author

After seeing this:

491 4192685 ---> fish_svn_prompt

I managed to solve it.

First, I did "funced fish_vcs_prompt" and deleted the following:

 or fish_hg_prompt
 or fish_svn_prompt

I then copied /usr/local/Cellar/fish/3.1.0/share/fish/functions/fish_git_prompt.fish to ~/.config/fish/functions, and edited the copy, replacing "command git" with "/Applications/Xcode.app/Contents/Developer/usr/bin/git"

FISH now starts up instantly. Even with a VCS prompt.

@floam
Copy link
Member

floam commented Feb 19, 2020

Yeah, it's the svn (and git?) prompt. xcrun has to find the Xcode-provided tools for the full path. This is really slow the first time you run after an Xcode update (or reboot? I don't know). We could maybe figure out a hack to resolve it to the full path ourselves, else fall back to using the /usr/bin path. In my mind git and svn should both take the same amount of time, not sure why svn would be slower unless it's just ran less by a user or something.

Or maybe just disable svn by default on macOS.

@floam
Copy link
Member

floam commented Feb 19, 2020

Potential hack: most people do not have homedirs that are VCS repos. On fish launch, skip doing the vcs prompt but launch no-op SVN and git commands in the background to prime it for the next prompt.

@faho
Copy link
Member

faho commented Feb 19, 2020

How about checking if the xcode path exists and using it then?

@floam
Copy link
Member

floam commented Feb 19, 2020

The Xcode path is going to vary - quickly resolving it ourselves is kind of tricky. Using xcrun to ask for the path is likely to be just as slow the firs time. We could maybe cache it. That still will be slow the very first time.

@faho
Copy link
Member

faho commented Feb 19, 2020

The Xcode path is going to vary

Sure about that? "/Applications/Xcode.app/Contents/Developer/usr/bin/git" looks quite stable.

@floam
Copy link
Member

floam commented Feb 20, 2020

absolutely sure, you could install Xcode.app anywhere - and we don't know which Xcode.app is active, Xcode.app or Xcode-beta.app or.. "my Xcode 8.app"

@floam
Copy link
Member

floam commented Feb 20, 2020

"install" is the wrong word - you can put Xcode.app anywhere, it's portable-ish. It's several gigabytes so people do move it around to save space on / and stuff.

@floam
Copy link
Member

floam commented Feb 20, 2020

It remains to be seen if xcode-select -p which outputs /Applications/Xcode-beta.app/Contents/Developer is slow in that first run circumstance. Annoying to test.

@floam
Copy link
Member

floam commented Feb 20, 2020

(The fact that the xcrun machinery exists for the wrappers installed to $PATH and xcode-select exists and the reason that the first run is slow is precisely because you can put Xcode anywhere and you can have multiple toolchains and versions of Xcode on a system.)

@floam
Copy link
Member

floam commented Feb 20, 2020

Oh, and big one - some people do not install Xcode at all (it's huge) and just install the command line tools package by running xcode-select --install or downloading a .pkg from Apple. That can be at /Developer/CommandLineTools/usr/bin/git or /Library/Developer/CommandLineTools/usr/bin/git or installed to a custom path.

@Darkshadow2
Copy link
Contributor

xcrun -f COMMAND will give you the path to that command. You could use that to cache the path, though there'd be no way to pick up if someone uses xcode-select to change the Xcode being used.

@floam
Copy link
Member

floam commented Feb 20, 2020

Does it short circuit caching? My concern would be if it blocks for a few second the first time you run it anyhow. I'll do some testing

@Darkshadow2
Copy link
Contributor

Yeah, it blocks the first time you use it. I don't think there's any way around that using Apple's tools.

@floam
Copy link
Member

floam commented Feb 20, 2020

I think there's a way around it by taking advantage of something else most likely cached previously and synthesizing the path.

@floam
Copy link
Member

floam commented Feb 20, 2020

e.g. xcrun -f clang | string replace -r clang git compared to how long xcrun -f ls | string replace -r ls git takes. (ls obviously not being provided by the SDK so it's taking as long as a first-run git or anything else.)

@Darkshadow2
Copy link
Contributor

That specific case won't work, as the clang executable is in a different place than the git one.

Maybe xcrun -f xcodebuild? Although I'm not sure if that's only working because it got cached earlier.

@floam
Copy link
Member

floam commented Feb 20, 2020

Whoops, busted example.

@Darkshadow2
Copy link
Contributor

Huh, interesting. If you give xcrun -f an invalid command it gives you this error:

xcrun: error: sh -c '/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -sdk macosx -find idonotexist 2> /dev/null' failed with exit code 17664: (null) (errno=No such file or directory)
xcrun: error: unable to find utility "idonotexist", not a developer tool or in PATH

So it looks like it's not even actually xcrun that's looking up the paths...

@floam
Copy link
Member

floam commented Feb 20, 2020

Nice!

@Darkshadow2
Copy link
Contributor

Ah, but if you use xcodebuild -sdk macosx -find COMMAND directly, there's no caching. It always takes a bit to find something.

@floam
Copy link
Member

floam commented Feb 20, 2020

the dylib that causes the wrapping takes SDKROOT into account as well, so it should be possible once we have a way to quickly get the SDK path regardless of cache (xcrun --show-sdk-path?) we can just export something and a bare git or anything else should work. I think. Or maybe not.

@duganchen
Copy link
Author

duganchen commented Feb 22, 2020

These paths aren't going to change often, so I'd propose baking the xcrun results into "set -U" universal variables instead of running xcrun each time FISH starts.

And for cache invalidation: I'd just check if the path still exists, either on startup on when you display the prompt.

The following script, which is meant to be executed with an ampersand, is what I have in mind:

if not test -x $GIT_PATH
    set -U GIT_PATH (xcrun -f git)
end

@duganchen

This comment was marked as off-topic.

@faho

This comment was marked as off-topic.

@drewish
Copy link

drewish commented Apr 7, 2020

I've noticed another issue that seems to be slowing down the prompt is locating svn (I removed my prompt since it had some unrelated information displayed):

$ fish -p FILE -c 'fish_prompt'
$ sort -n FILE | tail -n 8
6655	6655	---------> read -lz key value
10674	10674	-----> command git rev-parse --git-dir --is-inside-git-dir --is-bare-repository --is-inside-work-tree HEAD 2>/dev/null
10903	10903	---------> command git diff --staged --name-status | string match -r \\w
12420	12420	---------> command git rev-list --count --left-right $upstream...HEAD 2>/dev/null
12935	12935	--------> command git symbolic-ref HEAD 2>/dev/null
18197	18197	---------> command git diff --name-status 2>/dev/null | string match -r \\w
23973	23973	---------> command git ls-files --others --exclude-standard | wc -l | string trim
594636	594636	-----> command svn info 2>/dev/null
$ svn info
svn: error: Failed to locate 'svn'.
svn: error: The subversion command line tools are no longer provided by Xcode.
$ time svn info
svn: error: Failed to locate 'svn'.
svn: error: The subversion command line tools are no longer provided by Xcode.
        1.02 real         0.35 user         0.40 sys

@drewish
Copy link

drewish commented Apr 7, 2020

Oh looks like my SVN issue was fixed by #6681

@zanchey zanchey added the performance Purely performance-related enhancement without any changes in black box output label Apr 29, 2020
@zanchey zanchey added this to the fish-future milestone Apr 29, 2020
@peterstory
Copy link

I think this issue can be closed, based on the fix in #6681 having been incorporated into 3.1.1 and later. Fish is speedy again! 🎉

@ridiculousfish
Copy link
Member

Cool! Thanks!

@zanchey zanchey modified the milestones: fish-future, fish 3.1.1 Jun 4, 2020
@faho
Copy link
Member

faho commented Jun 4, 2020

It's... not fixed?

The svn prompt was one issue, the initial problem however was that macOS does weird things with git, and I don't think that's changed?

I mean I'd personally be entirely okay with calling that an OS bug, but then I'm not using macOS.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 2, 2020
@floam
Copy link
Member

floam commented Nov 12, 2022

Yeah this isn't fixed.

@floam floam reopened this Nov 12, 2022
@floam floam modified the milestones: fish 3.1.1, fish-future Nov 12, 2022
@floam floam changed the title VCS prompt causes FISH to start very slowly on OS X git prompt causes FISH to start very slowly on OS X Nov 12, 2022
@floam
Copy link
Member

floam commented Nov 12, 2022

Update since the last time I commented here: this happens specifically after each reboot, because that clears $TMPDIR. libxcselect.dylib is regenerating the cache at $TMPDIR/xcrun_db.

It specifically takes so long because syspolicyd is spending a lot of time in the security framework checking code signing across the Xcode bundle.

@floam
Copy link
Member

floam commented Nov 12, 2022

So there is a practical workaround: if $TMPDIR/xcrun_db doesn't exist, we run something like git --version& backgrounded. That'll kick off the cache being rebuilt without making fish appear to hang in the meantime. Likely the user won't even notice that a lack of VCS status in their prompts printed during those ten seconds or whatever, since most home directories are not git repos.

floam added a commit to floam/fish-shell that referenced this issue Nov 12, 2022
This prevents fish appearing to hang on launch on macOS.

/usr/bin/git is a shim that finds Git in the Xcode or Command Line
Tools Developer dir. There is a cache file kept in TMPRDIR
that is cleared on reboot. The validity of the Xcode installation
is verified as well for security reasons, causing this to be
potentially really slow.

This runs a git no-op command in the background when we autoload
fish_git_prompt if the db file is missing on Darwin, with a
self-destructing callback that repaints the commandline when it's
done.

If the callback function exists, it means we're currently waiting
for the cold-start git command to finish, and do not print a
git_prompt.

Fixes fish-shell#6625
@floam
Copy link
Member

floam commented Nov 12, 2022

I addressed this in PR #9345.

Even if fish is launched with a CWD of a git repo, the behavior is pretty pleasant now, with the prompt and command line updating as soon as the cache file exists, the VCS component just kind of pops in late.

@floam floam added the macos macOS-specific issue label Nov 12, 2022
ridiculousfish added a commit to ridiculousfish/fish-shell that referenced this issue Dec 27, 2022
git on macOS has two hazards:

1. It comes "preinstalled" as a stub which pops a dialog to install
   command line developer tools.

2. It may populate the xcrun cache when run for the first time, which
   may take several seconds.

We fix these as follows, both fixes limited to Darwin:

1. If git is `/usr/bin/git` and `xcode-select --print-path` fails,
   then do not run git automatically.

2. Second, if there is no file at `xcrun --show-cache-path`, we take it
   as an indication that the cache is not yet populated. In this case we
   run `git` in the background to populate the cache.

Credit to @floam for the idea.

Fixes fish-shell#9343. Fixes fish-shell#6625.
ridiculousfish added a commit to ridiculousfish/fish-shell that referenced this issue Dec 27, 2022
git on macOS has two hazards:

1. It comes "preinstalled" as a stub which pops a dialog to install
   command line developer tools.

2. It may populate the xcrun cache when run for the first time, which
   may take several seconds.

We fix these as follows, both fixes limited to Darwin:

1. If git is `/usr/bin/git` and `xcode-select --print-path` fails,
   then do not run git automatically.

2. Second, if there is no file at `xcrun --show-cache-path`, we take it
   as an indication that the cache is not yet populated. In this case we
   run `git` in the background to populate the cache.

Credit to @floam for the idea.

Fixes fish-shell#9343. Fixes fish-shell#6625.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
macos macOS-specific issue performance Purely performance-related enhancement without any changes in black box output
Projects
None yet
Development

No branches or pull requests

8 participants