diff --git a/doc_src/cmds/fish.rst b/doc_src/cmds/fish.rst index 01929d863..d82d3e912 100644 --- a/doc_src/cmds/fish.rst +++ b/doc_src/cmds/fish.rst @@ -31,7 +31,9 @@ The following options are available: - ``-n`` or ``--no-execute`` do not execute any commands, only perform syntax checking -- ``-p`` or ``--profile=PROFILE_FILE`` when fish exits, output timing information on all executed commands to the specified file +- ``-p`` or ``--profile=PROFILE_FILE`` when fish exits, output timing information on all executed commands to the specified file. This excludes time spent starting up and reading the configuration. + +- ``--profile-startup=PROFILE_FILE`` will write timing information for fish's startup to the specified file. This is useful to profile your configuration. - ``-P`` or ``--private`` enables :ref:`private mode `, so fish will not access old or store new history. diff --git a/share/completions/fish.fish b/share/completions/fish.fish index 63c55536f..149a18a94 100644 --- a/share/completions/fish.fish +++ b/share/completions/fish.fish @@ -5,7 +5,8 @@ complete -c fish -s v -l version -d "Display version and exit" complete -c fish -s n -l no-execute -d "Only parse input, do not execute" complete -c fish -s i -l interactive -d "Run in interactive mode" complete -c fish -s l -l login -d "Run as a login shell" -complete -c fish -s p -l profile -d "Output profiling information to specified file" -r +complete -c fish -s p -l profile -d "Output profiling information (excluding startup) to a file" -r +complete -c fish -s p -l profile-startup -d "Output startup profiling information to a file" -r complete -c fish -s d -l debug -d "Specify debug categories" -x -a "(fish --print-debug-categories | string replace ' ' \t)" complete -c fish -s o -l debug-output -d "Where to direct debug output to" -rF complete -c fish -s D -l debug-stack-frames -d "Show specified # of frames with debug output" -x -a "(seq 128)\t\n" diff --git a/src/fish.cpp b/src/fish.cpp index f5e198d14..b601cba8c 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -67,6 +67,7 @@ class fish_cmd_opts_t { std::string debug_output; // File path for profiling output, or empty for none. std::string profile_output; + std::string profile_startup_output; // Commands to be executed in place of interactive shell. std::vector batch_cmds; // Commands to execute after the shell's config has been read. @@ -274,6 +275,7 @@ static int fish_parse_opt(int argc, char **argv, fish_cmd_opts_t *opts) { {"print-rusage-self", no_argument, nullptr, 1}, {"print-debug-categories", no_argument, nullptr, 2}, {"profile", required_argument, nullptr, 'p'}, + {"profile-startup", required_argument, nullptr, 3}, {"private", no_argument, nullptr, 'P'}, {"help", no_argument, nullptr, 'h'}, {"version", no_argument, nullptr, 'v'}, @@ -353,7 +355,14 @@ static int fish_parse_opt(int argc, char **argv, fish_cmd_opts_t *opts) { exit(0); } case 'p': { + // "--profile" - this does not activate profiling right away, + // rather it's done after startup is finished. opts->profile_output = optarg; + break; + } + case 3: { + // With "--profile-startup" we immediately turn profiling on. + opts->profile_startup_output = optarg; g_profiling_active = true; break; } @@ -479,6 +488,18 @@ int main(int argc, char **argv) { // Stomp the exit status of any initialization commands (issue #635). parser.set_last_statuses(statuses_t::just(STATUS_CMD_OK)); + // If we're profiling startup to a separate file, write it now. + if (!opts.profile_startup_output.empty() + && opts.profile_startup_output != opts.profile_output) { + parser.emit_profiling(opts.profile_startup_output.c_str()); + + // If we are profiling both, ensure the startup data only + // ends up in the startup file. + parser.clear_profiling(); + } + + g_profiling_active = !opts.profile_output.empty(); + // Run post-config commands specified as arguments, if any. if (!opts.postconfig_cmds.empty()) { res = run_command_list(parser, &opts.postconfig_cmds, {}); @@ -544,7 +565,7 @@ int main(int argc, char **argv) { restore_term_mode(); restore_term_foreground_process_group_for_exit(); - if (g_profiling_active) { + if (!opts.profile_output.empty()) { parser.emit_profiling(opts.profile_output.c_str()); } diff --git a/src/parser.cpp b/src/parser.cpp index fe4a7bdc1..0c22b0ac9 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -272,6 +272,10 @@ static void print_profile(const std::deque &items, FILE *out) { } } +void parser_t::clear_profiling() { + profile_items.clear(); +} + void parser_t::emit_profiling(const char *path) const { // Save profiling information. OK to not use CLO_EXEC here because this is called while fish is // exiting (and hence will not fork). diff --git a/src/parser.h b/src/parser.h index aec1e934c..c93aa5ccd 100644 --- a/src/parser.h +++ b/src/parser.h @@ -400,6 +400,9 @@ class parser_t : public std::enable_shared_from_this { /// If profiling is not active, this returns nullptr. profile_item_t *create_profile_item(); + /// Remove the profiling items. + void clear_profiling(); + /// Output profiling data to the given filename. void emit_profiling(const char *path) const; diff --git a/tests/checks/invocation.fish b/tests/checks/invocation.fish index 005d3e501..a8c7cbb35 100644 --- a/tests/checks/invocation.fish +++ b/tests/checks/invocation.fish @@ -56,3 +56,25 @@ $fish -c 'string escape y$argv' -c 'string escape x$argv' 1 2 3 # Should just do nothing. $fish --no-execute + +set -l tmp (mktemp -d) +$fish --profile $tmp/normal.prof --profile-startup $tmp/startup.prof -ic exit + +# This should be the full file - just the one command we gave explicitly! +cat $tmp/normal.prof +# CHECK: Time{{\s+}}Sum{{\s+}}Command +# CHECK: {{\d+\s+\d+\s+>}} exit + +string match -rq "builtin source " < $tmp/startup.prof +and echo matched +# CHECK: matched + +# See that sending both profiles to the same file works. +$fish --profile $tmp/full.prof --profile-startup $tmp/full.prof -c 'echo thisshouldneverbeintheconfig' +# CHECK: thisshouldneverbeintheconfig +string match -rq "builtin source " < $tmp/full.prof +and echo matched +# CHECK: matched +string match -rq "echo thisshouldneverbeintheconfig" < $tmp/full.prof +and echo matched +# CHECK: matched