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

Use a hash for the different color palettes in flamegraph.pl #345

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
342 changes: 193 additions & 149 deletions flamegraph.pl
Original file line number Diff line number Diff line change
Expand Up @@ -395,158 +395,202 @@ sub random_namehash {
return rand(1)
}

sub color {
my ($type, $hash, $name) = @_;
my ($v1, $v2, $v3);

if ($hash) {
$v1 = namehash($name);
$v2 = $v3 = namehash(scalar reverse $name);
} elsif ($rand) {
$v1 = rand(1);
$v2 = rand(1);
$v3 = rand(1);
} else {
$v1 = random_namehash($name);
$v2 = random_namehash($name);
$v3 = random_namehash($name);
}

# theme palettes
if (defined $type and $type eq "hot") {
my $r = 205 + int(50 * $v3);
my $g = 0 + int(230 * $v1);
my $b = 0 + int(55 * $v2);
return "rgb($r,$g,$b)";
}
if (defined $type and $type eq "mem") {
my $r = 0;
my $g = 190 + int(50 * $v2);
my $b = 0 + int(210 * $v1);
return "rgb($r,$g,$b)";
}
if (defined $type and $type eq "io") {
my $r = 80 + int(60 * $v1);
my $g = $r;
my $b = 190 + int(55 * $v2);
return "rgb($r,$g,$b)";
}
# Color name to RGB mapping
my %color_map = (
'aqua' => 'rgb(0,255,255)',
'green' => 'rgb(0,128,0)',
'yellow' => 'rgb(255,255,0)',
'orange' => 'rgb(255,165,0)',
'red' => 'rgb(255,0,0)',
);

# multi palettes
if (defined $type and $type eq "java") {
# Handle both annotations (_[j], _[i], ...; which are
# accurate), as well as input that lacks any annotations, as
# best as possible. Without annotations, we get a little hacky
# and match on java|org|com, etc.
if ($name =~ m:_\[j\]$:) { # jit annotation
$type = "green";
} elsif ($name =~ m:_\[i\]$:) { # inline annotation
$type = "aqua";
} elsif ($name =~ m:^L?(java|javax|jdk|net|org|com|io|sun)/:) { # Java
$type = "green";
} elsif ($name =~ /:::/) { # Java, typical perf-map-agent method separator
$type = "green";
} elsif ($name =~ /::/) { # C++
$type = "yellow";
} elsif ($name =~ m:_\[k\]$:) { # kernel annotation
$type = "orange";
} elsif ($name =~ /::/) { # C++
$type = "yellow";
} else { # system
$type = "red";
}
# fall-through to color palettes
}
if (defined $type and $type eq "perl") {
if ($name =~ /::/) { # C++
$type = "yellow";
} elsif ($name =~ m:Perl: or $name =~ m:\.pl:) { # Perl
$type = "green";
} elsif ($name =~ m:_\[k\]$:) { # kernel
$type = "orange";
} else { # system
$type = "red";
}
# fall-through to color palettes
}
if (defined $type and $type eq "js") {
# Handle both annotations (_[j], _[i], ...; which are
# accurate), as well as input that lacks any annotations, as
# best as possible. Without annotations, we get a little hacky,
# and match on a "/" with a ".js", etc.
if ($name =~ m:_\[j\]$:) { # jit annotation
if ($name =~ m:/:) {
$type = "green"; # source
} else {
$type = "aqua"; # builtin
}
} elsif ($name =~ /::/) { # C++
$type = "yellow";
} elsif ($name =~ m:/.*\.js:) { # JavaScript (match "/" in path)
$type = "green";
} elsif ($name =~ m/:/) { # JavaScript (match ":" in builtin)
$type = "aqua";
} elsif ($name =~ m/^ $/) { # Missing symbol
$type = "green";
} elsif ($name =~ m:_\[k\]:) { # kernel
$type = "orange";
} else { # system
$type = "red";
}
# fall-through to color palettes
}
if (defined $type and $type eq "wakeup") {
$type = "aqua";
# fall-through to color palettes
}
if (defined $type and $type eq "chain") {
if ($name =~ m:_\[w\]:) { # waker
$type = "aqua"
} else { # off-CPU
$type = "blue";
}
# fall-through to color palettes
}
# Define color palettes
my %color_palettes = (
'hot' => {
'default' => sub {
my ($r, $g, $b) = @_;
return sprintf("rgb(%d,%d,%d)",
205 + int(50 * $b),
0 + int(230 * $r),
0 + int(55 * $g));
},
},
'mem' => {
'default' => sub {
my ($r, $g, $b) = @_;
return sprintf("rgb(%d,%d,%d)",
0,
190 + int(50 * $g),
0 + int(210 * $r));
},
},
'io' => {
'default' => sub {
my ($r, $g, $b) = @_;
return sprintf("rgb(%d,%d,%d)",
80 + int(60 * $r),
80 + int(60 * $r),
190 + int(55 * $g));
},
},
'java' => {
'_\[j\]$' => sub { return 'green'; },
'_\[i\]$' => sub { return 'aqua'; },
'^L?(java|javax|jdk|net|org|com|io|sun)/' => sub { return 'green'; },
':::' => sub { return 'green'; },
'::' => sub { return 'yellow'; },
'_\[k\]$' => sub { return 'orange'; },
'default' => sub { return 'red'; },
},
'perl' => {
'::' => sub { return 'yellow'; },
'Perl:|\.pl' => sub { return 'green'; },
'_\[k\]$' => sub { return 'orange'; },
'default' => sub { return 'red'; },
},
'js' => {
'_\[j\]$' => sub {
my ($r, $g, $b, $name) = @_;
return $name =~ m:/: ? 'green' : 'aqua';
},
'::' => sub { return 'yellow'; },
'/.*\.js' => sub { return 'green'; },
':' => sub { return 'aqua'; },
'^ $' => sub { return 'green'; },
'_\[k\]' => sub { return 'orange'; },
'default' => sub { return 'red'; },
},
'crdb' => {
'__default' => sub { return "#f5f5f5"; }, # White Smoke
'__searchcolor' => "#ffff00", # Bright yellow for search

'github\.com/cockroachdb/cockroach/pkg/sql' => sub { return "#4a86e8"; }, # Light Blue
'github\.com/cockroachdb/cockroach/pkg/kv/kvserver' => sub { return "#ff9999"; }, # Light Red
'github\.com/cockroachdb/cockroach/pkg/kv/kvclient' => sub { return "#ffcccc"; }, # Very Light Red
'github\.com/cockroachdb/cockroach/pkg/util' => sub { return "#90ee90"; }, # Light Green
'github\.com/cockroachdb/cockroach/pkg/sql/colflow' => sub { return "#dda0dd"; }, # Light Purple
'github\.com/cockroachdb/cockroach/pkg/sql/colexec' => sub { return "#e6e6fa"; }, # Lavender
'google\.golang\.org/grpc' => sub { return "#ffa500"; }, # Orange
'github\.com/cockroachdb/cockroach/pkg/rpc' => sub { return "#ffdab9"; }, # Peach Puff
'github\.com/cockroachdb/pebble' => sub { return "#20b2aa"; }, # Light Sea Green
'github\.com/cockroachdb/cockroach/pkg/kv/kvserver/rangefeed' => sub { return "#ffffe0"; }, # Light Yellow

# Runtime - lighter aqua colors
'runtime\.goexit' => sub { return "#e0ffff"; }, # Light Cyan
'runtime\.main' => sub { return "#afeeee"; }, # Pale Turquoise
'runtime\.mcall' => sub { return "#7fffd4"; }, # Aquamarine
'runtime\.morestack' => sub { return "#48d1cc"; }, # Medium Turquoise
'runtime\.newproc' => sub { return "#40e0d0"; }, # Turquoise
'runtime\.gopark' => sub { return "#00ced1"; }, # Dark Turquoise
'runtime\.gosched' => sub { return "#5f9ea0"; }, # Cadet Blue
'runtime\.gcBgMarkWorker' => sub { return "#4682b4"; }, # Steel Blue
'runtime\.bgsweep' => sub { return "#6495ed"; }, # Cornflower Blue
'runtime\.selectgo' => sub { return "#87cefa"; }, # Light Sky Blue
'runtime\.' => sub { return "#b0e0e6"; }, # Powder Blue

# Network-related - lighter shades of blue
'net\.' => sub { return "#1e90ff"; }, # Dodger Blue
'internal/poll\.FD' => sub { return "#00bfff"; }, # Deep Sky Blue
'crypto/tls' => sub { return "#87cefa"; }, # Light Sky Blue
'internal/poll' => sub { return "#b0c4de"; }, # Light Steel Blue

# IO - lighter purple
'io\.' => sub { return "#da70d6"; }, # Orchid

# Sync - lighter red
'sync\.' => sub { return "#ff6347"; }, # Tomato
'sync\.runtime_notifyListWait' => sub { return "#ff6347"; }, # Tomato (same as sync.)
},
# Add other palettes here...
);

# color palettes
if (defined $type and $type eq "red") {
my $r = 200 + int(55 * $v1);
my $x = 50 + int(80 * $v1);
return "rgb($r,$x,$x)";
}
if (defined $type and $type eq "green") {
my $g = 200 + int(55 * $v1);
my $x = 50 + int(60 * $v1);
return "rgb($x,$g,$x)";
}
if (defined $type and $type eq "blue") {
my $b = 205 + int(50 * $v1);
my $x = 80 + int(60 * $v1);
return "rgb($x,$x,$b)";
}
if (defined $type and $type eq "yellow") {
my $x = 175 + int(55 * $v1);
my $b = 50 + int(20 * $v1);
return "rgb($x,$x,$b)";
}
if (defined $type and $type eq "purple") {
my $x = 190 + int(65 * $v1);
my $g = 80 + int(60 * $v1);
return "rgb($x,$g,$x)";
}
if (defined $type and $type eq "aqua") {
my $r = 50 + int(60 * $v1);
my $g = 165 + int(55 * $v1);
my $b = 165 + int(55 * $v1);
return "rgb($r,$g,$b)";
}
if (defined $type and $type eq "orange") {
my $r = 190 + int(65 * $v1);
my $g = 90 + int(65 * $v1);
return "rgb($r,$g,0)";
}
sub color_to_rgb {
my ($color) = @_;

# If $color is a code reference, execute it
if (ref($color) eq 'CODE') {
$color = $color->();
warn "DEBUG: Executed color function, result: $color\n";
}

# Return as-is if it's already in rgb() format
if ($color =~ /^rgb\((\d+),(\d+),(\d+)\)$/) {
my ($r, $g, $b) = ($1, $2, $3);
if ($r == 0 && $g == 0 && $b == 0) {
warn "WARNING: Black color (rgb(0,0,0)) detected. This may be unintended.\n";
}
return $color;
}

# Return as-is if it's a hex color code
if ($color =~ /^#[0-9A-Fa-f]{6}$/) {
if ($color eq '#000000') {
warn "WARNING: Black color (#000000) detected. This may be unintended.\n";
}
return $color;
}

# Look up in color map if it's a named color
if (exists $color_map{$color}) {
my $rgb_color = $color_map{$color};
warn "DEBUG: Mapped named color '$color' to $rgb_color\n";
return $rgb_color;
}

# If we don't recognize the format, return as-is but with a warning
warn "WARNING: Unrecognized color format: $color\n";
return $color;
}

return "rgb(0,0,0)";
sub color {
my ($type, $hash, $name) = @_;
my ($r, $g, $b);

if ($hash) {
$r = namehash($name);
$g = $b = namehash(scalar reverse $name);
} elsif ($rand) {
$r = rand(1);
$g = rand(1);
$b = rand(1);
} else {
$r = random_namehash($name);
$g = random_namehash($name);
$b = random_namehash($name);
}

my $result_color;

if (defined $type && exists $color_palettes{$type}) {
my $palette = $color_palettes{$type};

if (exists $palette->{'__searchcolor'}) {
$searchcolor = $palette->{'__searchcolor'};
delete $palette->{'__searchcolor'};
}

# Sort patterns by length in descending order
my @sorted_patterns = sort { length($b) <=> length($a) } keys %$palette;

foreach my $pattern (@sorted_patterns) {
next if $pattern eq '__default'; # Skip default, we'll use it if nothing else matches
if ($name =~ /$pattern/) {
$result_color = color_to_rgb($palette->{$pattern}->($r, $g, $b, $name));
# warn "DEBUG: Matched pattern '$pattern' for name '$name', color: $result_color\n";
return $result_color;
}
}
# If no specific pattern matched, use the default color
if (exists $palette->{'__default'}) {
$result_color = color_to_rgb($palette->{'__default'}->($r, $g, $b, $name));
# warn "DEBUG: Using default color for name '$name', color: $result_color\n";
return $result_color;
}
}

# Default color if no palette matches
warn "WARNING: No matching color found for type '$type' and name '$name'. Using light gray color.\n";
return "rgb(204,204,204)";
}

sub color_scale {
Expand Down
Loading