• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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