0dc784431b
This runs through the trace of the allocations in a sql benchmark app executed in the benchmark thread. Add one benchmark with decay time set to 0 and another with decay time set to 1. Include a script that can generate a header file that can be used to regenerate the data. Bug: 112317428 Test: Builds, ran unit tests, ran benchmarks. Change-Id: I62e287cc06b74b74bcc5a4bbee71b0fac0a196fd
334 lines
10 KiB
Perl
Executable file
334 lines
10 KiB
Perl
Executable file
#!/usr/bin/perl -w
|
|
# Copyright (C) 2018 The Android Open Source Project
|
|
# All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# * Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# * Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in
|
|
# the documentation and/or other materials provided with the
|
|
# distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
# SUCH DAMAGE.
|
|
|
|
use strict;
|
|
|
|
sub PrintHeader() {
|
|
print <<EOT;
|
|
/*
|
|
* Copyright (C) 2018 The Android Open Source Project
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
// Generated by gen_malloc.pl, do not modify.
|
|
|
|
EOT
|
|
}
|
|
|
|
sub PrintMainloop() {
|
|
print <<EOT;
|
|
void BenchmarkMalloc(MallocEntry entries[], size_t total_entries, size_t max_allocs) {
|
|
void* ptrs[max_allocs];
|
|
|
|
for (size_t i = 0; i < total_entries; i++) {
|
|
switch (entries[i].type) {
|
|
case MALLOC:
|
|
ptrs[entries[i].idx] = malloc(entries[i].size);
|
|
// Touch at least one byte of the allocation to make sure that
|
|
// PSS for this allocation is counted.
|
|
reinterpret_cast<uint8_t*>(ptrs[entries[i].idx])[0] = 10;
|
|
break;
|
|
case CALLOC:
|
|
ptrs[entries[i].idx] = calloc(entries[i].arg2, entries[i].size);
|
|
// Touch at least one byte of the allocation to make sure that
|
|
// PSS for this allocation is counted.
|
|
reinterpret_cast<uint8_t*>(ptrs[entries[i].idx])[0] = 20;
|
|
break;
|
|
case MEMALIGN:
|
|
ptrs[entries[i].idx] = memalign(entries[i].arg2, entries[i].size);
|
|
// Touch at least one byte of the allocation to make sure that
|
|
// PSS for this allocation is counted.
|
|
reinterpret_cast<uint8_t*>(ptrs[entries[i].idx])[0] = 30;
|
|
break;
|
|
case REALLOC:
|
|
if (entries[i].arg2 == 0) {
|
|
ptrs[entries[i].idx] = realloc(nullptr, entries[i].size);
|
|
} else {
|
|
ptrs[entries[i].idx] = realloc(ptrs[entries[i].arg2 - 1], entries[i].size);
|
|
}
|
|
// Touch at least one byte of the allocation to make sure that
|
|
// PSS for this allocation is counted.
|
|
reinterpret_cast<uint8_t*>(ptrs[entries[i].idx])[0] = 40;
|
|
break;
|
|
case FREE:
|
|
free(ptrs[entries[i].idx]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
EOT
|
|
}
|
|
|
|
sub PrintDefinitions() {
|
|
print <<EOT;
|
|
enum AllocEnum : uint8_t {
|
|
MALLOC = 0,
|
|
CALLOC,
|
|
MEMALIGN,
|
|
REALLOC,
|
|
FREE,
|
|
};
|
|
|
|
struct MallocEntry {
|
|
AllocEnum type;
|
|
size_t idx;
|
|
size_t size;
|
|
size_t arg2;
|
|
};
|
|
|
|
EOT
|
|
}
|
|
|
|
sub PrintUsageAndExit() {
|
|
print "USAGE: gen_malloc.pl [-d][-i][-m] THREAD_ID STRUCT_NAME MAX_SLOT_NAME < ALLOCS.txt\n";
|
|
print " -d\n";
|
|
print " Print the structure definitions.\n";
|
|
print " -i\n";
|
|
print " Ignore missing allocations.\n";
|
|
print " -m\n";
|
|
print " Print the main loop code that can reproduce the trace.\n";
|
|
print " THREAD_ID\n";
|
|
print " The thread for which entries will be printed.\n";
|
|
print " STRUCT_NAME\n";
|
|
print " The name of the structure containing all of the entries.\n";
|
|
print " MAX_SLOT_NAME\n";
|
|
print " The name of the name of the maximum slots variable.\n";
|
|
print " ALLOCS.txt\n";
|
|
print " A file generated by the malloc debug option record_allocs\n";
|
|
exit(1);
|
|
}
|
|
|
|
sub GetSlot($) {
|
|
my ($opts) = @_;
|
|
|
|
if (scalar(@{$opts->{empty_slots}}) == 0) {
|
|
return $opts->{last_slot}++;
|
|
} else {
|
|
return pop(@{$opts->{empty_slots}});
|
|
}
|
|
}
|
|
|
|
sub PrintFreeSlots($) {
|
|
my ($opts) = @_;
|
|
|
|
if (scalar(@{$opts->{empty_slots}}) == $opts->{last_slot}) {
|
|
return;
|
|
}
|
|
|
|
print "\n // Free rest of the allocs.\n";
|
|
my @sorted_empty_slots = sort({$a <=> $b} @{$opts->{empty_slots}});
|
|
my $slot = 0;
|
|
my $last_slot = $opts->{last_slot};
|
|
while ($slot < $last_slot) {
|
|
my $empty_slot = $last_slot;
|
|
if (scalar(@sorted_empty_slots) != 0) {
|
|
$empty_slot = shift(@sorted_empty_slots);
|
|
}
|
|
for (; $slot < $empty_slot; $slot++) {
|
|
print " {FREE, $slot, 0, 0},\n";
|
|
}
|
|
$slot++;
|
|
}
|
|
}
|
|
|
|
sub PrintAlloc($$$$$$) {
|
|
my ($opts, $cur_thread, $pointer, $name, $size, $arg2) = @_;
|
|
|
|
if ($opts->{thread} eq $cur_thread) {
|
|
my $slot = GetSlot($opts);
|
|
$opts->{pointers}->{$pointer} = $slot;
|
|
print " {$name, $slot, $size, $arg2},\n";
|
|
} else {
|
|
$opts->{pointers}->{$pointer} = -1;
|
|
}
|
|
}
|
|
|
|
sub PrintEntries($$) {
|
|
my ($thread, $ignore_missing_allocations) = @_;
|
|
|
|
my $opts = {};
|
|
$opts->{thread} = $thread;
|
|
$opts->{empty_slots} = [];
|
|
$opts->{last_slot} = 0;
|
|
$opts->{pointers} = {};
|
|
|
|
while (<>) {
|
|
if (!/^(\d+):\s*/) {
|
|
continue
|
|
}
|
|
my $cur_thread = $1;
|
|
|
|
$_ = $';
|
|
if (/^malloc\s+(\S+)\s+(\d+)/) {
|
|
my $pointer = $1;
|
|
my $size = $2;
|
|
PrintAlloc($opts, $cur_thread, $pointer, "MALLOC", $size, 0);
|
|
} elsif (/^calloc\s+(\S+)\s+(\d+)\s+(\d+)/) {
|
|
my $pointer = $1;
|
|
my $nmemb = $2;
|
|
my $size = $3;
|
|
PrintAlloc($opts, $cur_thread, $pointer, "CALLOC", $size, $nmemb);
|
|
} elsif (/^memalign\s+(\S+)\s+(\d+)\s+(\d+)/) {
|
|
my $pointer = $1;
|
|
my $align = $2;
|
|
my $size = $3;
|
|
PrintAlloc($opts, $cur_thread, $pointer, "MEMALIGN", $size, $align);
|
|
} elsif (/^free\s+(\S+)/) {
|
|
my $pointer = $1;
|
|
if (!exists $opts->{pointers}->{$pointer}) {
|
|
if ($ignore_missing_allocations) {
|
|
warn "WARNING: $.: Unknown allocation $pointer ignored on $cur_thread\n";
|
|
next;
|
|
} else {
|
|
die "$.: Unknown allocation $pointer on $cur_thread\n";
|
|
}
|
|
} elsif ($opts->{pointers}->{$pointer} != -1) {
|
|
print " {FREE, $opts->{pointers}->{$pointer}, 0, 0},\n";
|
|
push @{$opts->{empty_slots}}, $opts->{pointers}->{$pointer};
|
|
}
|
|
} elsif (/^realloc\s+(\S+)\s+(\S+)\s+(\d+)/) {
|
|
my $new_pointer = $1;
|
|
my $old_pointer = $2;
|
|
my $size = $3;
|
|
|
|
if ($thread ne $cur_thread) {
|
|
if ($new_pointer ne $old_pointer) {
|
|
$opts->{pointers}->{$new_pointer} = -1;
|
|
delete $opts->{pointers}->{$old_pointer};
|
|
}
|
|
} elsif ($old_pointer eq "0x0") {
|
|
my $slot = GetSlot($opts);
|
|
# This was a realloc(nullptr, size) call.
|
|
print " {REALLOC, $slot, $size, 0},\n";
|
|
$opts->{pointers}->{$new_pointer} = $slot;
|
|
} else {
|
|
if (!exists $opts->{pointers}->{$old_pointer}) {
|
|
if ($ignore_missing_allocations) {
|
|
warn "WARNING: $.: Unknown realloc allocation $old_pointer ignored on $cur_thread\n";
|
|
next;
|
|
} else {
|
|
die "Unknown realloc allocation $old_pointer on $cur_thread\n";
|
|
}
|
|
}
|
|
|
|
if ($opts->{pointers}->{$old_pointer} != -1) {
|
|
# Reuse the same slot, no need to get a new one.
|
|
my $slot = $opts->{pointers}->{$old_pointer};
|
|
printf(" {REALLOC, $slot, $size, %d},\n", $slot + 1);
|
|
|
|
# NOTE: It is possible that old pointer and new pointer are the
|
|
# same (a realloc returns the same pointer).
|
|
if ($new_pointer ne $old_pointer) {
|
|
$opts->{pointers}->{$new_pointer} = $slot;
|
|
delete $opts->{pointers}->{$old_pointer};
|
|
}
|
|
}
|
|
}
|
|
} elsif (!/^thread_done/) {
|
|
die "$.: Unknown line $_\n";
|
|
}
|
|
}
|
|
|
|
PrintFreeSlots($opts);
|
|
|
|
return $opts->{last_slot};
|
|
}
|
|
|
|
sub ProcessArgs($) {
|
|
my ($opts) = @_;
|
|
|
|
$opts->{print_definitions} = 0;
|
|
$opts->{ignore_missing_allocations} = 0;
|
|
$opts->{print_mainloop} = 0;
|
|
my @args = ();
|
|
while (scalar(@ARGV)) {
|
|
my $arg = pop(@ARGV);
|
|
if ($arg =~ /^-/) {
|
|
if ($arg eq "-d") {
|
|
$opts->{print_definitions} = 1;
|
|
} elsif ($arg eq "-i") {
|
|
$opts->{ignore_missing_allocations} = 1;
|
|
} elsif ($arg eq "-m") {
|
|
$opts->{print_mainloop} = 1;
|
|
} else {
|
|
print "Unknown option $arg\n";
|
|
PrintUsageAndExit();
|
|
}
|
|
} else {
|
|
unshift @args, $arg;
|
|
}
|
|
}
|
|
|
|
return @args;
|
|
}
|
|
|
|
my $opts = {};
|
|
my @args = ProcessArgs($opts);
|
|
if (scalar(@args) != 3) {
|
|
PrintUsageAndExit();
|
|
}
|
|
|
|
my $thread = $args[0];
|
|
my $struct_name = $args[1];
|
|
my $max_slot_name = $args[2];
|
|
|
|
PrintHeader();
|
|
if ($opts->{print_definitions}) {
|
|
PrintDefinitions();
|
|
}
|
|
if ($opts->{print_mainloop}) {
|
|
PrintMainloop();
|
|
}
|
|
|
|
print "static MallocEntry ${struct_name}[] = {\n";
|
|
my $total_slots = PrintEntries($thread, $opts->{ignore_missing_allocations});
|
|
print "};\n";
|
|
print "static constexpr size_t ${max_slot_name} = $total_slots;\n";
|