| 1 |
/** |
|---|
| 2 |
* ThreadViewer for ActionScript Thread Library |
|---|
| 3 |
* |
|---|
| 4 |
* @author Copyright (c) 2008 daoki2 |
|---|
| 5 |
* @version 0.6 |
|---|
| 6 |
* @link http://www.libspark.org/wiki/daoki2/ThreadViewer |
|---|
| 7 |
* @link http://homepage.mac.com/daoki2/ |
|---|
| 8 |
* |
|---|
| 9 |
* Copyright (c) 2008 daoki2 |
|---|
| 10 |
* |
|---|
| 11 |
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|---|
| 12 |
* of this software and associated documentation files (the "Software"), to deal |
|---|
| 13 |
* in the Software without restriction, including without limitation the rights |
|---|
| 14 |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|---|
| 15 |
* copies of the Software, and to permit persons to whom the Software is |
|---|
| 16 |
* furnished to do so, subject to the following conditions: |
|---|
| 17 |
* |
|---|
| 18 |
* The above copyright notice and this permission notice shall be included in |
|---|
| 19 |
* all copies or substantial portions of the Software. |
|---|
| 20 |
* |
|---|
| 21 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|---|
| 22 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|---|
| 23 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|---|
| 24 |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|---|
| 25 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|---|
| 26 |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|---|
| 27 |
* THE SOFTWARE. |
|---|
| 28 |
*/ |
|---|
| 29 |
|
|---|
| 30 |
import flash.events.*; |
|---|
| 31 |
import flash.filesystem.*; |
|---|
| 32 |
import flash.data.*; |
|---|
| 33 |
import flash.desktop.NativeApplication; |
|---|
| 34 |
import flash.net.LocalConnection; |
|---|
| 35 |
|
|---|
| 36 |
import mx.events.*; |
|---|
| 37 |
import mx.collections.ArrayCollection; |
|---|
| 38 |
import mx.controls.*; |
|---|
| 39 |
|
|---|
| 40 |
import org.libspark.snippets.AIRUtils.PreferenceUtil; |
|---|
| 41 |
import org.libspark.thread.ThreadState; |
|---|
| 42 |
import org.libspark.utils.SqlUtil; |
|---|
| 43 |
import org.libspark.snippets.controls.ChartData.FusionCharts.SingleChartData; |
|---|
| 44 |
|
|---|
| 45 |
private static const LEFT_PADD:uint = 38; // スレッド名のX座標 |
|---|
| 46 |
private var _conn:LocalConnection; // LocalConnection接続用 |
|---|
| 47 |
private var threadList:ArrayCollection = new ArrayCollection(); // スレッドリスト |
|---|
| 48 |
private var threadNameLabel:ArrayCollection = new ArrayCollection(); // スレッド名Labelのリスト |
|---|
| 49 |
private var threadStatus:ArrayCollection = new ArrayCollection(); // スレッド状態のリスト |
|---|
| 50 |
private var threadLog:ArrayCollection = new ArrayCollection(); // スレッド遷移グラフのリスト |
|---|
| 51 |
private var pos_x:Number = 0; // スレッド遷移グラフのX座標 |
|---|
| 52 |
private var isLogging:Boolean = false; // ログ収集中か否か |
|---|
| 53 |
private var framePerSecond:uint = 24; // フレーム/秒 |
|---|
| 54 |
private var frameInterval:Number = 1000/24; // 時間(ms)/フレーム |
|---|
| 55 |
private var _dbconn:SQLConnection; // ローカルデータベース接続用 |
|---|
| 56 |
|
|---|
| 57 |
/* ######## アプリケーション関連のハンドラ ######## */ |
|---|
| 58 |
|
|---|
| 59 |
/* |
|---|
| 60 |
* アプリケーションの初期化 |
|---|
| 61 |
*/ |
|---|
| 62 |
private function init():void { |
|---|
| 63 |
// イベントハンドラの登録 |
|---|
| 64 |
addEventListener(Event.CLOSING, onAppClosing); |
|---|
| 65 |
addEventListener(FlexNativeWindowBoundsEvent.WINDOW_RESIZE, onWinResize); |
|---|
| 66 |
threadName.addEventListener(MouseEvent.MOUSE_DOWN, threadName_MouseDownHandler); |
|---|
| 67 |
|
|---|
| 68 |
// プリファレンス(初期値)の読み込み |
|---|
| 69 |
var result:Object = PreferenceUtil.load(this, "preference.xml"); |
|---|
| 70 |
if (!result.status) |
|---|
| 71 |
trace(result.message); |
|---|
| 72 |
|
|---|
| 73 |
// 注:最小サイズを設定した時の動作が謎すぎるので、とりあえずコメントアウト |
|---|
| 74 |
//this.minWidth = 780; |
|---|
| 75 |
//this.minHeight = 520; |
|---|
| 76 |
// ウィンドウサイズが最小サイズより小さければ是正 |
|---|
| 77 |
//if (this.width < this.minWidth) |
|---|
| 78 |
// this.width = this.minWidth + 16; |
|---|
| 79 |
//if (this.height < this.minHeight) |
|---|
| 80 |
// this.height = this.minHeight + 16; |
|---|
| 81 |
|
|---|
| 82 |
this.visible = true; |
|---|
| 83 |
|
|---|
| 84 |
// LocalConnectionの作成 |
|---|
| 85 |
_conn = new LocalConnection(); |
|---|
| 86 |
_conn.client = this; |
|---|
| 87 |
|
|---|
| 88 |
try { |
|---|
| 89 |
// LocalConnectionに接続 |
|---|
| 90 |
_conn.connect("AS3ThreadLibV1"); // 接続名 |
|---|
| 91 |
_conn.allowDomain("*"); // どこからの接続でも許す |
|---|
| 92 |
|
|---|
| 93 |
// nopの生成 |
|---|
| 94 |
createNop(); |
|---|
| 95 |
|
|---|
| 96 |
// ローカルデータベースをオープン |
|---|
| 97 |
var dbFile:File = File.applicationDirectory.resolvePath("thread.db"); |
|---|
| 98 |
_dbconn = new SQLConnection(); |
|---|
| 99 |
_dbconn.open(dbFile, SQLMode.UPDATE); |
|---|
| 100 |
SqlUtil.executeSQLStatement(_dbconn, "delete from threadTime"); // 古いデータを削除 |
|---|
| 101 |
SqlUtil.executeSQLStatement(_dbconn, "delete from threadState"); // 古いデータを削除 |
|---|
| 102 |
|
|---|
| 103 |
// Chartの作業ファイルを設定 |
|---|
| 104 |
threadStateHtml = File.applicationStorageDirectory.resolvePath("threadState.html"); |
|---|
| 105 |
threadStateData = File.applicationStorageDirectory.resolvePath("threadState.xml"); |
|---|
| 106 |
threadTimeHtml = File.applicationStorageDirectory.resolvePath("threadTime.html"); |
|---|
| 107 |
threadTimeData = File.applicationStorageDirectory.resolvePath("threadTime.xml"); |
|---|
| 108 |
} catch(argerr:ArgumentError) { |
|---|
| 109 |
trace("Can't connect...the connection name is already used by another SWF"); |
|---|
| 110 |
} catch(error:Error) { |
|---|
| 111 |
trace(error.message); |
|---|
| 112 |
} |
|---|
| 113 |
} |
|---|
| 114 |
|
|---|
| 115 |
/* |
|---|
| 116 |
* アプリケーションの終了ハンドラ |
|---|
| 117 |
*/ |
|---|
| 118 |
private function onAppClosing(event:Event):void { |
|---|
| 119 |
|
|---|
| 120 |
// プリファレンス(初期値)の書き込み |
|---|
| 121 |
var prefList:ArrayCollection = new ArrayCollection(); |
|---|
| 122 |
prefList.addItem("nativeWindow.x"); // X座標 |
|---|
| 123 |
prefList.addItem("nativeWindow.y"); // Y座標 |
|---|
| 124 |
prefList.addItem("width"); // ウィンドウの幅 |
|---|
| 125 |
prefList.addItem("height"); // ウィンドウの高さ |
|---|
| 126 |
PreferenceUtil.save(this, prefList, "preference.xml"); |
|---|
| 127 |
|
|---|
| 128 |
// LocalConnectionをクローズ |
|---|
| 129 |
if (_dbconn != null) |
|---|
| 130 |
_dbconn.close(); |
|---|
| 131 |
} |
|---|
| 132 |
|
|---|
| 133 |
/* |
|---|
| 134 |
* Windowのリサイズハンドラ |
|---|
| 135 |
*/ |
|---|
| 136 |
private function onWinResize(event:FlexNativeWindowBoundsEvent):void { |
|---|
| 137 |
|
|---|
| 138 |
// スレッド名、スレッド遷移グラフの場所を元に戻す |
|---|
| 139 |
threadName.y = 0; |
|---|
| 140 |
threadTime.y = 0; |
|---|
| 141 |
|
|---|
| 142 |
// もしログ収集を止めてたら、スレッド実行時間のグラフを再描画 |
|---|
| 143 |
if (!isLogging && threadList.length != 0) |
|---|
| 144 |
createThreadTimeChart(); |
|---|
| 145 |
} |
|---|
| 146 |
|
|---|
| 147 |
/* ######## LocalConnection関連のイベントハンドラ ######## */ |
|---|
| 148 |
|
|---|
| 149 |
/* |
|---|
| 150 |
* 新規スレッドの取得ハンドラ |
|---|
| 151 |
*/ |
|---|
| 152 |
public function threadEntryHandler(name:String, newThread:Boolean = true):void { |
|---|
| 153 |
|
|---|
| 154 |
// ログ収集中でなければ終わる |
|---|
| 155 |
if (!isLogging) |
|---|
| 156 |
return; |
|---|
| 157 |
|
|---|
| 158 |
// スレッドリスト中にあるか? |
|---|
| 159 |
if (threadList.contains(name)) { |
|---|
| 160 |
trace("Thread", name, "already exists"); // 既に同名のスレッドが存在する |
|---|
| 161 |
} else { |
|---|
| 162 |
// スレッド名のLabelを生成 |
|---|
| 163 |
var t:Label = new Label(); |
|---|
| 164 |
t.x = LEFT_PADD; |
|---|
| 165 |
t.y = threadList.length * 16; |
|---|
| 166 |
t.text = name; |
|---|
| 167 |
threadName.addChild(t); |
|---|
| 168 |
threadNameLabel.addItem(t); |
|---|
| 169 |
|
|---|
| 170 |
// スレッド状態のImageを生成 |
|---|
| 171 |
var s:Image = new Image(); |
|---|
| 172 |
s.x = 8; |
|---|
| 173 |
s.y = threadList.length * 16 + 1; |
|---|
| 174 |
if (newThread) |
|---|
| 175 |
s.source = "icons/NEW.png"; // 新規のスレッド |
|---|
| 176 |
else |
|---|
| 177 |
s.source = "icons/RUNNABLE.png"; //既存のスレッド |
|---|
| 178 |
|
|---|
| 179 |
threadName.addChild(s); |
|---|
| 180 |
threadStatus.addItem(s); |
|---|
| 181 |
|
|---|
| 182 |
// スレッドリストに登録 |
|---|
| 183 |
threadList.addItem(name); |
|---|
| 184 |
} |
|---|
| 185 |
} |
|---|
| 186 |
|
|---|
| 187 |
/* |
|---|
| 188 |
* スレッドの名称変更取得ハンドラ |
|---|
| 189 |
*/ |
|---|
| 190 |
public function threadNameHandler(name:String, newName:String):void { |
|---|
| 191 |
|
|---|
| 192 |
// ログ収集中でなければ終わる |
|---|
| 193 |
if (!isLogging) |
|---|
| 194 |
return; |
|---|
| 195 |
|
|---|
| 196 |
// スレッドリスト中にあるか? |
|---|
| 197 |
if (!threadList.contains(name)) |
|---|
| 198 |
threadEntryHandler(name, false); // 無いので、既存のスレッドとして登録 |
|---|
| 199 |
|
|---|
| 200 |
// スレッドリストに登録されてるスレッド名を変更 |
|---|
| 201 |
var index:int = threadList.getItemIndex(name); |
|---|
| 202 |
threadList.removeItemAt(index); // 古いスレッド名を削除 |
|---|
| 203 |
threadList.addItemAt(newName, index); // 新しいスレッド名を登録 |
|---|
| 204 |
|
|---|
| 205 |
// Labelリストから該当のLabelを取得して、スレッド名を更新する |
|---|
| 206 |
var n:Label = Label(threadNameLabel.getItemAt(index - 1)); // threadListは"nop"も含むので、ひとつ引く |
|---|
| 207 |
n.text = newName; |
|---|
| 208 |
} |
|---|
| 209 |
|
|---|
| 210 |
/* |
|---|
| 211 |
* スレッドの状態取得ハンドラ |
|---|
| 212 |
*/ |
|---|
| 213 |
public function threadStateHandler(name:String, status:uint, time:int):void { |
|---|
| 214 |
|
|---|
| 215 |
// スレッド状態画像のファイル名 注:TERMINATINGとTERMINATEDは同じ画像を使う |
|---|
| 216 |
var StateImageFiles:Array = new Array("icons/NEW.png", "icons/RUNNABLE.png", "icons/WAITING.png", |
|---|
| 217 |
"icons/TIMED_WAITING.png", "icons/TERMINATED.png", "icons/TERMINATED.png"); |
|---|
| 218 |
|
|---|
| 219 |
// ログ収集中でなければ終わる |
|---|
| 220 |
if (!isLogging) |
|---|
| 221 |
return; |
|---|
| 222 |
|
|---|
| 223 |
// スレッドリスト中にあるか? |
|---|
| 224 |
if (!threadList.contains(name)) |
|---|
| 225 |
threadEntryHandler(name, false); // 無いので、既存のスレッドとして登録 |
|---|
| 226 |
|
|---|
| 227 |
// 状態リストから該当のImageを取得して、画像を更新する |
|---|
| 228 |
var index:int = threadList.getItemIndex(name); |
|---|
| 229 |
var s:Image = Image(threadStatus.getItemAt(index - 1)); // threadListは"nop"も含むので、ひとつ引く |
|---|
| 230 |
s.source = StateImageFiles[status]; |
|---|
| 231 |
|
|---|
| 232 |
// ローカルデータベースに情報を記録 |
|---|
| 233 |
SqlUtil.executeSQLStatement(_dbconn, "insert into threadState(name, state, time) values ('" + name + "', " + status + ", " + time + ")"); |
|---|
| 234 |
} |
|---|
| 235 |
|
|---|
| 236 |
private var b_color:uint = 0x0000ff; // スレッド遷移グラフのデフォルト色 |
|---|
| 237 |
|
|---|
| 238 |
/* |
|---|
| 239 |
* スレッドの実行時間取得ハンドラ |
|---|
| 240 |
*/ |
|---|
| 241 |
public function threadTimeHandler(name:String, startTime:int, endTime:int):void { |
|---|
| 242 |
|
|---|
| 243 |
// ログ収集中でなければ終わる |
|---|
| 244 |
if (!isLogging) |
|---|
| 245 |
return; |
|---|
| 246 |
|
|---|
| 247 |
// スレッドリスト中にあるか? |
|---|
| 248 |
if (!threadList.contains(name)) |
|---|
| 249 |
threadEntryHandler(name, false); // 無いので、既存のスレッドとして登録 |
|---|
| 250 |
|
|---|
| 251 |
var pos_y:int = threadList.getItemIndex(name) * 16; // スレッド遷移グラフのY座標 |
|---|
| 252 |
|
|---|
| 253 |
// スレッド遷移グラフの生成 |
|---|
| 254 |
var b:VBox = new VBox(); |
|---|
| 255 |
b.x = pos_x; |
|---|
| 256 |
b.y = pos_y; |
|---|
| 257 |
b.toolTip = String(endTime - startTime) + "ms"; |
|---|
| 258 |
b.width = (endTime - startTime) * hsScale.value; |
|---|
| 259 |
b.height = 16; |
|---|
| 260 |
b.setStyle("backgroundColor", b_color); |
|---|
| 261 |
threadTime.addChild(b); |
|---|
| 262 |
threadLog.addItem({bar:b, time:(endTime - startTime)}); // スレッド遷移グラフリストへ登録 |
|---|
| 263 |
|
|---|
| 264 |
pos_x += b.width; // 次回の描画X座標の計算 |
|---|
| 265 |
|
|---|
| 266 |
// もしフレーム同期ハンドラが呼ばれた直後だったら? |
|---|
| 267 |
// 注:フレーム同期の直後は"nop"が来る |
|---|
| 268 |
if (name == "nop") { |
|---|
| 269 |
checkInterval(endTime, endTime - startTime); // フレーム同期間隔の確認 |
|---|
| 270 |
|
|---|
| 271 |
// フレーム毎に色を入れ替える |
|---|
| 272 |
if (b_color == 0x0000ff) |
|---|
| 273 |
b_color = 0x8888ff; |
|---|
| 274 |
else |
|---|
| 275 |
b_color = 0x0000ff; |
|---|
| 276 |
} |
|---|
| 277 |
|
|---|
| 278 |
// ローカルデータベースに情報を記録 |
|---|
| 279 |
SqlUtil.executeSQLStatement(_dbconn, "insert into threadTime(name, startTime, endTime, elapse) values ('" + name + "', " + startTime + ", " + endTime + ", " + (endTime - startTime) + ")"); |
|---|
| 280 |
tElapseTime.text = convertElapseTime(pos_x); |
|---|
| 281 |
} |
|---|
| 282 |
|
|---|
| 283 |
/* ######## コンポーネントのイベントハンドラ ######## */ |
|---|
| 284 |
|
|---|
| 285 |
/* |
|---|
| 286 |
* Record/Stopボタンのクリックハンドラ |
|---|
| 287 |
*/ |
|---|
| 288 |
private function bRecordStop_ClickHandler(event:MouseEvent):void { |
|---|
| 289 |
|
|---|
| 290 |
hsScale.enabled = isLogging; // 縮尺スケーラを有効/無効にする |
|---|
| 291 |
|
|---|
| 292 |
// もしログ収集中だったら? |
|---|
| 293 |
if (isLogging) { |
|---|
| 294 |
// ログ収集を止める |
|---|
| 295 |
bRecordStop.toolTip = "Record"; // ログ収集を止めるから次回は"Record"。ややこしい... |
|---|
| 296 |
threadList.removeItemAt(0); // スレッドリストから"nop"を削除 |
|---|
| 297 |
// もしスレッドが1つでもあれば |
|---|
| 298 |
if (threadList.length != 0) { |
|---|
| 299 |
// スレッド選択コンボボックスの更新 |
|---|
| 300 |
cbThread.dataProvider = threadList; |
|---|
| 301 |
cbThread.selectedItem = threadList.getItemAt(0); |
|---|
| 302 |
|
|---|
| 303 |
// スレッド状態Chartの作成 |
|---|
| 304 |
createThreadStateChart(String(threadList.getItemAt(0))); |
|---|
| 305 |
|
|---|
| 306 |
// スレッド実行時間のアップデート |
|---|
| 307 |
updateThreadTimes(String(threadList.getItemAt(0))); |
|---|
| 308 |
|
|---|
| 309 |
// スレッド実行時間Chartの作成 |
|---|
| 310 |
createThreadTimeChart(); |
|---|
| 311 |
} |
|---|
| 312 |
} else { |
|---|
| 313 |
bRecordStop.toolTip = "Stop"; // ログ収集を始めるから次回は"Stop"。やっぱりややこしい... |
|---|
| 314 |
} |
|---|
| 315 |
|
|---|
| 316 |
isLogging = !isLogging; // ログ収集中なら止める。収集してなかったら、始める。 |
|---|
| 317 |
bRecordStop.enabled = isLogging; // Recordボタンを無効/有効にする |
|---|
| 318 |
} |
|---|
| 319 |
|
|---|
| 320 |
/* |
|---|
| 321 |
* Clearボタンのクリックハンドラ |
|---|
| 322 |
*/ |
|---|
| 323 |
private function bClear_ClickHandler(event:MouseEvent):void { |
|---|
| 324 |
|
|---|
| 325 |
// 管理リストの削除 |
|---|
| 326 |
threadLog.removeAll(); |
|---|
| 327 |
threadList.removeAll(); |
|---|
| 328 |
threadNameLabel.removeAll(); |
|---|
| 329 |
threadStatus.removeAll(); |
|---|
| 330 |
|
|---|
| 331 |
// スレッド遷移グラフの削除 |
|---|
| 332 |
threadName.removeAllChildren(); |
|---|
| 333 |
threadTime.removeAllChildren(); |
|---|
| 334 |
pos_x = 0; // 描画X座標を先頭に戻す |
|---|
| 335 |
|
|---|
| 336 |
// 画面に表示されてるコンポーネント関連を初期化 |
|---|
| 337 |
bRecordStop.enabled = true; |
|---|
| 338 |
cbThread.dataProvider = null; |
|---|
| 339 |
tIssue.text = ""; |
|---|
| 340 |
tElapse.text = ""; |
|---|
| 341 |
tAveTime.text = ""; |
|---|
| 342 |
tMaxTime.text = ""; |
|---|
| 343 |
tMinTime.text = ""; |
|---|
| 344 |
htmlState.htmlText = ""; |
|---|
| 345 |
htmlTime.htmlText = ""; |
|---|
| 346 |
tElapseTime.text = "00:00:00.000"; |
|---|
| 347 |
tElapseMax.text = "Max 00.000"; |
|---|
| 348 |
tElapseMin.text = "Min 00.000"; |
|---|
| 349 |
tElapseAve.text = "Ave 00.000"; |
|---|
| 350 |
tFPS.text = "FPS: 000"; |
|---|
| 351 |
threadName.y = 0; |
|---|
| 352 |
threadTime.y = 0; |
|---|
| 353 |
|
|---|
| 354 |
// 内部変数の初期化 |
|---|
| 355 |
frameGet = false; |
|---|
| 356 |
maxInterval = 0; |
|---|
| 357 |
maxIntPos = 0; |
|---|
| 358 |
minInterval = 10000; |
|---|
| 359 |
minIntPos = 0; |
|---|
| 360 |
lastNopTime = 0; |
|---|
| 361 |
totalInterval = 0; |
|---|
| 362 |
intervalCount = 0; |
|---|
| 363 |
|
|---|
| 364 |
// ローカルデータベースの古いデータを削除 |
|---|
| 365 |
SqlUtil.executeSQLStatement(_dbconn, "delete from threadTime"); |
|---|
| 366 |
SqlUtil.executeSQLStatement(_dbconn, "delete from threadState"); |
|---|
| 367 |
|
|---|
| 368 |
// nopを生成 |
|---|
| 369 |
createNop(); |
|---|
| 370 |
} |
|---|
| 371 |
|
|---|
| 372 |
/* |
|---|
| 373 |
* スケールの変更ハンドラ |
|---|
| 374 |
*/ |
|---|
| 375 |
private function hsScale_ChangeHandler(event:SliderEvent):void { |
|---|
| 376 |
|
|---|
| 377 |
// 描画X座標を先頭に戻す |
|---|
| 378 |
pos_x = 0; |
|---|
| 379 |
|
|---|
| 380 |
// スレッド遷移グラフの位置とサイズを更新 |
|---|
| 381 |
for each (var val:* in threadLog) { |
|---|
| 382 |
var b:VBox = VBox(val.bar); |
|---|
| 383 |
var time:int = int(val.time); |
|---|
| 384 |
b.x = pos_x; |
|---|
| 385 |
b.width = time * event.value; // 縮尺と時間からサイズを計算 |
|---|
| 386 |
pos_x += b.width; |
|---|
| 387 |
} |
|---|
| 388 |
} |
|---|
| 389 |
|
|---|
| 390 |
/* |
|---|
| 391 |
* スレッド選択コンボボックスのクローズハンドラ |
|---|
| 392 |
*/ |
|---|
| 393 |
private function cbThread_CloseHandler(event:Event):void { |
|---|
| 394 |
|
|---|
| 395 |
// スレッド状態Chartの作成 |
|---|
| 396 |
createThreadStateChart(String(ComboBox(event.target).selectedItem)); |
|---|
| 397 |
|
|---|
| 398 |
// スレッド実行時間の更新 |
|---|
| 399 |
updateThreadTimes(String(ComboBox(event.target).selectedItem)); |
|---|
| 400 |
} |
|---|
| 401 |
|
|---|
| 402 |
/* |
|---|
| 403 |
* インフォメーション表示 |
|---|
| 404 |
*/ |
|---|
| 405 |
private function bInfo_ClickHandler(event:MouseEvent):void { |
|---|
| 406 |
// Publisher IDをアラートで表示 |
|---|
| 407 |
Alert.show(NativeApplication.nativeApplication.publisherID, 'Publisher ID'); |
|---|
| 408 |
} |
|---|
| 409 |
|
|---|
| 410 |
/* |
|---|
| 411 |
* Interval Maxラベルのクリックハンドラ |
|---|
| 412 |
*/ |
|---|
| 413 |
private function tElapseMax_ClickHandler(event:MouseEvent):void { |
|---|
| 414 |
// フレーム同期間隔が最大の場所にスクロール |
|---|
| 415 |
threadTimeContainer.horizontalScrollPosition = maxIntPos * hsScale.value; |
|---|
| 416 |
} |
|---|
| 417 |
|
|---|
| 418 |
/* |
|---|
| 419 |
* Interval Minラベルのクリックハンドラ |
|---|
| 420 |
*/ |
|---|
| 421 |
private function tElapseMin_ClickHandler(event:MouseEvent):void { |
|---|
| 422 |
// フレーム同期間隔が最小の場所にスクロール |
|---|
| 423 |
threadTimeContainer.horizontalScrollPosition = minIntPos * hsScale.value; |
|---|
| 424 |
} |
|---|
| 425 |
|
|---|
| 426 |
/* |
|---|
| 427 |
* threadNameのドラッグ制御 |
|---|
| 428 |
*/ |
|---|
| 429 |
private var m_x:Number; |
|---|
| 430 |
private var m_y:Number; |
|---|
| 431 |
private function threadName_MouseDownHandler(event:MouseEvent):void { |
|---|
| 432 |
|
|---|
| 433 |
// 現座標の登録 |
|---|
| 434 |
m_x = event.stageX; |
|---|
| 435 |
m_y = event.stageY; |
|---|
| 436 |
|
|---|
| 437 |
// イベントハンドラの登録 |
|---|
| 438 |
threadName.addEventListener(MouseEvent.MOUSE_MOVE, threadName_MouseMoveHandler); |
|---|
| 439 |
threadName.addEventListener(MouseEvent.MOUSE_UP, threadName_MouseUpHandler); |
|---|
| 440 |
} |
|---|
| 441 |
|
|---|
| 442 |
private var chata:Array = new Array(1, -1); // チャタリング防止用のバッファ |
|---|
| 443 |
private var chata_cancel:int = 0; // チャタリング防止キャンセルのカウンタ |
|---|
| 444 |
private function threadName_MouseMoveHandler(event:MouseEvent):void { |
|---|
| 445 |
|
|---|
| 446 |
// もしスレッド名が表示領域に収まらないなら |
|---|
| 447 |
if (threadName.height > threadName.parent.height) { |
|---|
| 448 |
var delta_y:Number = event.stageY - m_y; // 移動量の計算 |
|---|
| 449 |
var pos_y:Number = threadName.y + delta_y; // Y座標を求める |
|---|
| 450 |
|
|---|
| 451 |
// チャタリング防止のアルゴリズム: |
|---|
| 452 |
// 過去2回のドラッグ方向(上もしくは下)と違ったらチャタリングと判断してキャンセル |
|---|
| 453 |
// ただし、同じ方向に2回続けば、チャタリングじゃないと判断 |
|---|
| 454 |
if (chata[0] < 0 && chata[1] < 0) { |
|---|
| 455 |
if (delta_y >= 0 && chata_cancel < 2) { |
|---|
| 456 |
pos_y = 1; |
|---|
| 457 |
chata_cancel++; |
|---|
| 458 |
} else { |
|---|
| 459 |
chata[0] = chata[1]; |
|---|
| 460 |
chata[1] = delta_y; |
|---|
| 461 |
chata_cancel = 0; |
|---|
| 462 |
} |
|---|
| 463 |
} else if (chata[0] >=0 && chata[1] >= 0) { |
|---|
| 464 |
if (delta_y < 0 && chata_cancel < 2) { |
|---|
| 465 |
pos_y = 1; |
|---|
| 466 |
chata_cancel++; |
|---|
| 467 |
} else { |
|---|
| 468 |
chata[0] = chata[1]; |
|---|
| 469 |
chata[1] = delta_y; |
|---|
| 470 |
chata_cancel = 0; |
|---|
| 471 |
} |
|---|
| 472 |
} else { |
|---|
| 473 |
chata[0] = chata[1]; |
|---|
| 474 |
chata[1] = delta_y; |
|---|
| 475 |
} |
|---|
| 476 |
|
|---|
| 477 |
// 移動可能か? |
|---|
| 478 |
if ((pos_y <= 0) && (pos_y > (threadName.parent.height - threadName.height))) { |
|---|
| 479 |
threadName.y = pos_y; |
|---|
| 480 |
threadTime.y = pos_y; |
|---|
| 481 |
} |
|---|
| 482 |
} |
|---|
| 483 |
|
|---|
| 484 |
// 現座標を更新 |
|---|
| 485 |
m_x = event.stageX; |
|---|
| 486 |
m_y = event.stageY; |
|---|
| 487 |
} |
|---|
| 488 |
|
|---|
| 489 |
private function threadName_MouseUpHandler(event:MouseEvent):void { |
|---|
| 490 |
|
|---|
| 491 |
// イベントハンドラを削除 |
|---|
| 492 |
threadName.removeEventListener(MouseEvent.MOUSE_MOVE, threadName_MouseMoveHandler); |
|---|
| 493 |
threadName.removeEventListener(MouseEvent.MOUSE_UP, threadName_MouseUpHandler); |
|---|
| 494 |
|
|---|
| 495 |
// チャタリング防止解除 |
|---|
| 496 |
chata[0] = 1; |
|---|
| 497 |
chata[1] = -1; |
|---|
| 498 |
chata_cancel = 0; |
|---|
| 499 |
} |
|---|
| 500 |
|
|---|
| 501 |
/* ######## ローカル関数 ####### */ |
|---|
| 502 |
|
|---|
| 503 |
/* |
|---|
| 504 |
* スレッドの状態の割合グラフの生成 |
|---|
| 505 |
*/ |
|---|
| 506 |
private var threadStateHtml:File = null; // 作業ファイル |
|---|
| 507 |
private var threadStateData:File = null; // 作業ファイル |
|---|
| 508 |
private function createThreadStateChart(name:String):void { |
|---|
| 509 |
|
|---|
| 510 |
// それぞれのスレッド状態の時間を求める |
|---|
| 511 |
var result:ArrayCollection = getThreadStateTime(name); |
|---|
| 512 |
|
|---|
| 513 |
// スレッドの状態が1つ以上あればChartを生成 |
|---|
| 514 |
if (result.length != 0) { |
|---|
| 515 |
var chartData:SingleChartData = new SingleChartData(); |
|---|
| 516 |
chartData.graphAttribute.decimalPrecision = "0"; |
|---|
| 517 |
chartData.graphAttribute.formatNumberScale = "0"; |
|---|
| 518 |
for each (var val:* in result) { |
|---|
| 519 |
chartData.addSetElement(val); |
|---|
| 520 |
} |
|---|
| 521 |
chartData.createHtmlFile(threadStateHtml, "Doughnut2D", htmlState.width, htmlState.height, threadStateData.nativePath); |
|---|
| 522 |
chartData.createDataFile(threadStateData); |
|---|
| 523 |
htmlState.location = "file:///" + threadStateHtml.nativePath; |
|---|
| 524 |
} else |
|---|
| 525 |
htmlState.htmlText = ""; // 空のHTMLを表示 |
|---|
| 526 |
} |
|---|
| 527 |
|
|---|
| 528 |
/* |
|---|
| 529 |
* スレッドの状態時間の演算 |
|---|
| 530 |
*/ |
|---|
| 531 |
private function getThreadStateTime(name:String):ArrayCollection { |
|---|
| 532 |
|
|---|
| 533 |
// Chart用の情報 |
|---|
| 534 |
var stateName:Array = new Array("NEW", "RUNNABLE", "WAITING", "TIMED_WAITING", "TERMINATING", "TEMINATED"); // スレッドの状態名 |
|---|
| 535 |
var stateColor:Array = new Array("E1E1E1", "00FF00", "FF0000", "FFFF00", "656565", "656565"); // スレッドの状態色 |
|---|
| 536 |
var elapse:Array = new Array(0, 0, 0, 0, 0, 0); // スレッドの状態時間 |
|---|
| 537 |
|
|---|
| 538 |
// 該当スレッドの状態データを取得 |
|---|
| 539 |
var result:Array = SqlUtil.getData(_dbconn, "select name, state, time from threadState where name == '" + name + "'"); |
|---|
| 540 |
|
|---|
| 541 |
var currentState:int = 0; |
|---|
| 542 |
var lastTime:int = 0; |
|---|
| 543 |
var value:ArrayCollection = new ArrayCollection(); |
|---|
| 544 |
|
|---|
| 545 |
// もし状態データが存在すれば |
|---|
| 546 |
if (result[1].data != null) { |
|---|
| 547 |
for each (var val:* in result[1].data) { |
|---|
| 548 |
elapse[currentState] += (val.time - lastTime); // スレッドの状態時間を計算する |
|---|
| 549 |
currentState = val.state; |
|---|
| 550 |
lastTime = val.time; |
|---|
| 551 |
} |
|---|
| 552 |
// スレッドの状態遷移が1つ以上あれば |
|---|
| 553 |
if (result[1].data.length > 1) { |
|---|
| 554 |
// スレッドの状態時間をChart用データに登録 |
|---|
| 555 |
for (var i:uint = 1; i < 6; i++) |
|---|
| 556 |
value.addItem({name: stateName[i], value: elapse[i], color: stateColor[i]}); |
|---|
| 557 |
} else { |
|---|
| 558 |
// 無ければスレッドの状態時間は正しく求められないので、仕方ないからスレッドの実行時間を状態時間として求める |
|---|
| 559 |
var result2:Array = SqlUtil.getData(_dbconn, "select sum(elapse) as value from threadTime where name =='" + name + "'"); |
|---|
| 560 |
if (result2[1].data != null) { |
|---|
| 561 |
value.addItem({name: stateName[0], value: 0, color: stateColor[0]}); // ダミーデータ |
|---|
| 562 |
value.addItem({name: stateName[currentState], value: result2[1].data[0].value, color: stateColor[currentState]}); |
|---|
| 563 |
} |
|---|
| 564 |
} |
|---|
| 565 |
} |
|---|
| 566 |
return value; |
|---|
| 567 |
} |
|---|
| 568 |
|
|---|
| 569 |
/* |
|---|
| 570 |
* スレッドの実行時間等の情報更新 |
|---|
| 571 |
*/ |
|---|
| 572 |
private function updateThreadTimes(name:String):void { |
|---|
| 573 |
// 該当スレッドの実行回数を取得 |
|---|
| 574 |
var result:Array = SqlUtil.getData(_dbconn, "select count(*) as issue from threadTime where name == '" + name + "'"); |
|---|
| 575 |
|
|---|
| 576 |
// もしデータがあれば |
|---|
| 577 |
if (result[1].data != null) { |
|---|
| 578 |
// スレッドの実行回数 |
|---|
| 579 |
tIssue.text = result[1].data[0].issue; |
|---|
| 580 |
|
|---|
| 581 |
// スレッドの総実行時間を取得 |
|---|
| 582 |
result = SqlUtil.getData(_dbconn, "select sum(elapse) as value from threadTime where name == '" + name + "'"); |
|---|
| 583 |
tElapse.text = result[1].data[0].value; |
|---|
| 584 |
|
|---|
| 585 |
// スレッドの平均実行時間を計算 |
|---|
| 586 |
tAveTime.text = "" + int(tElapse.text) / int(tIssue.text); |
|---|
| 587 |
|
|---|
| 588 |
// スレッドの最長実行時間を取得 |
|---|
| 589 |
result = SqlUtil.getData(_dbconn, "select max(elapse) as value from threadTime where name == '" + name + "'"); |
|---|
| 590 |
tMaxTime.text = result[1].data[0].value; |
|---|
| 591 |
|
|---|
| 592 |
// スレッドの最短実行時間を取得 |
|---|
| 593 |
result = SqlUtil.getData(_dbconn, "select min(elapse) as value from threadTime where name == '" + name + "'"); |
|---|
| 594 |
tMinTime.text = result[1].data[0].value; |
|---|
| 595 |
} else { |
|---|
| 596 |
// データ無い... |
|---|
| 597 |
tIssue.text = ""; |
|---|
| 598 |
tElapse.text = ""; |
|---|
| 599 |
tAveTime.text = ""; |
|---|
| 600 |
tMaxTime.text = ""; |
|---|
| 601 |
tMinTime.text = ""; |
|---|
| 602 |
} |
|---|
| 603 |
} |
|---|
| 604 |
|
|---|
| 605 |
/* |
|---|
| 606 |
* スレッドの実行時間のグラフ生成 |
|---|
| 607 |
*/ |
|---|
| 608 |
private var threadTimeHtml:File = null; |
|---|
| 609 |
private var threadTimeData:File = null; |
|---|
| 610 |
private function createThreadTimeChart():void { |
|---|
| 611 |
|
|---|
| 612 |
// スレッドの実行時間データを求める |
|---|
| 613 |
var result:ArrayCollection = getThreadElapseTime(); |
|---|
| 614 |
|
|---|
| 615 |
// スレッドの実行時間Chartの生成 |
|---|
| 616 |
var chartData:SingleChartData = new SingleChartData(); |
|---|
| 617 |
chartData.graphAttribute.decimalPrecision = "0"; |
|---|
| 618 |
chartData.graphAttribute.formatNumberScale = "0"; |
|---|
| 619 |
for each (var val:* in result) { |
|---|
| 620 |
chartData.addSetElement(val); |
|---|
| 621 |
} |
|---|
| 622 |
chartData.createHtmlFile(threadTimeHtml, "Bar2D", htmlTime.width, htmlTime.height, threadTimeData.nativePath); |
|---|
| 623 |
chartData.createDataFile(threadTimeData); |
|---|
| 624 |
htmlTime.location = "file:///" + threadTimeHtml.nativePath; |
|---|
| 625 |
} |
|---|
| 626 |
|
|---|
| 627 |
/* |
|---|
| 628 |
* スレッドの実行時間の計算 |
|---|
| 629 |
*/ |
|---|
| 630 |
private function getThreadElapseTime():ArrayCollection { |
|---|
| 631 |
|
|---|
| 632 |
// それぞれのスレッドの実行時間の合計を得る |
|---|
| 633 |
var result:Array = SqlUtil.getData(_dbconn, "select name, sum(elapse) as value from threadTime group by name order by value desc"); |
|---|
| 634 |
|
|---|
| 635 |
var value:ArrayCollection = new ArrayCollection(); // Chart用データ |
|---|
| 636 |
var cnt:int = 0; |
|---|
| 637 |
var etc_time:int = 0; // TOP 10以外のスレッドの実行時間の合計 |
|---|
| 638 |
|
|---|
| 639 |
for each (var val:* in result[1].data) { |
|---|
| 640 |
// まだTOP 10が求まってなければ |
|---|
| 641 |
if (cnt < 10) { |
|---|
| 642 |
value.addItem({name: val.name, value: val.value}); // 実行時間をChartデータに登録 |
|---|
| 643 |
} else { |
|---|
| 644 |
// TOP 10以外のスレッドの実行時間を足す |
|---|
| 645 |
etc_time += val.value; |
|---|
| 646 |
} |
|---|
| 647 |
cnt++; |
|---|
| 648 |
} |
|---|
| 649 |
|
|---|
| 650 |
// スレッドが10個以上あれば |
|---|
| 651 |
if (cnt >= 10) |
|---|
| 652 |
value.addItem({name: "etc.", value: etc_time}); // TOP 10以外のスレッドの実行時間をChartデータに登録 |
|---|
| 653 |
|
|---|
| 654 |
return value; |
|---|
| 655 |
} |
|---|
| 656 |
|
|---|
| 657 |
/* |
|---|
| 658 |
* フレーム同期間隔の確認/更新 |
|---|
| 659 |
*/ |
|---|
| 660 |
private var frameGet:Boolean = false; // フレーム同期を既に受け取ったか |
|---|
| 661 |
private var lastNopTime:int = 0; // 最後にnopが来た時間 |
|---|
| 662 |
private var maxInterval:int = 0; // フレーム間隔の最大値 |
|---|
| 663 |
private var maxIntPos:int = 0; // 最大フレーム間隔が発生した時間 |
|---|
| 664 |
private var minInterval:int = 10000; // フレーム間隔の最小値 デフォルト値:10秒 |
|---|
| 665 |
private var minIntPos:int = 0; // 最小フレーム間隔が発生した時間 |
|---|
| 666 |
private var totalInterval:int = 0; // 総フレーム間隔時間 |
|---|
| 667 |
private var intervalCount:int = 0; // フレーム同期回数 |
|---|
| 668 |
private function checkInterval(t:int, period:int):void { |
|---|
| 669 |
var intervalTime:int = 0; |
|---|
| 670 |
|
|---|
| 671 |
// もしフレーム同期を既に取得済みなら |
|---|
| 672 |
if (frameGet) { |
|---|
| 673 |
intervalTime = t - lastNopTime; // 同期間隔を求める |
|---|
| 674 |
} else { |
|---|
| 675 |
intervalTime = period; |
|---|
| 676 |
frameGet = true; |
|---|
| 677 |
} |
|---|
| 678 |
totalInterval += intervalTime; |
|---|
| 679 |
intervalCount++; |
|---|
| 680 |
|
|---|
| 681 |
// もし現在の最大値より大きかったら |
|---|
| 682 |
if (intervalTime > maxInterval) { |
|---|
| 683 |
maxInterval = intervalTime; |
|---|
| 684 |
maxIntPos = pos_x - intervalTime; |
|---|
| 685 |
tElapseMax.text = "Max " + convertElapseTime(maxInterval).substring(6); |
|---|
| 686 |
} |
|---|
| 687 |
|
|---|
| 688 |
// もし現在の最小値よりも小さかったら |
|---|
| 689 |
if (intervalTime < minInterval) { |
|---|
| 690 |
minInterval = intervalTime; |
|---|
| 691 |
minIntPos = pos_x - intervalTime; |
|---|
| 692 |
tElapseMin.text = "Min " + convertElapseTime(minInterval).substring(6); |
|---|
| 693 |
} |
|---|
| 694 |
|
|---|
| 695 |
var ave_time:int = totalInterval/intervalCount; // 平均フレーム間隔時間を求める |
|---|
| 696 |
tElapseAve.text = "Ave " + convertElapseTime(ave_time).substring(6); |
|---|
| 697 |
tFPS.text = "FPS: " + int(1000 / ave_time); // FPSを計算 |
|---|
| 698 |
lastNopTime = t; // 現在時間の更新 |
|---|
| 699 |
} |
|---|
| 700 |
|
|---|
| 701 |
/* |
|---|
| 702 |
* 時間(ms)を文字列 "HH:MM:SS.SSS" に変換 |
|---|
| 703 |
*/ |
|---|
| 704 |
private function convertElapseTime(time:int):String { |
|---|
| 705 |
var elapseTime:Date = new Date(time); |
|---|
| 706 |
var value:String = ""; |
|---|
| 707 |
if (elapseTime.hoursUTC < 10) |
|---|
| 708 |
value += "0"; |
|---|
| 709 |
value += (elapseTime.hoursUTC + ":"); |
|---|
| 710 |
if (elapseTime.minutesUTC < 10) |
|---|
| 711 |
value += "0"; |
|---|
| 712 |
value += (elapseTime.minutesUTC + ":"); |
|---|
| 713 |
if (elapseTime.secondsUTC < 10) |
|---|
| 714 |
value += "0"; |
|---|
| 715 |
value += (elapseTime.secondsUTC + "."); |
|---|
| 716 |
if (elapseTime.millisecondsUTC < 100) |
|---|
| 717 |
value += "0"; |
|---|
| 718 |
if (elapseTime.millisecondsUTC < 10) |
|---|
| 719 |
value += "0"; |
|---|
| 720 |
value += (elapseTime.millisecondsUTC + ""); |
|---|
| 721 |
return value; |
|---|
| 722 |
} |
|---|
| 723 |
|
|---|
| 724 |
/* |
|---|
| 725 |
* nopを生成 |
|---|
| 726 |
*/ |
|---|
| 727 |
private function createNop():void { |
|---|
| 728 |
threadList.addItem("nop"); // スレッドリストに"nop"を登録 |
|---|
| 729 |
var t:Label = new Label(); |
|---|
| 730 |
t.x = LEFT_PADD; t.y = 0; t.text = "nop"; |
|---|
| 731 |
threadName.addChild(t); |
|---|
| 732 |
} |
|---|
| 733 |
|
|---|
| 734 |
/* |
|---|
| 735 |
* Frame Per Second(FPS)情報の取得ハンドラ |
|---|
| 736 |
*/ |
|---|
| 737 |
public function frameInfoHandler(fps:uint):void { |
|---|
| 738 |
framePerSecond = fps; |
|---|
| 739 |
frameInterval = 1000/framePerSecond; |
|---|
| 740 |
} |
|---|
| 741 |
|
|---|