Ich bastel gerade an einer Möglichkeit einfach und bequem meine Scripte zu analysieren und die Performance einzelner Funktionen zu messen.
Ziele:
1. Ermittlung der langsamsten Funktionen
2. Ermittlung der am meist aufgerufenen Funktionen
3. Ermittlung der insgesamt zeitintensivsten Programmteile
4. Eventuell grafische und zeitliche Darstellung des Scriptablaufs (Sequenzdiagramm)
Die aktuelle Version erstellt ein "Trace" Script aus dem Quellscript, startet dieses und gibt die Ergebnisse der Messungen aus. Derzeit fehlt also noch eine Auswertung (sollte kein Problem sein) und grafisch ansprechende Darstellung (schon eher ein Problem).
Außerdem fehlt es mir an entsprechend umfangreichen Testscripten um die Funktionalität der aktuellen Fassung zu testen.
Und ja der Quellcode schaut derzeit noch etwas chaotisch aus, gerade was das einfügen bei "return" statements angeht musste ich lange rumspielen bis es mit meinem Testscript zuverlässig funktioniert hatte. Vermutlich wäre der Einsatz von regulären Ausdrücke deutlich einfacher gewesen, steht jedenfalls auf meiner Todo das nochmals etwas zu ändern.
Ich möchte euch daher erstmal darum bitten das Script ausgiebig mit umfangreicheren Scripten zu testen und mich auf Fehler aufmerksam zumachen (z.B. nicht ersetzte oder falsch ersetzte "return" Strings). Ich bin mir auch nicht sicher ob das Script funktioniert wenn im Original Script bereits ein "onautoitexitregister" verwendet wird und wie ich das in solch einem Fall evtl. besser lösen könnte ohne permanent in die Datei schreiben zu müssen. Theoretisch wäre auch ein überwachendes Script denkbar um erst garkeinen Umweg über eine Datei gehen zu müssen, allerdings möchte ich die Performance des Scriptes auch nicht zu sehr durch die Messungen und Datenübertragung der Messergebnisse beeinflussen.
Ideen, Vorschläge oder gar modifizierte/erweiterte Scriptversionen sind herzlich Willkommen.
EDIT: Netter Nebeneffekt ist im übrigen, dass man bei abstürzenden Scripten sehr gut im Logfile sieht welche Funktion zuletzt ausgeführt wurde, was die Fehlersuche bei kompilierten Scripten evtl. ein wenig vereinfachen kann.
v0.3.1
#include <Constants.au3>
#include <array.au3>
#include <file.au3>
Global $sourceFile = FileOpenDialog("Script auswählen...",@ScriptDir,"Autoit (*.au3)",3)
;Global $sourceFile = @ScriptDir & "\test.au3"
Global $targetFile = StringReplace($sourceFile,".au3","_trace.au3",-1)
Global $traceLogFile = StringReplace($sourceFile,".au3","_trace.log",-1)
createTraceScript($sourceFile)
runTraceScript($targetFile)
showTraceResult($traceLogFile)
Func showTraceResult($sPath)
Local $aData
_FileReadToArray($sPath,$aData)
Local $aReturn[UBound($aData)][4]=[["Name","FuncStart","FuncEnd","FuncRunTime"]]
Local $aSplit
For $i = 1 To UBound($aData)-1
$aSplit = StringSplit($aData[$i],";")
For $j = 1 To UBound($aSplit)-1
$aReturn[$i][$j-1]=$aSplit[$j]
Next
Next
_ArrayDisplay($aReturn)
EndFunc
Func createTraceScript($sourceFile)
Local $aScript
_FileReadToArray($sourceFile,$aScript)
Local $aFunctionNames = getFunctionNames($aScript)
Local $aReturn[UBound($aScript)+(2*(UBound($aFunctionNames)))]=[$aScript[0]]
Local $sDebugVarName = getDebugVarName($aScript)
Local $sDebugFunctionName = getDebugFunctionName($aScript)
Local $sDebugFunctionShutdownName = getDebugFunctionName($aScript,"__MyDebugTraceFunctionShutdown")
Local $aTemp, $sTemp, $iPos
Local $k = 1
Local $j = 0
$aReturn[$j] = '; MyDebugTrace by MisterSpeed' & @CRLF & _
'Global ' & $sDebugVarName & '[1][4]=[["Name","FuncStart","FuncEnd","FuncRunTime"]]' & @CRLF & _
'OnAutoItExitRegister("' & $sDebugFunctionShutdownName & '")' & @CRLF & _
'Func ' & $sDebugFunctionName & '($sName,$iState,$dReturn="")' & @CRLF & _
@TAB & 'Local $time' & @CRLF & _
@TAB & 'If $iState = 1 Then' & @CRLF & _
@TAB & @TAB & '$time = TimerInit()' & @CRLF & _
@TAB & @TAB & 'Redim ' & $sDebugVarName & '[ubound(' & $sDebugVarName & ')+1][4]' & @CRLF & _
@TAB & @TAB & $sDebugVarName & '[UBound(' & $sDebugVarName & ')-1][0] = $sName' & @CRLF & _
@TAB & @TAB & $sDebugVarName & '[UBound(' & $sDebugVarName & ')-1][1] = $time' & @CRLF & _
@TAB & 'Else' & @CRLF & _
@TAB & @TAB & '$time = TimerInit()' & @CRLF & _
@TAB & @TAB & 'For $i = UBound(' & $sDebugVarName & ')-1 To 1 Step -1' & @CRLF & _
@TAB & @TAB & @TAB & 'If ' & $sDebugVarName & '[$i][0] = $sName And ' & $sDebugVarName & '[$i][3] = "" Then ExitLoop' & @CRLF & _
@TAB & @TAB & 'Next' & @CRLF & _
@TAB & @TAB & $sDebugVarName & '[$i][2] = $time' & @CRLF & _
@TAB & @TAB & $sDebugVarName & '[$i][3] = TimerDiff(' & $sDebugVarName & '[$i][1])' & @CRLF & _
@TAB & 'Endif' & @CRLF & _
@TAB & 'Return $dReturn' & @CRLF & _
'EndFunc' & @CRLF & @CRLF & _
'Func ' & $sDebugFunctionShutdownName & '()' & @CRLF & _
@TAB & 'Local $sTemp' & @CRLF & _
@TAB & 'For $i = 1 To UBound(' & $sDebugVarName & ')-1' & @CRLF & _
@TAB & @TAB & '$sTemp &= ' & $sDebugVarName & '[$i][0] & ";" & ' & $sDebugVarName & '[$i][1] & ";" & ' & $sDebugVarName & '[$i][2] & ";" & ' & $sDebugVarName & '[$i][3] & @CRLF' & @CRLF & _
@TAB & 'Next' & @CRLF & _
@TAB & 'Local $hFile = FileOpen("' & $traceLogFile & '",2)' & @CRLF & _
@TAB & 'FileWrite($hFile,$sTemp)' & @CRLF & _
@TAB & 'FileClose($hFile)' & @CRLF & _
'EndFunc' & @CRLF
$j+=1
For $i = 1 to UBound($aScript)-1
If $aFunctionNames[$k][1] = $i Then
$aReturn[$j]=$aScript[$i]
$j+=1
$aReturn[$j]=$sDebugFunctionName & '("' & $aFunctionNames[$k][0] & '",1)'
ConsoleWrite("New function starts!" & @CRLF & $aScript[$i] & @CRLF & $aReturn[$j] & @CRLF & "--------------" & @CRLF)
Else
If StringRight($aScript[$i],7) = " Return" Or StringRight($aScript[$i],7) = " return" Then
$aReturn[$j]=StringTrimRight($aScript[$i],6) & 'Return ' & $sDebugFunctionName & '("' & $aFunctionNames[$k][0] & '",0)'
ConsoleWrite("Return at end of line!" & @CRLF & $aScript[$i] & @CRLF & $aReturn[$j] & @CRLF & "--------------" & @CRLF)
$j+=1
ContinueLoop
EndIf
$sTemp = StringReplace($aScript[$i]," ","")
$sTemp = StringReplace($sTemp,@TAB,"")
$aTemp = StringSplit($sTemp,";")
If $aTemp[1] = "return" Or $aTemp[1] = "Return" Then
$sTemp = StringReplace($aScript[$i],"return","Return")
$aTemp = StringSplit($sTemp,"Return",1)
If $aTemp[0] > 1 Then
$aReturn[$j] = $aTemp[1] & 'Return ' & $sDebugFunctionName & '("' & $aFunctionNames[$k][0] & '",0)'
Else
$aReturn[$j] = 'Return ' & $sDebugFunctionName & '("' & $aFunctionNames[$k][0] & '",0)'
EndIf
$aTemp = StringSplit($aScript[$i],";")
If $aTemp[0] > 1 Then
For $x = 2 To $aTemp[0]
$aReturn[$j] &= ";" & $aTemp[$x]
Next
EndIf
ConsoleWrite("Single Return line!" & @CRLF & $aScript[$i] & @CRLF & $aReturn[$j] & @CRLF & "--------------" & @CRLF)
$j+=1
ContinueLoop
EndIf
$iPos = StringInStr($aScript[$i],"return ")
If $iPos Then ; return Zeile
If $iPos = 1 Then
$sTemp = StringReplace($aScript[$i],"return ","",1)
$aTemp = StringSplit($sTemp,";")
$sTemp = $aTemp[1]
$aReturn[$j] = 'Return ' & $sDebugFunctionName & '("' & $aFunctionNames[$k][0] & '",0,' & $sTemp & ')'
If $aTemp[0] > 1 Then
$sTemp = ""
For $x = 2 to $aTemp[0]
$sTemp &= $aTemp[$x]
Next
$aReturn[$j] &= $sTemp
EndIf
ConsoleWrite("Return Line starting charpos 1!" & @CRLF & $aScript[$i] & @CRLF & $aReturn[$j] & @CRLF & "--------------" & @CRLF)
Else
If StringIsAlpha(StringMid($aScript[$i],$iPos-1,1)) Then
$aReturn[$j]=$aScript[$i]
ConsoleWrite("String is alpha!" & @CRLF & $aScript[$i] & @CRLF & $aReturn[$j] & @CRLF & "--------------"& @CRLF)
Else
$sTemp = StringReplace($aScript[$i],"return ","Return",1)
$aTemp = StringSplit($sTemp,"Return",1)
$aReturn[$j] = $aTemp[1]
$sTemp = ""
For $x=2 To $aTemp[0]
$sTemp &= $aTemp[$x]
Next
;StringReplace($aScript[$i],"Return ","",1)
$aTemp = StringSplit($sTemp,";")
$sTemp = $aTemp[1]
$aReturn[$j] &= 'Return ' & $sDebugFunctionName & '("' & $aFunctionNames[$k][0] & '",0,' & $sTemp & ')'
If $aTemp[0] > 1 Then
$sTemp = ""
For $x = 2 to $aTemp[0]
$sTemp &= ';' & $aTemp[$x]
Next
$aReturn[$j] &= $sTemp
EndIf
ConsoleWrite("Return Line starting charpos x!" & @CRLF & $aScript[$i] & @CRLF & $aReturn[$j] & @CRLF & "--------------" & @CRLF)
EndIf
EndIf
Else
If $aFunctionNames[$k][2] = $i Then
$aReturn[$j]=$sDebugFunctionName & '("' & $aFunctionNames[$k][0] & '",0)'
ConsoleWrite("New function ends!" & @CRLF & $aReturn[$j] & @CRLF & $aScript[$i] & @CRLF & "--------------" & @CRLF)
$j+=1
$k+=1
$aReturn[$j]=$aScript[$i]
Else
$aReturn[$j]=$aScript[$i]
EndIf
EndIf
EndIf
$j+=1
Next
_FileWriteFromArray($targetFile,$aReturn,0)
Return $aReturn
EndFunc
Func runTraceScript($sPath)
ConsoleWrite(@CRLF & @CRLF & "Running Script now!" & @CRLF & "--------------" & @CRLF)
Local $sLine = ""
Local $sTemp = ""
Local $PID = Run('"' & @AutoItExe & '" /ErrorStdOut "' & $targetFile & '"',"",@SW_SHOW,$STDOUT_CHILD)
While 1
$sTemp = StdoutRead($PID)
If @error Then ExitLoop
If $sTemp <> "" Then $sLine &= $sTemp & @CRLF
WEnd
ConsoleWrite($sLine)
EndFunc
Func getFunctionNames(ByRef $aData)
Local $aTemp
Local $sTemp
Local $aReturn[UBound($aData)][3] = [["Name","FuncStartLine","FuncEndLine"]]
Local $k = 1
For $i = 1 To UBound($aData)-1
If StringInStr($aData[$i],"Func ") And Not StringInStr($aData[$i],"EndFunc") Then
$aData[$i]=StringReplace($aData[$i],"func ","Func ",1,1)
$aTemp = StringSplit($aData[$i],"Func ",1)
$aTemp = StringSplit($aTemp[$aTemp[0]],"(")
$sTemp = StringReplace($aTemp[1]," ","")
$sTemp = StringReplace($aTemp[1],@TAB,"")
$aReturn[$k][0] = $sTemp & "(...)"
$aReturn[$k][1] = $i
For $j = $i+1 To UBound($aData)-1
If StringInStr($aData[$j],"EndFunc") Then
$aReturn[$k][2] = $j
$i = $j
ExitLoop
EndIf
Next
$k+=1
EndIf
Next
ReDim $aReturn[$k][3]
Return $aReturn
EndFunc
Func getDebugVarName(ByRef $aData, $sCheck="$aMyDebugTrace")
Local $sReturn = $sCheck
For $i = 1 To UBound($aData)-1
If StringInStr($aData[$i],$sReturn) Then
$sReturn = getDebugVarName($aData,$sCheck & TimerInit()) ; check again with random var name
ExitLoop
EndIf
Next
Return $sReturn
EndFunc
Func getDebugFunctionName(ByRef $aData, $sCheck="__MyDebugTraceFunction")
Local $sReturn = $sCheck
For $i = 1 To UBound($aData)-1
If StringInStr($aData[$i],$sReturn) Then
$sReturn = getDebugFunctionName($aData,$sCheck & TimerInit()) ; check again with random function name
ExitLoop
EndIf
Next
Return $sReturn
EndFunc