Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/curl/tests/memanalyze.pl
2065 views
1
#!/usr/bin/env perl
2
#***************************************************************************
3
# _ _ ____ _
4
# Project ___| | | | _ \| |
5
# / __| | | | |_) | |
6
# | (__| |_| | _ <| |___
7
# \___|\___/|_| \_\_____|
8
#
9
# Copyright (C) Daniel Stenberg, <[email protected]>, et al.
10
#
11
# This software is licensed as described in the file COPYING, which
12
# you should have received as part of this distribution. The terms
13
# are also available at https://curl.se/docs/copyright.html.
14
#
15
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
16
# copies of the Software, and permit persons to whom the Software is
17
# furnished to do so, under the terms of the COPYING file.
18
#
19
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20
# KIND, either express or implied.
21
#
22
# SPDX-License-Identifier: curl
23
#
24
###########################################################################
25
#
26
# Example input:
27
#
28
# MEM mprintf.c:1094 malloc(32) = e5718
29
# MEM mprintf.c:1103 realloc(e5718, 64) = e6118
30
# MEM sendf.c:232 free(f6520)
31
32
my $mallocs=0;
33
my $callocs=0;
34
my $reallocs=0;
35
my $strdups=0;
36
my $wcsdups=0;
37
my $showlimit;
38
my $sends=0;
39
my $recvs=0;
40
my $sockets=0;
41
42
while(1) {
43
if($ARGV[0] eq "-v") {
44
$verbose=1;
45
shift @ARGV;
46
}
47
elsif($ARGV[0] eq "-t") {
48
$trace=1;
49
shift @ARGV;
50
}
51
elsif($ARGV[0] eq "-l") {
52
# only show what alloc that caused a memlimit failure
53
$showlimit=1;
54
shift @ARGV;
55
}
56
else {
57
last;
58
}
59
}
60
61
my $memsum; # the total number of memory allocated over the lifetime
62
my $maxmem; # the high water mark
63
64
sub newtotal {
65
my ($newtot)=@_;
66
# count a max here
67
68
if($newtot > $maxmem) {
69
$maxmem= $newtot;
70
}
71
}
72
73
my $file = $ARGV[0];
74
75
if(! -f $file) {
76
print "Usage: memanalyze.pl [options] <dump file>\n",
77
"Options:\n",
78
" -l memlimit failure displayed\n",
79
" -v Verbose\n",
80
" -t Trace\n";
81
exit;
82
}
83
84
open(my $fileh, "<", "$file");
85
86
if($showlimit) {
87
while(<$fileh>) {
88
if(/^LIMIT.*memlimit$/) {
89
print $_;
90
last;
91
}
92
}
93
close($fileh);
94
exit;
95
}
96
97
98
my $lnum=0;
99
while(<$fileh>) {
100
chomp $_;
101
$line = $_;
102
$lnum++;
103
if($line =~ /^LIMIT ([^ ]*):(\d*) (.*)/) {
104
# new memory limit test prefix
105
my $i = $3;
106
my ($source, $linenum) = ($1, $2);
107
if($trace && ($i =~ /([^ ]*) reached memlimit/)) {
108
print "LIMIT: $1 returned error at $source:$linenum\n";
109
}
110
}
111
elsif($line =~ /^MEM ([^ ]*):(\d*) (.*)/) {
112
# generic match for the filename+linenumber
113
$source = $1;
114
$linenum = $2;
115
$function = $3;
116
117
if($function =~ /free\((\(nil\)|0x([0-9a-f]*))/) {
118
$addr = $2;
119
if($1 eq "(nil)") {
120
; # do nothing when free(NULL)
121
}
122
elsif(!exists $sizeataddr{$addr}) {
123
print "FREE ERROR: No memory allocated: $line\n";
124
}
125
elsif(-1 == $sizeataddr{$addr}) {
126
print "FREE ERROR: Memory freed twice: $line\n";
127
print "FREE ERROR: Previously freed at: ".$getmem{$addr}."\n";
128
}
129
else {
130
$totalmem -= $sizeataddr{$addr};
131
if($trace) {
132
print "FREE: malloc at ".$getmem{$addr}." is freed again at $source:$linenum\n";
133
printf("FREE: %d bytes freed, left allocated: $totalmem bytes\n", $sizeataddr{$addr});
134
}
135
136
newtotal($totalmem);
137
$frees++;
138
139
$sizeataddr{$addr}=-1; # set -1 to mark as freed
140
$getmem{$addr}="$source:$linenum";
141
142
}
143
}
144
elsif($function =~ /malloc\((\d*)\) = 0x([0-9a-f]*)/) {
145
$size = $1;
146
$addr = $2;
147
148
if($sizeataddr{$addr}>0) {
149
# this means weeeeeirdo
150
print "Mixed debug compile ($source:$linenum at line $lnum), rebuild curl now\n";
151
print "We think $sizeataddr{$addr} bytes are already allocated at that memory address: $addr!\n";
152
}
153
154
$sizeataddr{$addr}=$size;
155
$totalmem += $size;
156
$memsum += $size;
157
158
if($trace) {
159
print "MALLOC: malloc($size) at $source:$linenum",
160
" makes totally $totalmem bytes\n";
161
}
162
163
newtotal($totalmem);
164
$mallocs++;
165
166
$getmem{$addr}="$source:$linenum";
167
}
168
elsif($function =~ /calloc\((\d*),(\d*)\) = 0x([0-9a-f]*)/) {
169
$size = $1*$2;
170
$addr = $3;
171
172
$arg1 = $1;
173
$arg2 = $2;
174
175
if($sizeataddr{$addr}>0) {
176
# this means weeeeeirdo
177
print "Mixed debug compile, rebuild curl now\n";
178
}
179
180
$sizeataddr{$addr}=$size;
181
$totalmem += $size;
182
$memsum += $size;
183
184
if($trace) {
185
print "CALLOC: calloc($arg1,$arg2) at $source:$linenum",
186
" makes totally $totalmem bytes\n";
187
}
188
189
newtotal($totalmem);
190
$callocs++;
191
192
$getmem{$addr}="$source:$linenum";
193
}
194
elsif($function =~ /realloc\((\(nil\)|0x([0-9a-f]*)), (\d*)\) = 0x([0-9a-f]*)/) {
195
my ($oldaddr, $newsize, $newaddr) = ($2, $3, $4);
196
197
$totalmem -= $sizeataddr{$oldaddr};
198
if($trace) {
199
printf("REALLOC: %d less bytes and ", $sizeataddr{$oldaddr});
200
}
201
$sizeataddr{$oldaddr}=0;
202
203
$totalmem += $newsize;
204
$memsum += $size;
205
$sizeataddr{$newaddr}=$newsize;
206
207
if($trace) {
208
printf("%d more bytes ($source:$linenum)\n", $newsize);
209
}
210
211
newtotal($totalmem);
212
$reallocs++;
213
214
$getmem{$oldaddr}="";
215
$getmem{$newaddr}="$source:$linenum";
216
}
217
elsif($function =~ /strdup\(0x([0-9a-f]*)\) \((\d*)\) = 0x([0-9a-f]*)/) {
218
# strdup(a5b50) (8) = df7c0
219
220
$dup = $1;
221
$size = $2;
222
$addr = $3;
223
$getmem{$addr}="$source:$linenum";
224
$sizeataddr{$addr}=$size;
225
226
$totalmem += $size;
227
$memsum += $size;
228
229
if($trace) {
230
printf("STRDUP: $size bytes at %s, makes totally: %d bytes\n",
231
$getmem{$addr}, $totalmem);
232
}
233
234
newtotal($totalmem);
235
$strdups++;
236
}
237
elsif($function =~ /wcsdup\(0x([0-9a-f]*)\) \((\d*)\) = 0x([0-9a-f]*)/) {
238
# wcsdup(a5b50) (8) = df7c0
239
240
$dup = $1;
241
$size = $2;
242
$addr = $3;
243
$getmem{$addr}="$source:$linenum";
244
$sizeataddr{$addr}=$size;
245
246
$totalmem += $size;
247
$memsum += $size;
248
249
if($trace) {
250
printf("WCSDUP: $size bytes at %s, makes totally: %d bytes\n",
251
$getmem{$addr}, $totalmem);
252
}
253
254
newtotal($totalmem);
255
$wcsdups++;
256
}
257
else {
258
print "Not recognized input line: $function\n";
259
}
260
}
261
# FD url.c:1282 socket() = 5
262
elsif($_ =~ /^FD ([^ ]*):(\d*) (.*)/) {
263
# generic match for the filename+linenumber
264
$source = $1;
265
$linenum = $2;
266
$function = $3;
267
268
if($function =~ /socket\(\) = (\d*)/) {
269
$filedes{$1}=1;
270
$getfile{$1}="$source:$linenum";
271
$openfile++;
272
$sockets++; # number of socket() calls
273
}
274
elsif($function =~ /socketpair\(\) = (\d*) (\d*)/) {
275
$filedes{$1}=1;
276
$getfile{$1}="$source:$linenum";
277
$openfile++;
278
$filedes{$2}=1;
279
$getfile{$2}="$source:$linenum";
280
$openfile++;
281
}
282
elsif($function =~ /accept\(\) = (\d*)/) {
283
$filedes{$1}=1;
284
$getfile{$1}="$source:$linenum";
285
$openfile++;
286
}
287
elsif($function =~ /sclose\((\d*)\)/) {
288
if($filedes{$1} != 1) {
289
print "Close without open: $line\n";
290
}
291
else {
292
$filedes{$1}=0; # closed now
293
$openfile--;
294
}
295
}
296
}
297
# FILE url.c:1282 fopen("blabla") = 0x5ddd
298
elsif($_ =~ /^FILE ([^ ]*):(\d*) (.*)/) {
299
# generic match for the filename+linenumber
300
$source = $1;
301
$linenum = $2;
302
$function = $3;
303
304
if($function =~ /f[d]*open\(\"(.*)\",\"([^\"]*)\"\) = (\(nil\)|0x([0-9a-f]*))/) {
305
if($3 eq "(nil)") {
306
;
307
}
308
else {
309
$fopen{$4}=1;
310
$fopenfile{$4}="$source:$linenum";
311
$fopens++;
312
}
313
}
314
# fclose(0x1026c8)
315
elsif($function =~ /fclose\(0x([0-9a-f]*)\)/) {
316
if(!$fopen{$1}) {
317
print "fclose() without fopen(): $line\n";
318
}
319
else {
320
$fopen{$1}=0;
321
$fopens--;
322
}
323
}
324
}
325
# GETNAME url.c:1901 getnameinfo()
326
elsif($_ =~ /^GETNAME ([^ ]*):(\d*) (.*)/) {
327
# not much to do
328
}
329
# SEND url.c:1901 send(83) = 83
330
elsif($_ =~ /^SEND ([^ ]*):(\d*) (.*)/) {
331
$sends++;
332
}
333
# RECV url.c:1901 recv(102400) = 256
334
elsif($_ =~ /^RECV ([^ ]*):(\d*) (.*)/) {
335
$recvs++;
336
}
337
338
# ADDR url.c:1282 getaddrinfo() = 0x5ddd
339
elsif($_ =~ /^ADDR ([^ ]*):(\d*) (.*)/) {
340
# generic match for the filename+linenumber
341
$source = $1;
342
$linenum = $2;
343
$function = $3;
344
345
if($function =~ /getaddrinfo\(\) = (\(nil\)|0x([0-9a-f]*))/) {
346
my $add = $1;
347
if($add eq "(nil)") {
348
;
349
}
350
else {
351
$addrinfo{$add}=1;
352
$addrinfofile{$add}="$source:$linenum";
353
$addrinfos++;
354
}
355
if($trace) {
356
printf("GETADDRINFO ($source:$linenum)\n");
357
}
358
}
359
# fclose(0x1026c8)
360
elsif($function =~ /freeaddrinfo\((0x[0-9a-f]*)\)/) {
361
my $addr = $1;
362
if(!$addrinfo{$addr}) {
363
print "freeaddrinfo() without getaddrinfo(): $line\n";
364
}
365
else {
366
$addrinfo{$addr}=0;
367
$addrinfos--;
368
}
369
if($trace) {
370
printf("FREEADDRINFO ($source:$linenum)\n");
371
}
372
}
373
374
}
375
else {
376
print "Not recognized prefix line: $line\n";
377
}
378
}
379
close($fileh);
380
381
if($totalmem) {
382
print "Leak detected: memory still allocated: $totalmem bytes\n";
383
384
for(keys %sizeataddr) {
385
$addr = $_;
386
$size = $sizeataddr{$addr};
387
if($size > 0) {
388
print "At $addr, there's $size bytes.\n";
389
print " allocated by ".$getmem{$addr}."\n";
390
}
391
}
392
}
393
394
if($openfile) {
395
for(keys %filedes) {
396
if($filedes{$_} == 1) {
397
print "Open file descriptor created at ".$getfile{$_}."\n";
398
}
399
}
400
}
401
402
if($fopens) {
403
print "Open FILE handles left at:\n";
404
for(keys %fopen) {
405
if($fopen{$_} == 1) {
406
print "fopen() called at ".$fopenfile{$_}."\n";
407
}
408
}
409
}
410
411
if($addrinfos) {
412
print "IPv6-style name resolve data left at:\n";
413
for(keys %addrinfofile) {
414
if($addrinfo{$_} == 1) {
415
print "getaddrinfo() called at ".$addrinfofile{$_}."\n";
416
}
417
}
418
}
419
420
if($verbose) {
421
print "Mallocs: $mallocs\n",
422
"Reallocs: $reallocs\n",
423
"Callocs: $callocs\n",
424
"Strdups: $strdups\n",
425
"Wcsdups: $wcsdups\n",
426
"Frees: $frees\n",
427
"Sends: $sends\n",
428
"Recvs: $recvs\n",
429
"Sockets: $sockets\n",
430
"Allocations: ".($mallocs + $callocs + $reallocs + $strdups + $wcsdups)."\n",
431
"Operations: ".($mallocs + $callocs + $reallocs + $strdups + $wcsdups + $sends + $recvs + $sockets)."\n";
432
433
print "Maximum allocated: $maxmem\n";
434
print "Total allocated: $memsum\n";
435
}
436
437