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

Implement --deterministic, for chaotic but repeatable color selection #190

Merged
merged 10 commits into from
Oct 5, 2020
10 changes: 8 additions & 2 deletions src/bin/flamegraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,15 @@ struct Opt {
#[structopt(long = "cp")]
cp: bool,

/// Colors are keyed by function name hash
#[structopt(long = "hash")]
/// Colors are selected by hashing the function name, weighting earlier characters more
/// heavily
#[structopt(long = "hash", conflicts_with = "deterministic")]
hash: bool,

/// Colors are selected such that the color of a function does not change between runs
#[structopt(long = "deterministic")]
saethlin marked this conversation as resolved.
Show resolved Hide resolved
deterministic: bool,

/// Plot the flame graph up-side-down
#[structopt(short = "i", long = "inverted")]
inverted: bool,
Expand Down Expand Up @@ -208,6 +213,7 @@ impl<'a> Opt {
options.colors = self.colors;
options.bgcolors = self.bgcolors;
options.hash = self.hash;
options.deterministic = self.deterministic;

self.set_func_frameattrs(&mut options);

Expand Down
14 changes: 14 additions & 0 deletions src/flamegraph/color/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ fn rgb_components_for_palette(palette: Palette, name: &str, v1: f32, v2: f32, v3
pub(super) fn color(
palette: Palette,
hash: bool,
deterministic: bool,
name: &str,
mut rng: impl FnMut() -> f32,
) -> Color {
Expand All @@ -339,6 +340,19 @@ pub(super) fn color(
let reverse_name_hash = namehash(name.bytes().rev());

(name_hash, reverse_name_hash, reverse_name_hash)
} else if deterministic {
use std::hash::Hasher;
let prefixed_hash = |name: &str, prefix: u8| {
let mut hasher = ahash::AHasher::default();
hasher.write_u8(prefix);
hasher.write(name.as_bytes());
(hasher.finish() as f64 / u64::MAX as f64) as f32
};
saethlin marked this conversation as resolved.
Show resolved Hide resolved
(
prefixed_hash(name, 0),
prefixed_hash(name, 1),
prefixed_hash(name, 2),
)
} else {
(rng(), rng(), rng())
};
Expand Down
9 changes: 8 additions & 1 deletion src/flamegraph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ pub struct Options<'a> {
/// This will cause similar functions to be colored similarly.
pub hash: bool,

/// Choose names based on the hashes of function names, without the weighting scheme that
/// `hash` uses.
pub deterministic: bool,

/// Store the choice of color for each function so that later invocations use the same colors.
///
/// With this option enabled, a file called `palette.map` will be created the first time a
Expand Down Expand Up @@ -297,6 +301,7 @@ impl<'a> Default for Options<'a> {
subtitle: Default::default(),
bgcolors: Default::default(),
hash: Default::default(),
deterministic: Default::default(),
palette_map: Default::default(),
direction: Default::default(),
negate_differentials: Default::default(),
Expand Down Expand Up @@ -623,13 +628,15 @@ where
} else if let Some(ref mut palette_map) = opt.palette_map {
let colors = opt.colors;
let hash = opt.hash;
let deterministic = opt.deterministic;
palette_map.find_color_for(&frame.location.function, |name| {
color::color(colors, hash, name, &mut thread_rng)
color::color(colors, hash, deterministic, name, &mut thread_rng)
})
} else {
color::color(
opt.colors,
opt.hash,
opt.deterministic,
frame.location.function,
&mut thread_rng,
)
Expand Down