#!/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 <(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(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(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(ptrs[entries[i].idx])[0] = 40; break; case FREE: free(ptrs[entries[i].idx]); break; } } } EOT } sub PrintDefinitions() { print <{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";