| #!/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"; |