Christopher Ferris | 0dc7844 | 2018-08-09 15:19:57 -0700 | [diff] [blame] | 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 | |
| 28 | use strict; |
| 29 | |
| 30 | sub 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 | |
| 62 | EOT |
| 63 | } |
| 64 | |
| 65 | sub PrintMainloop() { |
| 66 | print <<EOT; |
| 67 | void 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 | |
| 107 | EOT |
| 108 | } |
| 109 | |
| 110 | sub PrintDefinitions() { |
| 111 | print <<EOT; |
| 112 | enum AllocEnum : uint8_t { |
| 113 | MALLOC = 0, |
| 114 | CALLOC, |
| 115 | MEMALIGN, |
| 116 | REALLOC, |
| 117 | FREE, |
| 118 | }; |
| 119 | |
| 120 | struct MallocEntry { |
| 121 | AllocEnum type; |
| 122 | size_t idx; |
| 123 | size_t size; |
| 124 | size_t arg2; |
| 125 | }; |
| 126 | |
| 127 | EOT |
| 128 | } |
| 129 | |
| 130 | sub 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 | |
| 149 | sub 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 | |
| 159 | sub 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 | |
| 182 | sub 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 | |
| 194 | sub 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 | |
| 285 | sub 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 | |
| 313 | my $opts = {}; |
| 314 | my @args = ProcessArgs($opts); |
| 315 | if (scalar(@args) != 3) { |
| 316 | PrintUsageAndExit(); |
| 317 | } |
| 318 | |
| 319 | my $thread = $args[0]; |
| 320 | my $struct_name = $args[1]; |
| 321 | my $max_slot_name = $args[2]; |
| 322 | |
| 323 | PrintHeader(); |
| 324 | if ($opts->{print_definitions}) { |
| 325 | PrintDefinitions(); |
| 326 | } |
| 327 | if ($opts->{print_mainloop}) { |
| 328 | PrintMainloop(); |
| 329 | } |
| 330 | |
| 331 | print "static MallocEntry ${struct_name}[] = {\n"; |
| 332 | my $total_slots = PrintEntries($thread, $opts->{ignore_missing_allocations}); |
| 333 | print "};\n"; |
| 334 | print "static constexpr size_t ${max_slot_name} = $total_slots;\n"; |