Skip to content

Commit

Permalink
cli: global --show-heap-stats argument for EOL heap stats
Browse files Browse the repository at this point in the history
Summary: One of the more useful features of mimalloc. In the future this
interface could be expanded to support printing to a callback, so that it
could be triggered and dumped e.g. on debugging endpoints for longer running
processes, when we get there.

However, it's not enough to just add it to the `main` module; not only would it
be ugly and overspecific, it should be reusable so other custom `jj` commands
can opt-in to statistics trivially as well. Users should be strongly encouraged
to do this -- enable mimalloc and stat tracking -- since that's what upstream
will now work and test with; but, of course, all things are optional.

Thus, we expose an API from the `cli_utils` module for use with
`cli_util::CliRunner::add_global_args`, but ultimately it's up to the client to
call it.

Signed-off-by: Austin Seipp <[email protected]>
Change-Id: I6abe4b962bbe7c62ebca97be630b750c
  • Loading branch information
thoughtpolice committed Feb 23, 2024
1 parent cfd9933 commit c606f2d
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 3 deletions.
54 changes: 53 additions & 1 deletion cli/src/cli_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@ use std::path::{Path, PathBuf};
use std::process::ExitCode;
use std::rc::Rc;
use std::str::FromStr;
use std::sync::Arc;
use std::sync::{Arc, OnceLock};
use std::time::SystemTime;
use std::{fs, iter, str};

use clap::builder::{NonEmptyStringValueParser, TypedValueParser, ValueParserFactory};
use clap::{Arg, ArgAction, ArgMatches, Command, FromArgMatches};
use indexmap::{IndexMap, IndexSet};
use itertools::Itertools;
use jj_cbits::mimalloc;
use jj_lib::backend::{BackendError, ChangeId, CommitId, MergedTreeId};
use jj_lib::commit::Commit;
use jj_lib::git::{GitConfigParseError, GitExportError, GitImportError, GitRemoteManagementError};
Expand Down Expand Up @@ -2529,6 +2530,57 @@ pub struct EarlyArgs {
pub config_toml: Vec<String>,
}

#[derive(clap::Args, Clone, Debug)]
pub struct ShowAllocStats {
/// Show memory allocation statistics from the internal heap allocator
/// on `stdout`, when the program exits.
#[arg(long, global = true)]
show_heap_stats: bool,
}

/// Lazy global static. Used only to defer printing mimalloc stats until the
/// program exits, if set to `true`.
static PRINT_HEAP_STATS: OnceLock<bool> = OnceLock::new();

/// Enable heap statistics for the user interface; should be used with
/// [`CliRunner::add_global_args`]. Does nothing if the memory allocator is
/// unused, i.e. `#[global_allocator]` is not set to mimalloc in your program.
pub fn heap_stats_enable(_ui: &mut Ui, opts: ShowAllocStats) -> Result<(), CommandError> {
if opts.show_heap_stats {
PRINT_HEAP_STATS.set(true).unwrap();
}
Ok(())
}

/// Reset heap allocation statistics for the memory allocator. Often used at the
/// very beginning of the program (to clear allocations that may happen before
/// `_main` execution.)
pub fn heap_stats_reset() {
mimalloc::stats_reset();
}

/// Print heap allocation statistics to `stderr`, if enabled.
pub fn heap_stats_print() {
// NOTE (aseipp): can we do our own custom printing here? it's kind of ugly
if PRINT_HEAP_STATS.get() == Some(&true) {
eprintln!("========================================");
eprintln!("mimalloc memory allocation statistics:\n");
mimalloc::stats_print(&|l| {
eprint!("{}", l.to_string_lossy());
});
}
}

/// Wrap a given closure with calls to [`heap_stats_reset`] and
/// [`heap_stats_print`], and return the result of the closure. Useful for
/// conveiently printing heap allocation statistics for a given function body.
pub fn heap_stats_with_closure<R>(f: impl FnOnce() -> R) -> R {
heap_stats_reset();
let result = f();
heap_stats_print();
result
}

/// Create a description from a list of paragraphs.
///
/// Based on the Git CLI behavior. See `opt_parse_m()` and `cleanup_mode` in
Expand Down
11 changes: 9 additions & 2 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.

//! Primary entrypoint for the stock `jj` command-line tool. mimalloc enabled.
use jj_cbits::mimalloc::MiMalloc;
use jj_cli::cli_util::CliRunner;
use jj_cli::cli_util::{heap_stats_enable, heap_stats_with_closure, CliRunner};

#[global_allocator]
static ALLOC: MiMalloc = MiMalloc;

fn main() -> std::process::ExitCode {
CliRunner::init().version(env!("JJ_VERSION")).run()
heap_stats_with_closure(|| {
CliRunner::init()
.add_global_args(heap_stats_enable)
.version(env!("JJ_VERSION"))
.run()
})
}
4 changes: 4 additions & 0 deletions cli/tests/[email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ repository.
Possible values: `true`, `false`
* `--config-toml <TOML>` — Additional configuration options (can be repeated)
* `--show-heap-stats` — Show memory allocation statistics from the internal heap allocator on `stdout`, when the program exits
Possible values: `true`, `false`
Expand Down
2 changes: 2 additions & 0 deletions cli/tests/test_global_opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,8 @@ fn test_help() {
--color <WHEN> When to colorize output (always, never, auto)
--no-pager Disable the pager
--config-toml <TOML> Additional configuration options (can be repeated)
--show-heap-stats Show memory allocation statistics from the internal heap
allocator on `stdout`, when the program exits
"###);
}

Expand Down

0 comments on commit c606f2d

Please sign in to comment.