From 594d51e7ebd0cf9fee1ff15cf38a1144374bd56f Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 8 Jan 2021 17:25:00 +0100 Subject: [PATCH] Add a separate --profile-startup option to profile startup This goes to a separate file because that makes option parsing easier and allows profiling both at the same time. The "normal" profile now contains only the profile data of the actual run, which is much more useful - you can now profile a function by running fish -C 'source /path/to/thing' --profile /tmp/thefunction.prof -c 'thefunction' and won't need to filter out extraneous information. --- doc_src/cmds/fish.rst | 4 +++- share/completions/fish.fish | 3 ++- src/fish.cpp | 23 ++++++++++++++++++++++- src/parser.cpp | 4 ++++ src/parser.h | 3 +++ tests/checks/invocation.fish | 22 ++++++++++++++++++++++ 6 files changed, 56 insertions(+), 3 deletions(-) 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