Add new malloc benchmarks.
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
diff --git a/libc/malloc_debug/tools/gen_malloc.pl b/libc/malloc_debug/tools/gen_malloc.pl
new file mode 100755
index 0000000..3fbb544
--- /dev/null
+++ b/libc/malloc_debug/tools/gen_malloc.pl
@@ -0,0 +1,334 @@
+#!/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";