1#!/usr/bin/perl -w 2# Copyright (C) 2018 The Android Open Source Project 3# All rights reserved. 4# 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions 7# are met: 8# * Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# * Redistributions in binary form must reproduce the above copyright 11# notice, this list of conditions and the following disclaimer in 12# the documentation and/or other materials provided with the 13# distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 22# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 25# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26# SUCH DAMAGE. 27 28use strict; 29 30sub PrintHeader() { 31 print <<EOT; 32/* 33 * Copyright (C) 2018 The Android Open Source Project 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * * Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * * Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in 43 * the documentation and/or other materials provided with the 44 * distribution. 45 * 46 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 47 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 48 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 49 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 50 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 51 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 52 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 53 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 54 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 55 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 56 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 57 * SUCH DAMAGE. 58 */ 59 60// Generated by gen_malloc.pl, do not modify. 61 62EOT 63} 64 65sub PrintMainloop() { 66 print <<EOT; 67void BenchmarkMalloc(MallocEntry entries[], size_t total_entries, size_t max_allocs) { 68 void* ptrs[max_allocs]; 69 70 for (size_t i = 0; i < total_entries; i++) { 71 switch (entries[i].type) { 72 case MALLOC: 73 ptrs[entries[i].idx] = malloc(entries[i].size); 74 // Touch at least one byte of the allocation to make sure that 75 // PSS for this allocation is counted. 76 reinterpret_cast<uint8_t*>(ptrs[entries[i].idx])[0] = 10; 77 break; 78 case CALLOC: 79 ptrs[entries[i].idx] = calloc(entries[i].arg2, entries[i].size); 80 // Touch at least one byte of the allocation to make sure that 81 // PSS for this allocation is counted. 82 reinterpret_cast<uint8_t*>(ptrs[entries[i].idx])[0] = 20; 83 break; 84 case MEMALIGN: 85 ptrs[entries[i].idx] = memalign(entries[i].arg2, entries[i].size); 86 // Touch at least one byte of the allocation to make sure that 87 // PSS for this allocation is counted. 88 reinterpret_cast<uint8_t*>(ptrs[entries[i].idx])[0] = 30; 89 break; 90 case REALLOC: 91 if (entries[i].arg2 == 0) { 92 ptrs[entries[i].idx] = realloc(nullptr, entries[i].size); 93 } else { 94 ptrs[entries[i].idx] = realloc(ptrs[entries[i].arg2 - 1], entries[i].size); 95 } 96 // Touch at least one byte of the allocation to make sure that 97 // PSS for this allocation is counted. 98 reinterpret_cast<uint8_t*>(ptrs[entries[i].idx])[0] = 40; 99 break; 100 case FREE: 101 free(ptrs[entries[i].idx]); 102 break; 103 } 104 } 105} 106 107EOT 108} 109 110sub PrintDefinitions() { 111 print <<EOT; 112enum AllocEnum : uint8_t { 113 MALLOC = 0, 114 CALLOC, 115 MEMALIGN, 116 REALLOC, 117 FREE, 118}; 119 120struct MallocEntry { 121 AllocEnum type; 122 size_t idx; 123 size_t size; 124 size_t arg2; 125}; 126 127EOT 128} 129 130sub PrintUsageAndExit() { 131 print "USAGE: gen_malloc.pl [-d][-i][-m] THREAD_ID STRUCT_NAME MAX_SLOT_NAME < ALLOCS.txt\n"; 132 print " -d\n"; 133 print " Print the structure definitions.\n"; 134 print " -i\n"; 135 print " Ignore missing allocations.\n"; 136 print " -m\n"; 137 print " Print the main loop code that can reproduce the trace.\n"; 138 print " THREAD_ID\n"; 139 print " The thread for which entries will be printed.\n"; 140 print " STRUCT_NAME\n"; 141 print " The name of the structure containing all of the entries.\n"; 142 print " MAX_SLOT_NAME\n"; 143 print " The name of the name of the maximum slots variable.\n"; 144 print " ALLOCS.txt\n"; 145 print " A file generated by the malloc debug option record_allocs\n"; 146 exit(1); 147} 148 149sub GetSlot($) { 150 my ($opts) = @_; 151 152 if (scalar(@{$opts->{empty_slots}}) == 0) { 153 return $opts->{last_slot}++; 154 } else { 155 return pop(@{$opts->{empty_slots}}); 156 } 157} 158 159sub PrintFreeSlots($) { 160 my ($opts) = @_; 161 162 if (scalar(@{$opts->{empty_slots}}) == $opts->{last_slot}) { 163 return; 164 } 165 166 print "\n // Free rest of the allocs.\n"; 167 my @sorted_empty_slots = sort({$a <=> $b} @{$opts->{empty_slots}}); 168 my $slot = 0; 169 my $last_slot = $opts->{last_slot}; 170 while ($slot < $last_slot) { 171 my $empty_slot = $last_slot; 172 if (scalar(@sorted_empty_slots) != 0) { 173 $empty_slot = shift(@sorted_empty_slots); 174 } 175 for (; $slot < $empty_slot; $slot++) { 176 print " {FREE, $slot, 0, 0},\n"; 177 } 178 $slot++; 179 } 180} 181 182sub PrintAlloc($$$$$$) { 183 my ($opts, $cur_thread, $pointer, $name, $size, $arg2) = @_; 184 185 if ($opts->{thread} eq $cur_thread) { 186 my $slot = GetSlot($opts); 187 $opts->{pointers}->{$pointer} = $slot; 188 print " {$name, $slot, $size, $arg2},\n"; 189 } else { 190 $opts->{pointers}->{$pointer} = -1; 191 } 192} 193 194sub PrintEntries($$) { 195 my ($thread, $ignore_missing_allocations) = @_; 196 197 my $opts = {}; 198 $opts->{thread} = $thread; 199 $opts->{empty_slots} = []; 200 $opts->{last_slot} = 0; 201 $opts->{pointers} = {}; 202 203 while (<>) { 204 if (!/^(\d+):\s*/) { 205 continue 206 } 207 my $cur_thread = $1; 208 209 $_ = $'; 210 if (/^malloc\s+(\S+)\s+(\d+)/) { 211 my $pointer = $1; 212 my $size = $2; 213 PrintAlloc($opts, $cur_thread, $pointer, "MALLOC", $size, 0); 214 } elsif (/^calloc\s+(\S+)\s+(\d+)\s+(\d+)/) { 215 my $pointer = $1; 216 my $nmemb = $2; 217 my $size = $3; 218 PrintAlloc($opts, $cur_thread, $pointer, "CALLOC", $size, $nmemb); 219 } elsif (/^memalign\s+(\S+)\s+(\d+)\s+(\d+)/) { 220 my $pointer = $1; 221 my $align = $2; 222 my $size = $3; 223 PrintAlloc($opts, $cur_thread, $pointer, "MEMALIGN", $size, $align); 224 } elsif (/^free\s+(\S+)/) { 225 my $pointer = $1; 226 if (!exists $opts->{pointers}->{$pointer}) { 227 if ($ignore_missing_allocations) { 228 warn "WARNING: $.: Unknown allocation $pointer ignored on $cur_thread\n"; 229 next; 230 } else { 231 die "$.: Unknown allocation $pointer on $cur_thread\n"; 232 } 233 } elsif ($opts->{pointers}->{$pointer} != -1) { 234 print " {FREE, $opts->{pointers}->{$pointer}, 0, 0},\n"; 235 push @{$opts->{empty_slots}}, $opts->{pointers}->{$pointer}; 236 } 237 } elsif (/^realloc\s+(\S+)\s+(\S+)\s+(\d+)/) { 238 my $new_pointer = $1; 239 my $old_pointer = $2; 240 my $size = $3; 241 242 if ($thread ne $cur_thread) { 243 if ($new_pointer ne $old_pointer) { 244 $opts->{pointers}->{$new_pointer} = -1; 245 delete $opts->{pointers}->{$old_pointer}; 246 } 247 } elsif ($old_pointer eq "0x0") { 248 my $slot = GetSlot($opts); 249 # This was a realloc(nullptr, size) call. 250 print " {REALLOC, $slot, $size, 0},\n"; 251 $opts->{pointers}->{$new_pointer} = $slot; 252 } else { 253 if (!exists $opts->{pointers}->{$old_pointer}) { 254 if ($ignore_missing_allocations) { 255 warn "WARNING: $.: Unknown realloc allocation $old_pointer ignored on $cur_thread\n"; 256 next; 257 } else { 258 die "Unknown realloc allocation $old_pointer on $cur_thread\n"; 259 } 260 } 261 262 if ($opts->{pointers}->{$old_pointer} != -1) { 263 # Reuse the same slot, no need to get a new one. 264 my $slot = $opts->{pointers}->{$old_pointer}; 265 printf(" {REALLOC, $slot, $size, %d},\n", $slot + 1); 266 267 # NOTE: It is possible that old pointer and new pointer are the 268 # same (a realloc returns the same pointer). 269 if ($new_pointer ne $old_pointer) { 270 $opts->{pointers}->{$new_pointer} = $slot; 271 delete $opts->{pointers}->{$old_pointer}; 272 } 273 } 274 } 275 } elsif (!/^thread_done/) { 276 die "$.: Unknown line $_\n"; 277 } 278 } 279 280 PrintFreeSlots($opts); 281 282 return $opts->{last_slot}; 283} 284 285sub ProcessArgs($) { 286 my ($opts) = @_; 287 288 $opts->{print_definitions} = 0; 289 $opts->{ignore_missing_allocations} = 0; 290 $opts->{print_mainloop} = 0; 291 my @args = (); 292 while (scalar(@ARGV)) { 293 my $arg = pop(@ARGV); 294 if ($arg =~ /^-/) { 295 if ($arg eq "-d") { 296 $opts->{print_definitions} = 1; 297 } elsif ($arg eq "-i") { 298 $opts->{ignore_missing_allocations} = 1; 299 } elsif ($arg eq "-m") { 300 $opts->{print_mainloop} = 1; 301 } else { 302 print "Unknown option $arg\n"; 303 PrintUsageAndExit(); 304 } 305 } else { 306 unshift @args, $arg; 307 } 308 } 309 310 return @args; 311} 312 313my $opts = {}; 314my @args = ProcessArgs($opts); 315if (scalar(@args) != 3) { 316 PrintUsageAndExit(); 317} 318 319my $thread = $args[0]; 320my $struct_name = $args[1]; 321my $max_slot_name = $args[2]; 322 323PrintHeader(); 324if ($opts->{print_definitions}) { 325 PrintDefinitions(); 326} 327if ($opts->{print_mainloop}) { 328 PrintMainloop(); 329} 330 331print "static MallocEntry ${struct_name}[] = {\n"; 332my $total_slots = PrintEntries($thread, $opts->{ignore_missing_allocations}); 333print "};\n"; 334print "static constexpr size_t ${max_slot_name} = $total_slots;\n"; 335