├── README.markdown ├── deploy ├── AC_RunActiveContent.js ├── WSMonitor.html └── WSMonitor.swf └── source ├── de └── websector │ ├── .DS_Store │ └── util │ └── tools │ ├── .DS_Store │ └── memory │ ├── Logo.as │ └── WSMonitor.as └── fla ├── .DS_Store └── WSMonitor.fla /README.markdown: -------------------------------------------------------------------------------- 1 | #WSMonitor is a handy tool based on AS3 to detect memory issues in Flash or Flex applications. 2 | 3 | ##Author 4 | Jens Krause // [WEBSECTOR.DE](http://www.websector.de) 5 | 6 | ##Licence 7 | Licensed under the Mozilla Public License 1.1. [http://www.mozilla.org/MPL/MPL-1.1.html](http://www.mozilla.org/MPL/MPL-1.1.html) 8 | 9 | ##Usage 10 | Just follow the instruction posted here: [Detecting memory leaks in Flash or Flex applications using WSMonitor](http://www.websector.de/blog/2007/10/01/detecting-memory-leaks-in-flash-or-flex-applications-using-wsmonitor/) 11 | 12 | ##Note 13 | The source doesn't include the Font named "FFFHarmony". Grab it on [Fonts For Flash](http://fontsforflash.com/) 14 | 15 | ##Any questions? 16 | If you have any questions feel free to post these [on my blog post](http://www.websector.de/blog/2007/10/01/detecting-memory-leaks-in-flash-or-flex-applications-using-wsmonitor/) mentioned above or drop me an email. 17 | 18 | Have fun! 19 | 20 | -Jens -------------------------------------------------------------------------------- /deploy/AC_RunActiveContent.js: -------------------------------------------------------------------------------- 1 | //v1.7 2 | // Flash Player Version Detection 3 | // Detect Client Browser type 4 | // Copyright 2005-2007 Adobe Systems Incorporated. All rights reserved. 5 | var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false; 6 | var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false; 7 | var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false; 8 | 9 | function ControlVersion() 10 | { 11 | var version; 12 | var axo; 13 | var e; 14 | 15 | // NOTE : new ActiveXObject(strFoo) throws an exception if strFoo isn't in the registry 16 | 17 | try { 18 | // version will be set for 7.X or greater players 19 | axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7"); 20 | version = axo.GetVariable("$version"); 21 | } catch (e) { 22 | } 23 | 24 | if (!version) 25 | { 26 | try { 27 | // version will be set for 6.X players only 28 | axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6"); 29 | 30 | // installed player is some revision of 6.0 31 | // GetVariable("$version") crashes for versions 6.0.22 through 6.0.29, 32 | // so we have to be careful. 33 | 34 | // default to the first public version 35 | version = "WIN 6,0,21,0"; 36 | 37 | // throws if AllowScripAccess does not exist (introduced in 6.0r47) 38 | axo.AllowScriptAccess = "always"; 39 | 40 | // safe to call for 6.0r47 or greater 41 | version = axo.GetVariable("$version"); 42 | 43 | } catch (e) { 44 | } 45 | } 46 | 47 | if (!version) 48 | { 49 | try { 50 | // version will be set for 4.X or 5.X player 51 | axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3"); 52 | version = axo.GetVariable("$version"); 53 | } catch (e) { 54 | } 55 | } 56 | 57 | if (!version) 58 | { 59 | try { 60 | // version will be set for 3.X player 61 | axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3"); 62 | version = "WIN 3,0,18,0"; 63 | } catch (e) { 64 | } 65 | } 66 | 67 | if (!version) 68 | { 69 | try { 70 | // version will be set for 2.X player 71 | axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"); 72 | version = "WIN 2,0,0,11"; 73 | } catch (e) { 74 | version = -1; 75 | } 76 | } 77 | 78 | return version; 79 | } 80 | 81 | // JavaScript helper required to detect Flash Player PlugIn version information 82 | function GetSwfVer(){ 83 | // NS/Opera version >= 3 check for Flash plugin in plugin array 84 | var flashVer = -1; 85 | 86 | if (navigator.plugins != null && navigator.plugins.length > 0) { 87 | if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) { 88 | var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : ""; 89 | var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description; 90 | var descArray = flashDescription.split(" "); 91 | var tempArrayMajor = descArray[2].split("."); 92 | var versionMajor = tempArrayMajor[0]; 93 | var versionMinor = tempArrayMajor[1]; 94 | var versionRevision = descArray[3]; 95 | if (versionRevision == "") { 96 | versionRevision = descArray[4]; 97 | } 98 | if (versionRevision[0] == "d") { 99 | versionRevision = versionRevision.substring(1); 100 | } else if (versionRevision[0] == "r") { 101 | versionRevision = versionRevision.substring(1); 102 | if (versionRevision.indexOf("d") > 0) { 103 | versionRevision = versionRevision.substring(0, versionRevision.indexOf("d")); 104 | } 105 | } 106 | var flashVer = versionMajor + "." + versionMinor + "." + versionRevision; 107 | } 108 | } 109 | // MSN/WebTV 2.6 supports Flash 4 110 | else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4; 111 | // WebTV 2.5 supports Flash 3 112 | else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3; 113 | // older WebTV supports Flash 2 114 | else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2; 115 | else if ( isIE && isWin && !isOpera ) { 116 | flashVer = ControlVersion(); 117 | } 118 | return flashVer; 119 | } 120 | 121 | // When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available 122 | function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision) 123 | { 124 | versionStr = GetSwfVer(); 125 | if (versionStr == -1 ) { 126 | return false; 127 | } else if (versionStr != 0) { 128 | if(isIE && isWin && !isOpera) { 129 | // Given "WIN 2,0,0,11" 130 | tempArray = versionStr.split(" "); // ["WIN", "2,0,0,11"] 131 | tempString = tempArray[1]; // "2,0,0,11" 132 | versionArray = tempString.split(","); // ['2', '0', '0', '11'] 133 | } else { 134 | versionArray = versionStr.split("."); 135 | } 136 | var versionMajor = versionArray[0]; 137 | var versionMinor = versionArray[1]; 138 | var versionRevision = versionArray[2]; 139 | 140 | // is the major.revision >= requested major.revision AND the minor version >= requested minor 141 | if (versionMajor > parseFloat(reqMajorVer)) { 142 | return true; 143 | } else if (versionMajor == parseFloat(reqMajorVer)) { 144 | if (versionMinor > parseFloat(reqMinorVer)) 145 | return true; 146 | else if (versionMinor == parseFloat(reqMinorVer)) { 147 | if (versionRevision >= parseFloat(reqRevision)) 148 | return true; 149 | } 150 | } 151 | return false; 152 | } 153 | } 154 | 155 | function AC_AddExtension(src, ext) 156 | { 157 | if (src.indexOf('?') != -1) 158 | return src.replace(/\?/, ext+'?'); 159 | else 160 | return src + ext; 161 | } 162 | 163 | function AC_Generateobj(objAttrs, params, embedAttrs) 164 | { 165 | var str = ''; 166 | if (isIE && isWin && !isOpera) 167 | { 168 | str += ' '; 177 | } 178 | str += ''; 179 | } 180 | else 181 | { 182 | str += ' 2 | 3 | 4 | WSMonitor 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 43 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /deploy/WSMonitor.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sectore/wsmonitor/314fd24a00d559962268aacc420bd3a93a4a1b06/deploy/WSMonitor.swf -------------------------------------------------------------------------------- /source/de/websector/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sectore/wsmonitor/314fd24a00d559962268aacc420bd3a93a4a1b06/source/de/websector/.DS_Store -------------------------------------------------------------------------------- /source/de/websector/util/tools/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sectore/wsmonitor/314fd24a00d559962268aacc420bd3a93a4a1b06/source/de/websector/util/tools/.DS_Store -------------------------------------------------------------------------------- /source/de/websector/util/tools/memory/Logo.as: -------------------------------------------------------------------------------- 1 | /** 2 | * Logo 3 | * @author Jens Krause [www.websector.de] 4 | */ 5 | package de.websector.util.tools.memory 6 | { import flash.display.Sprite; 7 | 8 | internal class Logo extends Sprite {} } -------------------------------------------------------------------------------- /source/de/websector/util/tools/memory/WSMonitor.as: -------------------------------------------------------------------------------- 1 | /** 2 | * WSMonitor 3 | * 4 | * @author Jens Krause [www.websector.de] 5 | * @date 10/01/07 6 | * @see http://www.websector.de/blog/2007/10/01/detecting-memory-leaks-in-flash-or-flex-applications-using-wsmonitor/ 7 | */ 8 | package de.websector.util.tools.memory 9 | { 10 | import flash.display.Sprite; 11 | import flash.events.MouseEvent; 12 | import flash.events.TimerEvent; 13 | import flash.filters.DropShadowFilter; 14 | import flash.net.URLRequest; 15 | import flash.net.navigateToURL; 16 | import flash.system.Capabilities; 17 | import flash.system.System; 18 | import flash.text.TextField; 19 | import flash.text.TextFieldAutoSize; 20 | import flash.text.TextFormat; 21 | import flash.utils.Timer; 22 | 23 | import de.websector.util.tools.memory.Logo; 24 | 25 | import fl.controls.Button; 26 | public class WSMonitor extends Sprite 27 | { 28 | // 29 | // vars 30 | private var _memMinValue: uint = 0; 31 | private var _memMaxValue: uint = 0; 32 | private var _memCurrentValue: uint = 0; 33 | private var _memLastValue: uint = 0; 34 | private var _firstRun: Boolean = true; 35 | // 36 | // const 37 | private static const WIDTH: uint = 330; 38 | private static const HEIGHT: uint = 220; 39 | 40 | private static const FONT: String = "FFF Harmony"; 41 | 42 | private static const GRAPH_AREA_DIFF: uint = 2; 43 | private static const GRAPH_AREA_WIDTH: uint = 308; 44 | private static const GRAPH_AREA_HEIGHT: uint = 120; 45 | 46 | // private static const COLOR_NORMAL: uint = 0x00CCFF; 47 | private static const COLOR_NORMAL: uint = 0xFFFFFF; 48 | private static const COLOR_MIN: uint = 0x99FF00; 49 | private static const COLOR_MAX: uint = 0xF60094; 50 | private static const GRAPH_THICKNESS: uint = 1; 51 | 52 | private static const MEMORY_MAX_VALUES: uint = 78; 53 | // 54 | // instances 55 | private var _dataTxt : TextField; 56 | private var _dataTxtFormat : TextFormat; 57 | 58 | private var _bg : Sprite; 59 | 60 | private var _bgGraphArea : Sprite; 61 | private var _graph: Sprite; 62 | private var _holderGraph: Sprite; 63 | private var _bgGraph: Sprite; 64 | private var _maskGraph: Sprite; 65 | 66 | private var _graphTimer : Timer; 67 | 68 | private var _memValues: Array; 69 | 70 | private var _b_start: Button; 71 | private var _b_pause: Button; 72 | 73 | /** 74 | * constructor 75 | */ 76 | public function WSMonitor() 77 | { 78 | createChildren(); 79 | } 80 | 81 | /** 82 | * Creates all child elements 83 | */ 84 | private function createChildren (): void 85 | { 86 | 87 | // 88 | // backround 89 | _bg = new Sprite(); 90 | _bg.graphics.beginFill(0, 1); 91 | _bg.graphics.drawRoundRect(0, 0, WIDTH, HEIGHT, 15, 15); 92 | _bg.graphics.endFill(); 93 | addChild(_bg); 94 | 95 | // 96 | // logo 97 | var logo: Logo = new Logo(); 98 | logo.x = WIDTH - logo.width - 20; 99 | logo.y = 5; 100 | logo.addEventListener(MouseEvent.CLICK, 101 | function(even: MouseEvent): void 102 | { 103 | navigateToURL( new URLRequest("http://www.websector.de/blog/")); 104 | }); 105 | logo.buttonMode = true; 106 | addChild(logo); 107 | 108 | // 109 | // txtField for data 110 | _dataTxtFormat = new TextFormat(); 111 | _dataTxtFormat.font = FONT; 112 | _dataTxtFormat.color = 0xFFFFFF; 113 | _dataTxtFormat.size = 8; 114 | 115 | _dataTxt = new TextField(); 116 | _dataTxt.embedFonts = true; 117 | _dataTxt.multiline = true; 118 | _dataTxt.selectable = false; 119 | _dataTxt.autoSize = TextFieldAutoSize.LEFT; 120 | _dataTxt.defaultTextFormat = _dataTxtFormat; 121 | _dataTxt.x = 10; 122 | _dataTxt.y = 5; 123 | addChild(_dataTxt); 124 | 125 | showTxtData(); 126 | 127 | 128 | // 129 | // backround of graph 130 | _bgGraphArea = new Sprite(); 131 | _bgGraphArea.graphics.beginFill(0xFFFFFF, .1); 132 | _bgGraphArea.graphics.lineStyle(1, 0xFFFFFF); 133 | _bgGraphArea.graphics.drawRect( -GRAPH_AREA_DIFF, 134 | -GRAPH_AREA_DIFF, 135 | GRAPH_AREA_WIDTH + GRAPH_AREA_DIFF, 136 | GRAPH_AREA_HEIGHT + GRAPH_AREA_DIFF); 137 | _bgGraphArea.graphics.endFill(); 138 | _bgGraphArea.x = 12; 139 | _bgGraphArea.y = _dataTxt.y + _dataTxt.height + 10; 140 | 141 | addChild(_bgGraphArea); 142 | 143 | // 144 | // graph holder 145 | _holderGraph = new Sprite(); 146 | _bgGraphArea.addChild(_holderGraph); 147 | 148 | // 149 | // graph background 150 | _bgGraph = new Sprite(); 151 | _bgGraph.y = GRAPH_AREA_HEIGHT; 152 | _holderGraph.addChild(_bgGraph); 153 | // 154 | // graph sprite 155 | _graph = new Sprite(); 156 | _graph.y = GRAPH_AREA_HEIGHT; 157 | _holderGraph.addChild(_graph); 158 | 159 | // 160 | // 161 | var dropShadow: DropShadowFilter = new DropShadowFilter(); 162 | dropShadow.blurX = 2; 163 | dropShadow.blurY = 2; 164 | dropShadow.distance = 2; 165 | dropShadow.angle = 135; 166 | dropShadow.quality = 1; 167 | dropShadow.alpha = 0.85; 168 | dropShadow.color = 0; 169 | 170 | _graph.filters = [dropShadow]; 171 | 172 | 173 | // 174 | // mask graph and its bg 175 | _maskGraph = new Sprite(); 176 | _maskGraph.graphics.beginFill(0xFFFFFF, .1); 177 | _maskGraph.graphics.drawRect(0, 0, GRAPH_AREA_WIDTH, GRAPH_AREA_HEIGHT); 178 | _maskGraph.graphics.endFill(); 179 | 180 | _holderGraph.addChild(_maskGraph); 181 | _holderGraph.mask = _maskGraph; 182 | 183 | // 184 | // timer 185 | _graphTimer = new Timer(80, 0); 186 | _graphTimer.addEventListener(TimerEvent.TIMER, graphTimerHandler); 187 | 188 | // 189 | // store mem values 190 | _memValues = new Array(); 191 | 192 | // 193 | // txtField for data 194 | var _disabledTxtFormat: TextFormat = new TextFormat(); 195 | _disabledTxtFormat.font = FONT; 196 | _disabledTxtFormat.color = 0x999999; 197 | _disabledTxtFormat.size = 8; 198 | 199 | _b_start = new Button(); 200 | _b_start.x = 10; 201 | _b_start.y = _bgGraphArea.y + _bgGraphArea.height + 5; 202 | _b_start.width = 55; 203 | _b_start.height = 30; 204 | _b_start.toggle = true; 205 | _b_start.setStyle("textFormat", _dataTxtFormat); 206 | _b_start.setStyle("disabledTextFormat", _disabledTxtFormat); 207 | _b_start.setStyle("embedFonts", true); 208 | _b_start.label = "start"; 209 | _b_start.addEventListener(MouseEvent.CLICK, onClickStartButton); 210 | addChild(_b_start); 211 | 212 | _b_pause = new Button(); 213 | _b_pause.x = _b_start.x + _b_start.width + 10; 214 | _b_pause.y = _b_start.y; 215 | _b_pause.width = 55; 216 | _b_pause.height = 30; 217 | _b_pause.toggle = true; 218 | _b_pause.setStyle("textFormat", _dataTxtFormat); 219 | _b_pause.setStyle("embedFonts", true); 220 | _b_pause.setStyle("disabledTextFormat", _disabledTxtFormat); 221 | _b_pause.label = "pause"; 222 | _b_pause.enabled = false; 223 | _b_pause.addEventListener(MouseEvent.CLICK, onClickPauseButton); 224 | 225 | addChild(_b_pause); 226 | 227 | var _playerInfoTxt: TextField = new TextField(); 228 | _playerInfoTxt.embedFonts = true; 229 | _playerInfoTxt.selectable = false; 230 | _playerInfoTxt.defaultTextFormat = _dataTxtFormat; 231 | _playerInfoTxt.y = _b_start.y + 7; 232 | _playerInfoTxt.autoSize = TextFieldAutoSize.LEFT; 233 | _playerInfoTxt.htmlText = "flash player: " + Capabilities.version + ""; 234 | _playerInfoTxt.x = WIDTH - _playerInfoTxt.width - 20; 235 | addChild(_playerInfoTxt); 236 | 237 | }; 238 | 239 | /** 240 | * Shows memory data 241 | * 242 | */ 243 | private function showTxtData (): void 244 | { 245 | _dataTxt.htmlText = "memory usage: " + calculateMB(_memCurrentValue) + " MB (" + calculateKB(_memCurrentValue) + " kb) "; 246 | _dataTxt.htmlText += "memory min: " + calculateMB(_memMinValue) + " MB (" + calculateKB(_memMinValue) + " kb) "; 247 | _dataTxt.htmlText += "memory max: " + calculateMB(_memMaxValue) + " MB (" + calculateKB(_memMaxValue) + " kb) "; 248 | }; 249 | 250 | /** 251 | * Draws a graph using memory data 252 | * 253 | */ 254 | private function drawGraph (): void 255 | { 256 | 257 | // clear graph and its background 258 | _graph.graphics.clear(); 259 | _bgGraph.graphics.clear(); 260 | 261 | var color: uint = getGraphColor(_memCurrentValue, _memLastValue); 262 | var yPos: uint = getGraphPosY(_memValues[0]); 263 | 264 | _graph.graphics.lineStyle(GRAPH_THICKNESS, color); 265 | _graph.graphics.moveTo(0, -yPos); 266 | 267 | var i: int; 268 | var l: int = _memValues.length; 269 | for(i=0; i MEMORY_MAX_VALUES) _memValues.splice(0, 1); 418 | // 419 | // copy _memValues to calculate min and max values 420 | var _memValuesCopy: Array = _memValues.slice(); 421 | _memValuesCopy.sort(Array.NUMERIC); 422 | // min 423 | var min: uint = _memValuesCopy[0]; 424 | _memMinValue = (_memMinValue > min) ? min : _memMinValue; 425 | // max 426 | var max: uint = _memValuesCopy[_memValuesCopy.length - 1]; 427 | _memMaxValue = (_memMaxValue < max) ? max : _memMaxValue; 428 | 429 | showTxtData(); 430 | 431 | drawGraph(); 432 | 433 | }; 434 | 435 | /////////////////////////////////////////////////////////// 436 | // helper methods 437 | /////////////////////////////////////////////////////////// 438 | 439 | /** 440 | * Getting the right color for the graph depending on memory data 441 | * @param value1 current value using memoryTotal 442 | * @param value2 value before using memoryTotal 443 | * @return value of color 444 | */ 445 | private function getGraphColor (value1: int, value2:int): uint 446 | { 447 | var color: uint; 448 | 449 | if (value1 > value2) 450 | { 451 | color = COLOR_MAX; 452 | } 453 | else if (value1 < value2) 454 | { 455 | color = COLOR_MIN; 456 | } 457 | else 458 | { 459 | color = COLOR_NORMAL; 460 | } 461 | 462 | return color; 463 | }; 464 | 465 | /** 466 | * Getting the graph y-position depending on the latest memory value 467 | * @param value current value using memoryTotal 468 | * @return value of rounded y-position 469 | */ 470 | private function getGraphPosY (value: int): uint 471 | { 472 | return Math.round(GRAPH_AREA_HEIGHT * (value - _memMinValue) / (_memMaxValue - _memMinValue)); 473 | }; 474 | 475 | /** 476 | * Convert a value of byte in a value in kb 477 | * @param value current value using memoryTotal 478 | * @return value in kb 479 | * 480 | */ 481 | private function calculateKB(value: uint): uint 482 | { 483 | return Math.round(value / 1024); 484 | } 485 | 486 | /** 487 | * Convert a value of byte in a value in megabyte 488 | * @param value current value using memoryTotal 489 | * @return value in MB 490 | * 491 | */ 492 | private function calculateMB(value: uint): Number 493 | { 494 | // calculate MB rounding with two digits 495 | var newValue: Number = Math.round(value / 1024 / 1024 * 100); 496 | return newValue / 100; 497 | } 498 | 499 | 500 | /////////////////////////////////////////////////////////// 501 | // toString 502 | /////////////////////////////////////////////////////////// 503 | 504 | override public function toString() : String 505 | { 506 | return "[Instance of: de.websector.util.tools.WSProfiler]"; 507 | } 508 | } 509 | } -------------------------------------------------------------------------------- /source/fla/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sectore/wsmonitor/314fd24a00d559962268aacc420bd3a93a4a1b06/source/fla/.DS_Store -------------------------------------------------------------------------------- /source/fla/WSMonitor.fla: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sectore/wsmonitor/314fd24a00d559962268aacc420bd3a93a4a1b06/source/fla/WSMonitor.fla --------------------------------------------------------------------------------