Bug Summary

File:Source/GSMemoryPanel.m
Location:line 358, column 1
Description:Potential leak of an object allocated on line 349 and stored into 'snapshot'
Code is compiled to use reference counts

Annotated Source Code

1/* GSMemoryPanel.m -*-objc-*-
2
3 A GNUstep panel for tracking memory leaks.
4
5 Copyright (C) 2000, 2002 Free Software Foundation, Inc.
6
7 Author: Nicola Pero <nicola@brainstorm.co.uk>
8 Date: 2000, 2002
9
10 This file is part of the GNUstep GUI Library.
11
12 This library is free software; you can redistribute it and/or
13 modify it under the terms of the GNU Lesser General Public
14 License as published by the Free Software Foundation; either
15 version 2 of the License, or (at your option) any later version.
16
17 This library is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 Lesser General Public License for more details.
21
22 You should have received a copy of the GNU Lesser General Public
23 License along with this library; see the file COPYING.LIB.
24 If not, see <http://www.gnu.org/licenses/> or write to the
25 Free Software Foundation, 51 Franklin Street, Fifth Floor,
26 Boston, MA 02110-1301, USA.
27*/
28
29#import <Foundation/NSArray.h>
30#import <Foundation/NSDebug.h>
31#import <Foundation/NSString.h>
32#import <Foundation/NSValue.h>
33#import "AppKit/NSButton.h"
34#import "AppKit/NSScrollView.h"
35#import "AppKit/NSTableColumn.h"
36#import "AppKit/NSTableView.h"
37#import "GNUstepGUI/GSMemoryPanel.h"
38#import "GNUstepGUI/GSHbox.h"
39#import "GNUstepGUI/GSVbox.h"
40
41enum {
42 OrderByClassName,
43 OrderByCount,
44 OrderByTotal,
45 OrderByPeak
46};
47
48static inline NSComparisonResult
49invertComparison (NSComparisonResult comparison)
50{
51 /* invert comparison */
52 if (comparison == NSOrderedAscending)
53 {
54 comparison = NSOrderedDescending;
55 }
56 else if (comparison == NSOrderedDescending)
57 {
58 comparison = NSOrderedAscending;
59 }
60 return comparison;
61}
62
63/*
64 * Internal private class used in reordering entries.
65 *
66 */
67@interface GSMemoryPanelEntry : NSObject
68{
69 NSString *string;
70 NSNumber *count;
71 NSNumber *total;
72 NSNumber *peak;
73}
74- (id) initWithString: (NSString *)aString
75 count: (int)aCount
76 total: (int)aTotal
77 peak: (int)aPeak;
78- (NSString *) string;
79- (NSNumber *) count;
80- (NSNumber *) total;
81- (NSNumber *) peak;
82- (NSComparisonResult) compareByTotal: (GSMemoryPanelEntry *)aEntry;
83- (NSComparisonResult) compareByCount: (GSMemoryPanelEntry *)aEntry;
84- (NSComparisonResult) compareByPeak: (GSMemoryPanelEntry *)aEntry;
85- (NSComparisonResult) compareByClassName: (GSMemoryPanelEntry *)aEntry;
86@end
87
88@implementation GSMemoryPanelEntry
89
90- (id) initWithString: (NSString *)aString
91 count: (int)aCount
92 total: (int)aTotal
93 peak: (int)aPeak
94{
95 ASSIGN (string, aString)({ id __object = string; string = [(aString) retain]; [__object
release]; })
;
96 ASSIGN (count, [NSNumber numberWithInt: aCount])({ id __object = count; count = [([NSNumber numberWithInt: aCount
]) retain]; [__object release]; })
;
97 ASSIGN (total, [NSNumber numberWithInt: aTotal])({ id __object = total; total = [([NSNumber numberWithInt: aTotal
]) retain]; [__object release]; })
;
98 ASSIGN (peak, [NSNumber numberWithInt: aPeak])({ id __object = peak; peak = [([NSNumber numberWithInt: aPeak
]) retain]; [__object release]; })
;
99 return self;
100}
101
102- (void) dealloc
103{
104 RELEASE (string)[(string) release];
105 RELEASE (count)[(count) release];
106 RELEASE (total)[(total) release];
107 RELEASE (peak)[(peak) release];
108 [super dealloc];
109}
110
111- (NSString *) string
112{
113 return string;
114}
115
116- (NSNumber *) count
117{
118 return count;
119}
120
121- (NSNumber *) total
122{
123 return total;
124}
125
126- (NSNumber *) peak
127{
128 return peak;
129}
130
131- (NSComparisonResult) compareByCount: (GSMemoryPanelEntry *)aEntry
132{
133 NSComparisonResult comparison = [count compare: aEntry->count];
134
135 return invertComparison (comparison);
136}
137
138- (NSComparisonResult) compareByTotal: (GSMemoryPanelEntry *)aEntry
139{
140 NSComparisonResult comparison = [total compare: aEntry->total];
141
142 return invertComparison (comparison);
143}
144
145- (NSComparisonResult) compareByPeak: (GSMemoryPanelEntry *)aEntry
146{
147 NSComparisonResult comparison = [peak compare: aEntry->peak];
148
149 return invertComparison (comparison);
150}
151
152- (NSComparisonResult) compareByClassName: (GSMemoryPanelEntry *)aEntry
153{
154 return [string compare: aEntry->string];
155}
156
157@end
158
159/*
160 * The Memory Panel code
161 */
162
163static GSMemoryPanel *sharedGSMemoryPanel = nil((id)((void*)0));
164
165@implementation GSMemoryPanel
166+ (id) sharedMemoryPanel
167{
168 if (sharedGSMemoryPanel == nil((id)((void*)0)))
169 {
170 sharedGSMemoryPanel = [GSMemoryPanel new];
171 }
172
173 return sharedGSMemoryPanel;
174}
175
176+ (void) update: (id)sender
177{
178 [[self sharedMemoryPanel] update: sender];
179}
180
181- (id) init
182{
183 NSRect winFrame;
184 NSTableColumn *classColumn;
185 NSTableColumn *countColumn;
186 NSTableColumn *totalColumn;
187 NSTableColumn *peakColumn;
188 NSScrollView *scrollView;
189 GSVbox *vbox;
190 GSHbox *hbox;
191 NSButton *button;
192
193 /* Activate debugging of allocation. */
194 GSDebugAllocationActive (YES((BOOL)1));
195
196 hbox = [GSHbox new];
197 [hbox setDefaultMinXMargin: 5];
198 [hbox setBorder: 5];
199 [hbox setAutoresizingMask: NSViewWidthSizable];
200
201 /* Button updating the table. */
202 button = [NSButton new];
203 [button setBordered: YES((BOOL)1)];
204 [button setButtonType: NSMomentaryPushButton];
205 [button setTitle: @"Update"];
206 [button setImagePosition: NSNoImage];
207 [button setTarget: self];
208 [button setAction: @selector(update:)];
209 [button setAutoresizingMask: NSViewMaxXMargin];
210 [button sizeToFit];
211 [button setTag: 1];
212
213 [hbox addView: button];
214 RELEASE (button)[(button) release];
215
216 /* Button taking snapshot of the table. */
217 button = [NSButton new];
218 [button setBordered: YES((BOOL)1)];
219 [button setButtonType: NSMomentaryPushButton];
220 [button setTitle: @"Snapshot"];
221 [button setImagePosition: NSNoImage];
222 [button setTarget: self];
223 [button setAction: @selector(snapshot:)];
224 [button setAutoresizingMask: NSViewMinXMargin];
225 [button sizeToFit];
226 [button setTag: 2];
227
228 [hbox addView: button];
229 RELEASE (button)[(button) release];
230
231 classColumn = [[NSTableColumn alloc] initWithIdentifier: @"Class"];
232 [classColumn setEditable: NO((BOOL)0)];
233 [[classColumn headerCell] setStringValue: @"Class Name"];
234 [classColumn setMinWidth: 200];
235
236 countColumn = [[NSTableColumn alloc] initWithIdentifier: @"Count"];
237 [countColumn setEditable: NO((BOOL)0)];
238 [[countColumn headerCell] setStringValue: @"Current"];
239 [countColumn setMinWidth: 50];
240
241 totalColumn = [[NSTableColumn alloc] initWithIdentifier: @"Total"];
242 [totalColumn setEditable: NO((BOOL)0)];
243 [[totalColumn headerCell] setStringValue: @"Total"];
244 [totalColumn setMinWidth: 50];
245
246 peakColumn = [[NSTableColumn alloc] initWithIdentifier: @"Peak"];
247 [peakColumn setEditable: NO((BOOL)0)];
248 [[peakColumn headerCell] setStringValue: @"Peak"];
249 [peakColumn setMinWidth: 50];
250
251 table = [[NSTableView alloc] initWithFrame: NSMakeRect (0, 0, 300, 300)];
252 [table addTableColumn: classColumn];
253 RELEASE (classColumn)[(classColumn) release];
254 [table addTableColumn: countColumn];
255 RELEASE (countColumn)[(countColumn) release];
256 [table addTableColumn: totalColumn];
257 RELEASE (totalColumn)[(totalColumn) release];
258 [table addTableColumn: peakColumn];
259 RELEASE (peakColumn)[(peakColumn) release];
260 [table setDataSource: self];
261 [table setDelegate: self];
262 [table setDoubleAction: @selector (reorder:)];
263
264 scrollView = [[NSScrollView alloc]
265 initWithFrame: NSMakeRect (0, 0, 350, 300)];
266 [scrollView setDocumentView: table];
267 [scrollView setHasHorizontalScroller: YES((BOOL)1)];
268 [scrollView setHasVerticalScroller: YES((BOOL)1)];
269 [scrollView setBorderType: NSBezelBorder];
270 [scrollView setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)];
271 [table sizeToFit];
272 RELEASE (table)[(table) release];
273
274 vbox = [GSVbox new];
275 [vbox setDefaultMinYMargin: 5];
276 [vbox setBorder: 5];
277 [vbox addView: hbox enablingYResizing: NO((BOOL)0)];
278 RELEASE (hbox)[(hbox) release];
279 [vbox addView: scrollView];
280 RELEASE (scrollView)[(scrollView) release];
281
282 /* FIXME - should actually autosave the memory panel position and frame ! */
283 winFrame.size = [vbox frame].size;
284 winFrame.origin = NSMakePoint (100, 200);
285
286 self = [super initWithContentRect: winFrame
287 styleMask: (NSTitledWindowMask
288 | NSClosableWindowMask
289 | NSMiniaturizableWindowMask
290 | NSResizableWindowMask)
291 backing: NSBackingStoreBuffered
292 defer: NO((BOOL)0)];
293 if (nil((id)((void*)0)) == self)
294 return nil((id)((void*)0));
295
296 array = [NSMutableArray new];
297 orderingBy = @selector(compareByCount:);
298
299 [self setReleasedWhenClosed: NO((BOOL)0)];
300 [self setContentView: vbox];
301 RELEASE (vbox)[(vbox) release];
302 [self setTitle: @"Memory Panel"];
303
304 return self;
305}
306
307- (void) dealloc
308{
309 RELEASE(array)[(array) release];
310 [super dealloc];
311}
312
313- (int) numberOfRowsInTableView: (NSTableView *)aTableView
314{
315 return [array count];
316}
317
318- (id) tableView: (NSTableView *)aTableView
319 objectValueForTableColumn: (NSTableColumn *)aTableColumn
320 row:(int)rowIndex
321{
322 GSMemoryPanelEntry *entry = [array objectAtIndex: rowIndex];
323 id identifier = [aTableColumn identifier];
324
325 if ([identifier isEqual: @"Class"])
326 {
327 return [entry string];
328 }
329 else if ([identifier isEqual: @"Count"])
330 {
331 return [entry count];
332 }
333 else if ([identifier isEqual: @"Total"])
334 {
335 return [entry total];
336 }
337 else if ([identifier isEqual: @"Peak"])
338 {
339 return [entry peak];
340 }
341
342 NSLog (@"Hi, I am a bug in your table view");
343
344 return @"";
345}
346
347- (void) snapshot: (id)sender
348{
349 GSMemoryPanel *snapshot = [GSMemoryPanel new];
1
Method returns an Objective-C object with a +1 retain count
350
351 [snapshot setTitle:
352 [NSString stringWithFormat: @"Memory Snapshot at %@", [NSDate date]]];
353 [[[snapshot contentView] viewWithTag: 1] removeFromSuperview];
354 [[[snapshot contentView] viewWithTag: 2] removeFromSuperview];
355 [snapshot setReleasedWhenClosed: YES((BOOL)1)];
356 [snapshot makeKeyAndOrderFront: self];
357 [snapshot update: self];
358}
2
Object leaked: object allocated and stored into 'snapshot' is not referenced later in this execution path and has a retain count of +1
359
360- (void) update: (id)sender
361{
362 Class *classList = GSDebugAllocationClassList ();
363 Class *pointer;
364 GSMemoryPanelEntry *entry;
365 int i, count, total, peak;
366 NSString *className;
367
368 pointer = classList;
369 i = 0;
370 [array removeAllObjects];
371 while (pointer[i] != NULL((void *)0))
372 {
373 className = NSStringFromClass (pointer[i]);
374 count = GSDebugAllocationCount (pointer[i]);
375 total = GSDebugAllocationTotal (pointer[i]);
376 peak = GSDebugAllocationPeak (pointer[i]);
377
378 /* Insert into array */
379 entry = [[GSMemoryPanelEntry alloc]
380 initWithString: className
381 count: count
382 total: total
383 peak: peak];
384 [array addObject: entry];
385 RELEASE (entry)[(entry) release];
386 i++;
387 }
388 NSZoneFree(NSDefaultMallocZone(), classList);
389
390 [array sortUsingSelector: orderingBy];
391
392 [table reloadData];
393}
394
395- (void) reorder: (id)sender
396{
397 int selectedColumn = [table clickedColumn];
398 NSArray *tableColumns = [table tableColumns];
399 id identifier;
400 SEL newOrderingBy = @selector(compareByCount:);
401
402 if (selectedColumn == -1)
403 {
404 return;
405 }
406
407 identifier
408 = [(NSTableColumn*)[tableColumns objectAtIndex: selectedColumn] identifier];
409
410 if ([identifier isEqual: @"Class"])
411 {
412 newOrderingBy = @selector(compareByClassName:);
413 }
414 else if ([identifier isEqual: @"Count"])
415 {
416 newOrderingBy = @selector(compareByCount:);
417 }
418 else if ([identifier isEqual: @"Total"])
419 {
420 newOrderingBy = @selector(compareByTotal:);
421 }
422 else if ([identifier isEqual: @"Peak"])
423 {
424 newOrderingBy = @selector(compareByPeak:);
425 }
426
427 if (newOrderingBy == orderingBy)
428 {
429 return;
430 }
431 else
432 {
433 orderingBy = newOrderingBy;
434 [array sortUsingSelector: orderingBy];
435 [table reloadData];
436 }
437}
438
439@end
440
441@implementation NSApplication (memoryPanel)
442
443- (void) orderFrontSharedMemoryPanel: (id)sender
444{
445 GSMemoryPanel *memoryPanel;
446
447 memoryPanel = [GSMemoryPanel sharedMemoryPanel];
448 [memoryPanel update: self];
449 [memoryPanel orderFront: self];
450}
451
452@end