| 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 |
| 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 | |||
| 41 | enum { | ||
| 42 | OrderByClassName, | ||
| 43 | OrderByCount, | ||
| 44 | OrderByTotal, | ||
| 45 | OrderByPeak | ||
| 46 | }; | ||
| 47 | |||
| 48 | static inline NSComparisonResult | ||
| 49 | invertComparison (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 | |||
| 163 | static 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]; | ||
| |||
| 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 | } | ||
| |||
| 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 |