Moi!
Teen "virtuaalikonetta", joka tulkkaa binäärimuotoista "virtuaalikiintolevyä". Siinä on aika tehokas virheiden käsittely, mutta siinä on parantamisen varaa. Kun virhe jää kiinni Try-Catchissa, se hyppää virheenkäsittelijään joka tekee crashdumpin. Crashdumppiin tallentuu virtuaalikoneen muisti ja prosessorin tila. Muistin viimeisessä alkiossa on virheen tiedot sekä StackTrace. Siihen pitäisi saada myös virheen syy.
Miten tämä onnistuu vai onnistuuko millään?
Määrittele "virheen syy". Useinhan jos tietokoneessa ohjelma kaatuu, niin virheen syy on huonossa ohjelmoinnissa. Tämän tai muun varsinaisen syyn saaminen selville ei yleensä ole koneellisesti mahdollista. Eli minkälaista vastausta haetaan kysymykseen "mikä oli virheen syy"?
argumentexception?
Käyt ensin läpi mahdolliset syyt, ja määrität virheelle oman tekstisi.
Grez kirjoitti:
Määrittele "virheen syy".
Ainakin tuliko virhe virtuaalisessa tominnassa vai ihan oikeassa toiminnassa (eli vaikka koodissa bugi). Täytyypä määritellä oma exception-luokka...
groovyb kirjoitti:
Käyt ensin läpi mahdolliset syyt, ja määrität virheelle oman tekstisi.
Niin kai.
Nyt tein oman exceptionin VCError. Tällä hetkellä crashdumppi näyttää tätä:
VCdmp@mem2790# ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||¿1±black;¿2±gray| VCErr: Number 0 Description General error: Object reference not set to an instance of an object. Infodata Object reference not set to an instance of an object.<fmt>System.NullReferenceException: Object reference not set to an instance of an object. at VComputer.VComputer.Screen.keydown(Object sender, KeyEventArgs e) in C:\Users\Pauli\Documents\Visual Studio 2010\Projects\VB\VC\VC\VComputer.vb:line 581 at System.Windows.Forms.Control.OnKeyDown(KeyEventArgs e) at System.Windows.Forms.Control.ProcessKeyEventArgs(Message& m) at System.Windows.Forms.Control.ProcessKeyMessage(Message& m) at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.ScrollableControl.WndProc(Message& m) at System.Windows.Forms.Form.WndProc(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam) at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg) at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData) at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context) at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context) at System.Windows.Forms.Application.DoEvents() at VComputer.VComputer.Screen.ScrBIOS.Controller.Update() in C:\Users\Pauli\Documents\Visual Studio 2010\Projects\VB\VC\VC\VComputer.vb:line 631 @reg#0|0|0||
Eli pseudona:
dumppiheaderi, muistimerkintä, dumpin muistiosuuden koko ja muistin sisältöä (viimeiset ekalla rivillä on näyttömuistista eli näytönkontrollointikäskyt)
vika muistialue on errori:
numero 0
selitys virheelle
infodata eli additionaaliset tiedot
callstackki
prosessoritietojen headeri ja prossun rekisterit
Call Stackista näkee mitä kone on tehnyt. Se oli käynnistetty uudestaan ja sen jälkeen kaikki oli tyhjätty. Kun näyttöä päivitetään (viimeinen rivi callstackissa), kutsuttiin Application.DoEvents() -metodia ja se suoritti monimutkaisen toimintasarjan .NET-Frameworkissä. Lopulta päädyttiin näppäimistökäsittelijään, ja siellä yritettiin napsia keycode taullukkoon. Koska koneen uudelleenkäynnistyksessä kaikkiin muuttujiin on sijoitettu nothing, tulee exception.
Eli teitkö näin?
Try //jotain Catch Ex As Exception Dim sError = "VCErr: \r\nNumber 0 \r\nDescription General error: " + ex.ToString() Throw New ArgumentException(sError) End Try
Nyt kun lähdetään loggeria tekemään (tai mitä tahansa virheenkäsittelijää/tallentajaa), Olisi aika näppärää saada tietoon mikä metodi/funktio virheen laukaisee (jotta saadaan tietoon missä vaiheessa itse virhe tapahtuu, jotta ei tarvitse sourcea tutkia joka kerta)
Esim tuo yllä oleva esimerkkisi ei kerro mikä virheen triggaa, joten virheen selvitys olisi aika hankalaa.
Catch ex as System.NullReferenceException //Logger.Log(MyErrorCodes Errorcode, DateTime ErrorTime, string TriggeredBy, string SourceClass, string CallStack) MyLogger.Log(ErrorCodeEnum.NullReference,DateTime.Now().ToString(),"MyGroovybClass","MyFunction()",ex.ToString())
Log.txt, eventlog, tekstiä ruudulla yms. mihin sitten logger tai virheenkäsittelijä kirjoittaakaan:
VcError: Code 0
Explanation: Null reference Exception Occurred
Source: MyFunction()
Trigged by: MyGroovybClass
TimeStamp: 13.14.2012 22:08
CallStack: Lorem Ipsum
Tästä seuraava porras on lokitustyyppien määrittäminen (esim Information,Warning, Error) ja lokitusasteiden käyttäminen ( Basic | Virheet tallennetaan, Advanced | Virheet ja varoitukset tallennetaan , All | Virheet, varoitukset ja metodi/funktiokutsut tallennetaan ), jotka otetaan käyttöön vaikka sovellusargumentteina.
MyProgram.exe -EnableLog All
13.4.2012 22:08 Information: Program started
13.4.2012 22:08 Information: Checking program settings
13.4.2012 22:08 Information: Entering CheckSettings()
13.4.2012 22:08 Warning: Resolution not set in Settings.ini
13.4.2012 22:08 Error:
VcError: Code 0
Explanation: Null reference Exception Occurred
Source: CheckSettings()
Trigged by: MySettings.Resolution
TimeStamp: 13.14.2012 22:08
CallStack: Lorem Ipsum
13.4.2012 22:08 Information: Exiting CheckSettings()
13.4.2012 22:08 Information: Exiting Program
MyProgram.exe -EnableLog Advanced
13.4.2012 22:08 Warning: Resolution not set in Settings.ini
13.4.2012 22:08 Error:
VcError: Code 0
Explanation: Null reference Exception Occurred
Source: CheckSettings()
Trigged by: MySettings.Resolution
TimeStamp: 13.14.2012 22:08
CallStack: Lorem Ipsum
MyProgram.exe -EnableLog Basic
13.4.2012 22:08 Error:
VcError: Code 0
Explanation: Null reference Exception Occurred
Source: CheckSettings()
Trigged by: MySettings.Resolution
TimeStamp: 13.14.2012 22:08
CallStack: Lorem Ipsum
En aivan noin. Mutta tässä on jotakin koodia:
Private Shared Sub POST() 'biosin post-test joka käynnistetään omaan säikeeseensä
On Error GoTo errHandler 'virheenkäsittely tällä kertaa näin
'tässä välissä on koodi joka lataa laitteisiin oikeat tiedot filuista
Memory.PutObject(1024, "POST") : If Not Memory.GetObject(1024) = "POST" Then Console.Beep(750, 250) : ErrorHandler(New VCError(1, "1<fmt>")) 'muistitesti
Memory.PutObject(0, "init") 'muistiin prosessorin init-käsky joka onkin ainut ei-binäärikäsky
Processor.Run(0) : If Processor.Initialized = False Then Console.Beep(750, 250) : Console.Beep(750, 250) : ErrorHandler(New VCError(2, "1<fmt>")) 'käsketään prosessorin suorittaa init-käsky
If DiskCount = 0 Then Console.Beep(750, 2500) : Console.Beep(750, 250) : Console.Beep(750, 250) : ErrorHandler(New VCError(3, "1<fmt>")) 'jos kiintolevyjä 0, tulee virhe
Memory.PutObject(1, Nothing) 'vlearataan muisti jossa post-testin delegaatti oli
Data.Booting = True 'bootataan
Do : Thread.Sleep(2500) : Loop Until Data.Booting = False 'odotetaan muita säikeitä (kello tms.)
Memory.PutObject(1024, "¿1±black;¿2±gray") '1024 on näyttömuisti
If Not Data.bdFound Then Memory.PutObject(0, " ") 'jos bootdiskiä ei löydy, haltataan kone
Processor.Run(0) 'suoritetaan boottisektori/halt-käsky
Exit Sub 'poistutaan
errHandler: 'jos sattuu jokin muu virhe
ErrorHandler(New VCError(0, "'" & Err().Description & "' with code " & Err().Number & "<fmt>" & Err().Description)) 'lähetetään se errorHandleriin
End SubToinen esimerkki:
Public Shared Sub Update() 'Screen.ScrBIOS.Controller.Update()
Try
g = GetGraphics() 'haetaan graphicsit
If Not Processor.Initialized Then 'jos on haltattu
_frm.BackgroundImageLayout = ImageLayout.Tile
_frm.BackgroundImage = My.Resources.vclogo 'logo näyttöön
End If
Application.DoEvents()
If TypeOf Memory.GetObject(1024) Is Bitmap Then GoTo begindraw
If Not cursor Then 'kursori
g.DrawString("_", New Font("Courier", 36, FontStyle.Bold), New SolidBrush(clsClr), New Point(scrPos.X + 5, scrPos.Y + 5))
End If
begindraw: 'piirto
If TypeOf Memory.GetObject(1024) Is Bitmap Then 'bitmappi
g.DrawImage(CType(Memory.GetObject(1024), Bitmap), New Point(0, 0)) 'piirretään
_frm.Refresh() 'kaksi vähän turhaa kutstua
_frm.Update()
Else
If dt Then g.Clear(clsClr) : scrPos = New Point(5, 5)
Dim lines As String = Memory.GetObject(1024).ToString()
For Each cmd As String In Split(lines, ";") 'käskyt
If cmd = "¿0±" Then scrPos = New Point(5, scrPos.Y + 25) : Continue For 'uusi rivi
Select Case Left(cmd, 3)
Case "¿1±" 'cls
scrPos = New Point(5, 5)
clsClr = Color.FromName(Right(cmd, cmd.Length - 3))
g.Clear(clsClr) 'clear screen
Case "¿2±" 'clr
clr = Color.FromName(Right(cmd, cmd.Length - 3)) 'värin vaihto
Case "¿3±" 'print
g.DrawString(Right(cmd, cmd.Length - 3), New Font("Courier", 24), New SolidBrush(clr), scrPos) 'printataan
scrPos = New Point(scrPos.X + cmd.Length * 12 + 12, scrPos.Y)
Case "¿4±" 'dt
dt = Not dt 'joku ihme-drawto-käsky
Case Else
BIOS.ErrorHandler(New VCError(9, cmd & "<fmt>" & cmd)) 'virhe
End Select
Next
End If
If TypeOf Memory.GetObject(1024) Is Bitmap Then GoTo enddraw
If cursor Then
g.DrawString("_", New Font("Courier", 36, FontStyle.Bold), New SolidBrush(clr), New Point(scrPos.X + 3, scrPos.Y)) 'kursori
End If
enddraw:
cursor = Not cursor
_frm.Refresh() 'päivitys
Catch ex As Exception 'virhe
BIOS.ErrorHandler(New VCError(0, ex.Message & "<fmt>" & ex.ToString())) 'virheenkäsittely
End Try
End SubErrorhandleri:
Public Shared Sub ErrorHandler(VCerr As VCError) 'täällä on ehkä liikaa try-catcheja...
Try
Memory.PutObject(1025, " ") 'halt varmuuden vuoksi, ettei crashdumpin teko hidastu
Processor.Run(1025)
Catch
End Try
Try : Memory.PutObject(1025, VCerr.Message) : Catch : End Try 'errorin viesti
Try
Thread.Sleep(250)
Data.RunClock = False 'määritetään kello pois päältä
Catch
Finally
Try
Screen.GetForm().BackgroundImage = My.Resources.error_image 'errori-image
Screen.GetForm().BackgroundImageLayout = ImageLayout.Stretch 'venytetty kuva
Catch : End Try
Try
CreateCrashdump(Application.StartupPath & "\crashdump.mem") 'crashdumppi
Process.Start(Application.ExecutablePath, "report") 'virheraportti
ExitProgram = True 'poistutaan (pääsäie hoitaa homman)
Thread.CurrentThread.Suspend() 'pysähdytään
Catch : End Try
End TryVCError -luokka:
Public Class VCError
Inherits Exception 'exceptioni
'virheet on listattu järjestyksessä:
Public ReadOnly ERRs() As String = {"General error: {0}", "Step {0}: POSTErr - MemoryTest", "Step {0}: POSTErr - ProcessorTest", _
"Step {0}: POSTErr - DiskTest", "Step {0}: POSTErr - DiskRWTest", "Step {0}: POSTErr - ScreenTest", _
"ProcessorErr: '{0}' is not valid for the calculate command", "ProcessorErr: {0} is not a valid command", "ScreenErr: {0} is not a valid command"}
Dim _num As Integer, _id As String 'numero ja infodata ("id")
Protected msg As String 'viesti on suojattu
Public Overloads ReadOnly Property Message As String
Get
Return msg
End Get
End Property
Public Sub New(num As Integer, infodata As String) 'konstruktori
MyBase.New()
msg = "\n\nVCErr:\nNumber " & num & "\nDescription " & String.Format(ERRs(num), infodata.Split("<fmt>")(0) & "\nInfodata " & infodata & "\n" & StackTrace) 'muodostetaan viesti
msg = Replace(msg, "\n", Global.Microsoft.VisualBasic.vbCrLf) 'korvataan \n:t rivinvaihdoilla
_num = num
_id = infodata
End Sub
End ClassEn kaikkea koodia tietenkään laita (niitähän on yli 700 riviä), toivottavasti tuosta ymmärtää oleellisimman :)
Löysin yhdestä kirjasta, että virheiden käsittelyn voi tehdä delegaatin ja AppDomain.CurrentDomain.ErrorHandler tai jonkun muun vastaavan avulla.
Aihe on jo aika vanha, joten et voi enää vastata siihen.