blob: 3fbb5444bf1dbee834c9c88eb54485ec50903ef9 [file] [log] [blame]
Christopher Ferris0dc78442018-08-09 15:19:57 -07001#!/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";