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

Add bootsnap for bootup performance #17809

Merged

Conversation

adfoster-r7
Copy link
Contributor

@adfoster-r7 adfoster-r7 commented Mar 23, 2023

Adds caching to Ruby's load path to improve the bootup performance of msfconsole on startup on macOS from ~13 seconds to ~9.7 seconds

Context

I noticed when running strace to dump out the Linux system calls on msfconsole bootup, that when msfconsole attempts to load a Ruby file such as with require 'foo' you can see Ruby attempting to find the source code on disk in multiple places:

openat(... gem1/foo.rb ...) ENOENT (No such file or directory)
openat(... gem2/foo.rb ...) ENOENT (No such file or directory)
openat(... gem3/foo.rb ...) ENOENT (No such file or directory)
openat(... gem4/foo.rb ...) ENOENT (No such file or directory)
... etc ...

Kali example:

image

Specifically Ruby is checking the global $LOAD_PATH array which tracks the location of all the loaded gems/libraries that Metasploit depends on to find the required Ruby file. Since Metasploit's $LOAD_PATH is pretty big from the amount of gems we have, attempting to load a single Ruby file involves a lot of expensive trial and error until it finds the right file on disk:

>> $LOAD_PATH.length
=> 236

Example values of $LOAD_PATH

>> $LOAD_PATH
=> 
["/Users/user/Documents/code/metasploit-framework/lib",
 "/Users/user/Documents/code/metasploit-framework/app/models",
 "/Users/user/Documents/code/metasploit-framework/app/concerns",
 "/Users/user/Documents/code/metasploit-framework/app/validators",
 "/Users/user/.rvm/gems/ruby-3.0.5@metasploit-framework/gems/metasploit-credential-6.0.2/app/concerns",
  ... etc ...
]

Bootsnap can be used to optimize this look up by performing Path Pre-Scanning - i.e. Kernel#require and Kernel#load are modified to eliminate $LOAD_PATH scans

Verification

  • Code review
  • Confirm the before/after time for:
    • mac - Before: ~13, after ~9.7
    • windows - Before: 28.287, After: 26.2
    • kali - Before: 11.23, after: 9.359
    • pro -
  • Confirm there's no issues the readline $LOAD_PATH hack here:
    if opts['RealReadline']
    # Remove the gem version from load path to be sure we're getting the
    # stdlib readline.
    gem_dir = Gem::Specification.find_all_by_name('rb-readline').first.gem_dir
    rb_readline_path = File.join(gem_dir, "lib")
    index = $LOAD_PATH.index(rb_readline_path)
    # Bundler guarantees that the gem will be there, so it should be safe to
    # assume we found it in the load path, but check to be on the safe side.
    if index
    $LOAD_PATH.delete_at(index)
    end
    end
    begin
    require 'readline'
    • Both bundle exec ruby ./msfconsole -q --real-readline and bundle exec ruby ./msfconsole -q --no-readline appear to work as expected

@adfoster-r7 adfoster-r7 force-pushed the add-bootsnap-for-bootup-performance branch 4 times, most recently from 70bad93 to a8107ed Compare March 23, 2023 13:25
}

cache_metadata_path = File.join(bootsnap_config[:cache_dir], "metadata.yaml")
if File.exist?(cache_metadata_path)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metadata

$ cat ~/.msf4/bootsnap_cache/metadata.yaml
---
metasploit_framework_version: 6.3.9
ruby_description: ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c5) [x86_64-darwin20]
bundler_lockfile_hash: 00453b181bd04b9dfc411f0151514c1b
bootsnap_config:
  load_path_cache: true
  compile_cache_iseq: false
  compile_cache_yaml: false

@adfoster-r7 adfoster-r7 force-pushed the add-bootsnap-for-bootup-performance branch 3 times, most recently from 56667a3 to a1e1c10 Compare March 28, 2023 10:56
@adfoster-r7 adfoster-r7 marked this pull request as ready for review March 30, 2023 10:26
@adfoster-r7 adfoster-r7 force-pushed the add-bootsnap-for-bootup-performance branch from a1e1c10 to 653234e Compare April 4, 2023 09:36
@dwelch-r7 dwelch-r7 self-assigned this Apr 14, 2023
@dwelch-r7 dwelch-r7 merged commit 2c8ad1f into rapid7:master Apr 14, 2023
30 checks passed
@dwelch-r7 dwelch-r7 added the rn-enhancement release notes enhancement label Apr 14, 2023
@dwelch-r7
Copy link
Contributor

dwelch-r7 commented Apr 14, 2023

Release Notes

Adds caching to Ruby's load path logic to improve the bootup performance of msfconsole on startup, averaging 2-3 seconds faster boot time on the tested hardware

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rn-enhancement release notes enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants