GDBConnection.st
changeset 22 57025871aed4
parent 19 c48d33e27d34
child 23 a7eb888c81b5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GDBConnection.st	Fri Jun 20 21:25:03 2014 +0100
@@ -0,0 +1,311 @@
+"{ Package: 'jv:libgdbs' }"
+
+Object subclass:#GDBConnection
+	instanceVariableNames:'pid debuggerInput debuggerOutput inferiorPTY eventAnnouncer
+		eventAnnouncerInternal eventQueue eventQueueLock
+		eventQueueNotifier eventDispatchProcess eventPumpProcess
+		outstandingCommands'
+	classVariableNames:''
+	poolDictionaries:'GDBDebugFlags'
+	category:'GDB-Private'
+!
+
+
+!GDBConnection class methodsFor:'instance creation'!
+
+pid:pidArg input:inputArg output:outputArg
+    ^ self new 
+        initializeWithPid:pidArg
+        input:inputArg
+        output:outputArg
+
+    "Created: / 09-06-2014 / 18:20:37 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+! !
+
+!GDBConnection methodsFor:'accessing'!
+
+eventAnnouncer
+    ^ eventAnnouncer
+!
+
+eventAnnouncerInternal
+    ^ eventAnnouncerInternal
+
+    "Created: / 19-06-2014 / 22:18:02 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+inferiorPTY
+    ^ inferiorPTY
+! !
+
+!GDBConnection methodsFor:'event dispatching'!
+
+eventDispatchLoop
+    "raise an error: this method should be implemented (TODO)"
+
+    [  
+        | eventQueueEmpty |
+
+        eventQueueEmpty := false.
+        [ eventQueueEmpty ] whileFalse:[
+            | event |
+
+            event := nil.
+            eventQueueLock critical:[ 
+                eventQueueEmpty := eventQueue isEmpty.
+                eventQueueEmpty ifFalse:[ 
+                    event := eventQueue removeFirst.
+                ]
+            ].
+            eventQueueEmpty ifFalse:[
+                [
+                    self eventDispatchSingle: event.
+                ] on: Error do:[:ex | 
+                    "/ Pass
+                ].
+            ].
+        ].
+        pid isNil ifTrue:[ ^ self ]. "/ gdb process terninated
+        eventQueueNotifier wait.
+    ] loop.
+
+    "Created: / 02-06-2014 / 22:51:48 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified (comment): / 04-06-2014 / 09:16:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+eventDispatchSingle: aGDBEvent
+    TraceEvents ifTrue:[ 
+        Logger log: ('event loop: broadcasting %1 (%2)' bindWith: aGDBEvent class name with: aGDBEvent token) severity: #trace facility: 'GDB'
+    ].
+    eventAnnouncerInternal announce: aGDBEvent.
+    eventAnnouncer announce: aGDBEvent
+
+    "Created: / 02-06-2014 / 22:58:20 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 19-06-2014 / 22:18:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+eventDispatchStart
+    eventDispatchProcess isNil ifTrue:[
+        eventDispatchProcess := [
+                TraceEvents ifTrue:[
+                    Logger log: 'event loop: starting' severity: #trace facility: 'GDB'
+                ].
+                self eventDispatchLoop.
+            ] newProcess.
+        eventDispatchProcess name:('GDB Event dispatcher (%1)' bindWith:pid).
+        eventDispatchProcess priority:Processor userBackgroundPriority.
+        eventDispatchProcess addExitAction:[ 
+            eventDispatchProcess := nil. 
+            TraceEvents ifTrue:[
+                Logger log: 'event loop: terminated' severity: #trace facility: 'GDB'
+            ].
+        ].
+        eventDispatchProcess resume.
+    ].
+
+    "Created: / 02-06-2014 / 22:51:48 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 04-06-2014 / 09:27:04 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+eventDispatchStop
+    | t |
+
+    t := eventDispatchProcess.
+    (t notNil and:[ t isDead not]) ifTrue:[ 
+        eventDispatchProcess := nil.
+        t terminate.
+         "/ raise its prio to make it terminate quickly
+        t priority:(Processor userSchedulingPriority + 1)                       
+    ].
+
+    "Created: / 02-06-2014 / 22:52:51 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+! !
+
+!GDBConnection methodsFor:'event handling'!
+
+onCommand: aGDBCommandEvent
+    | command |
+
+    command := aGDBCommandEvent command.
+    command token notNil ifTrue:[ 
+        debuggerInput nextPutAll: command token printString.
+    ].
+    outstandingCommands add: command.
+    debuggerInput nextPutLine: command asString.
+
+    "Created: / 02-06-2014 / 23:38:59 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 19-06-2014 / 22:08:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+! !
+
+!GDBConnection methodsFor:'event pump'!
+
+eventPumpLoop
+    | parser |
+
+    parser := GDBParser on: debuggerOutput.
+    parser token2CommandMappingBlock:[ :token | 
+        | command |
+
+        command :=  outstandingCommands detect:[:cmd | cmd token == token ] ifNone:[nil].
+        command notNil ifTrue:[ outstandingCommands remove: command ].
+        command
+    ].
+    [ debuggerOutput atEnd ] whileFalse:[ 
+        | eventset |
+
+        [
+            [ 
+                eventset := parser parseOutput.
+            ] on: StreamNotOpenError do:[
+                ^ self.
+            ].
+            self pushEventSet: eventset.
+        ] on: AbortOperationRequest do:[
+            | terminator i c |
+
+            terminator := '(gdb)'.
+            i := 1.
+            debuggerOutput notNil ifTrue:[
+                [ debuggerOutput atEnd not and: [i <= terminator size ] ] whileTrue:[ 
+                    c := debuggerOutput next.
+                    c == (terminator at: i) ifTrue:[ 
+                        i := i + 1.
+                    ] ifFalse:[ 
+                        i := 1.
+                    ].
+                ].
+                debuggerOutput next. "/ read nl.
+            ] ifFalse:[ 
+                ^ self.
+            ].
+        ]
+    ]
+
+    "Created: / 02-06-2014 / 22:38:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 19-06-2014 / 22:10:24 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+eventPumpStart
+    eventPumpProcess isNil ifTrue:[
+        eventPumpProcess := [
+                TraceEvents ifTrue:[
+                    Logger log: 'event pump: starting' severity: #trace facility: 'GDB'
+                ].
+                self eventPumpLoop
+            ] newProcess.
+        eventPumpProcess name:('GDB Event pump (%1)' bindWith:pid).
+        eventPumpProcess priority:Processor userBackgroundPriority.
+        eventPumpProcess addExitAction:[ 
+            TraceEvents ifTrue:[
+                Logger log: 'event pump: terminated' severity: #trace facility: 'GDB'
+            ].
+            eventPumpProcess := nil. 
+        ].
+        eventPumpProcess resume.
+    ].
+
+    "Created: / 02-06-2014 / 22:38:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 04-06-2014 / 09:27:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+eventPumpStop
+    | t |
+
+    t := eventPumpProcess.
+    (t notNil and:[ t isDead not]) ifTrue:[ 
+        eventPumpProcess := nil.
+        t terminate.
+         "/ raise its prio to make it terminate quickly
+        t priority:(Processor userSchedulingPriority + 1)                       
+    ].
+
+    "Created: / 02-06-2014 / 22:40:38 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+! !
+
+!GDBConnection methodsFor:'events'!
+
+pushEvent: aGDBEvent
+    eventQueueLock critical:[
+        eventQueue add: aGDBEvent.
+        eventQueueNotifier signalForAll.
+    ].
+
+    "Created: / 02-06-2014 / 22:49:49 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+pushEventSet: aGDBEventSet
+    eventQueueLock critical:[
+        eventQueue addAll: aGDBEventSet.
+        eventQueueNotifier signalForAll.
+    ].
+
+    "Created: / 02-06-2014 / 22:42:54 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+! !
+
+!GDBConnection methodsFor:'initialize & release'!
+
+initializeWithPid:pidArg input:inputArg output:outputArg
+    pid := pidArg.
+    debuggerInput := inputArg.
+    debuggerOutput := outputArg.
+    inferiorPTY := GDBPTY new.
+    eventQueue := OrderedCollection new.
+    eventQueueLock := RecursionLock new.
+    eventQueueNotifier := Semaphore new.
+    eventAnnouncer := Announcer new.
+    eventAnnouncerInternal := Announcer new.    
+    outstandingCommands := Set new.
+    eventAnnouncerInternal 
+        when:GDBCommandEvent
+        send:#onCommand:
+        to:self.
+
+    "Created: / 09-06-2014 / 18:21:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 19-06-2014 / 22:18:10 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+release
+    pid notNil ifTrue:[
+        OperatingSystem sendSignal:(OperatingSystem sigKILL) to:pid.       
+    ]
+
+    "Created: / 26-05-2014 / 21:30:30 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 03-06-2014 / 00:55:22 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+released: status
+    TraceProcesses ifTrue:[ 
+        Logger log: ('gdb process: terminated with status %1' bindWith: status code)  severity: #trace facility: 'GDB'.
+    ].
+    TraceProcesses ifTrue:[ 
+        Logger log: ('gdb process: waiting for event pump to finish' bindWith: status code)  severity: #trace facility: 'GDB'.
+    ].
+    [ eventPumpProcess notNil ] whileTrue:[ 
+        Delay waitForMilliseconds: 200.  
+    ].
+    TraceProcesses ifTrue:[ 
+        Logger log: ('gdb process: event pump finished' bindWith: status code)  severity: #trace facility: 'GDB'.
+    ].
+    pid := nil.       
+    eventQueueNotifier signalForAll.           
+    debuggerInput notNil ifTrue:[ 
+        debuggerInput close.
+        debuggerInput := nil.
+    ].
+    debuggerOutput notNil ifTrue:[ 
+        debuggerOutput close.
+        debuggerOutput := nil.
+    ].
+    inferiorPTY release.
+
+    "Created: / 26-05-2014 / 21:31:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 09-06-2014 / 18:26:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+! !
+
+!GDBConnection class methodsFor:'documentation'!
+
+version_HG
+
+    ^ '$Changeset: <not expanded> $'
+! !
+