Add `GDBCLICommand >> #operation` returning (expanded) CLI command
authorJan Vrany <jan.vrany@fit.cvut.cz>
Mon, 28 Jan 2019 22:47:57 +0000
changeset 174 18ef81a3fee5
parent 173 02546d4fbe6d
child 175 a04e1a36e888
Add `GDBCLICommand >> #operation` returning (expanded) CLI command ...just like `GDBMICommand >> #operation` returns MI command. This can be used to test whether user typed in specific CLI command. However, this method uses static table and this may not be up-to-date with particular GDB and it is ignorant of any user-defined CLI commands.
GDBCLICommand.st
GDBDebugger.st
GDBMIPrinter.st
GDBThreadGroup.st
tests/GDBMIParserTests.st
--- a/GDBCLICommand.st	Mon Jan 28 14:56:14 2019 +0000
+++ b/GDBCLICommand.st	Mon Jan 28 22:47:57 2019 +0000
@@ -21,8 +21,8 @@
 "{ NameSpace: Smalltalk }"
 
 GDBCommand subclass:#GDBCLICommand
-	instanceVariableNames:'value runOnBackground'
-	classVariableNames:''
+	instanceVariableNames:'value operation runOnBackground'
+	classVariableNames:'Operations Aliases'
 	poolDictionaries:''
 	category:'GDB-Core-Commands'
 !
@@ -50,10 +50,291 @@
 "
 ! !
 
+!GDBCLICommand class methodsFor:'initialization'!
+
+initialize
+
+    " The string below is generated by:
+
+      gdb -ex 'set max-completions 1000000' -ex 'complete help ' -ex 'quit' | sed -e 's/^help //g'
+    "
+    Aliases := Dictionary withKeysAndValues: #(
+        'r'     'run'
+
+        'c'     'continue'
+        'fg'    'continue'
+
+        's'     'step'
+        'si'    'stepi'
+
+        'n'     'next'
+        'ni'    'nexti'
+
+        'u'     'until'    
+
+        'b'     'break'
+        ).
+
+    Operations := 
+'actions
+add-auto-load-safe-path
+add-auto-load-scripts-directory
+add-inferior
+add-symbol-file
+add-symbol-file-from-memory
+advance
+agent-printf
+alias
+aliases
+append
+apropos
+attach
+awatch
+backtrace
+bookmark
+break
+break-range
+breakpoints
+bt
+call
+catch
+cd
+checkpoint
+clear
+clone-inferior
+collect
+commands
+compare-sections
+compile
+complete
+condition
+continue
+core-file
+data
+define
+delete
+demangle
+detach
+directory
+disable
+disassemble
+disconnect
+display
+document
+dont-repeat
+down
+down-silently
+dprintf
+dump
+echo
+edit
+enable
+end
+eval
+exec-file
+explore
+expression
+faas
+file
+files
+find
+finish
+flash-erase
+flushregs
+focus
+forward-search
+frame
+fs
+ftrace
+function
+generate-core-file
+goto-bookmark
+guile
+guile-repl
+handle
+hbreak
+help
+if
+ignore
+inferior
+info
+init-if-undefined
+internals
+interpreter-exec
+interrupt
+jit-reader-load
+jit-reader-unload
+jump
+kill
+layout
+list
+load
+macro
+maintenance
+make
+mem
+monitor
+new-ui
+next
+nexti
+ni
+nosharedlibrary
+obscure
+output
+overlay
+passcount
+path
+print
+print-object
+printf
+ptype
+pwd
+python
+python-interactive
+queue-signal
+quit
+rbreak
+rc
+record
+refresh
+remote
+remove-inferiors
+remove-symbol-file
+restart
+restore
+return
+reverse-continue
+reverse-finish
+reverse-next
+reverse-nexti
+reverse-search
+reverse-step
+reverse-stepi
+rni
+rsi
+run
+running
+rwatch
+save
+search
+section
+select-frame
+set
+sharedlibrary
+shell
+show
+si
+signal
+skip
+source
+stack
+start
+starti
+status
+step
+stepi
+stepping
+stop
+strace
+support
+symbol-file
+taas
+tabset
+target
+task
+tbreak
+tcatch
+tdump
+teval
+tfaas
+tfind
+thbreak
+thread
+tp
+trace
+tracepoints
+tsave
+tstart
+tstatus
+tstop
+tty
+tui
+tvariable
+undisplay
+unset
+until
+up
+up-silently
+update
+user-defined
+watch
+wh
+whatis
+where
+while
+while-stepping
+winheight
+ws
+x'
+    splitBy: Character cr.
+
+    "Created: / 28-01-2019 / 22:13:56 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+! !
+
 !GDBCLICommand methodsFor:'accessing'!
 
+operation
+    "The CLI command string without any arguments, such as 'run' , 'continue' 
+     and so on.
+
+     The resulting value expands all aliases, i.e., would return 'continue' even if
+     in the (user-typed) command it is just 'c' or 'cont'.
+
+     If there's no such CLI command, return nil. This may happen when user types
+     an ambiguous command and/or bogus command.
+
+     WARNING: This method uses static data, therefore may not reflect your GDB.
+     It does not reflect any commands defined by scripts.
+    "
+
+    operation isNil ifTrue:[ 
+        | i |
+
+        i := value indexOfSeparator.
+        i ~~ 0 ifTrue:[
+            operation := value copyTo: i - 1.
+        ] ifFalse:[ 
+            operation := value.
+        ].
+
+        (Aliases includesKey: operation) ifTrue:[ 
+            "/ If it is an alias, resolve it to it's canonical form
+            operation := Aliases at: operation
+        ] ifFalse:[ 
+            "/ else  handle 'shortened forms', i.e., for 'cont'
+            "/ return 'continue'
+            | candidates |
+
+            candidates := Operations select:[:e | e startsWith: operation ].
+            candidates size == 1 ifTrue:[ 
+                operation := candidates first
+            ] ifFalse:[ 
+                (candidates includes: operation) ifFalse:[ 
+                    operation := nil.
+                ].
+            ].
+        ].
+
+    ].
+    ^ operation
+
+    "Created: / 28-01-2019 / 21:49:05 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
 runOnBackground
-    ^ runOnBackground
+    ^ runOnBackground == true
+
+    "Modified: / 28-01-2019 / 21:28:25 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 runOnBackground:aBoolean
@@ -65,7 +346,21 @@
 !
 
 value:aString
-    value := aString.
+    | i c |
+
+    i := aString size.
+    [ c := aString at: i. c isSeparator] whileTrue:[
+        i := i - 1.
+    ].
+    c == $& ifTrue:[ 
+        value := aString copyTo: i - 1.
+        runOnBackground := true.
+    ] ifFalse:[ 
+        value := aString.
+        runOnBackground := false.
+    ].
+
+    "Modified (format): / 28-01-2019 / 23:05:28 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !GDBCLICommand methodsFor:'testing'!
@@ -81,3 +376,5 @@
     ^ '$Changeset: <not expanded> $'
 ! !
 
+
+GDBCLICommand initialize!
--- a/GDBDebugger.st	Mon Jan 28 14:56:14 2019 +0000
+++ b/GDBDebugger.st	Mon Jan 28 22:47:57 2019 +0000
@@ -296,7 +296,7 @@
 
         cmd := command.
         cmd isString ifTrue:[
-            cmd := GDBCLICommand new value:cmd.
+            cmd := GDBCommand parse:cmd.
         ].    
         cmd token: self nextCommandSequnceNumber.
         connection pushEvent:(GDBCommandEvent new command:cmd).
@@ -305,7 +305,7 @@
 
     "Created: / 02-06-2014 / 23:45:43 / Jan Vrany <jan.vrany@fit.cvut.cz>"
     "Modified: / 26-03-2018 / 21:19:59 / jv"
-    "Modified: / 03-10-2018 / 11:59:43 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 28-01-2019 / 21:26:28 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 send:command andWaitFor:eventHandlers
@@ -1019,7 +1019,7 @@
     connection inferiorPTY notNil ifTrue:[
         self send: (GDBMI_inferior_tty_set arguments: (Array with: connection inferiorPTY name)).
     ].
-    self send: (GDBMI_gdb_set arguments: #('target-async' 'on')).
+    self send: (GDBMI_gdb_set arguments: #('mi-async' 'on')).
 
     result := self send: GDBMI_list_features new.
     debuggerFeatures := result propertyAt: #features.
@@ -1030,7 +1030,7 @@
 
     "Created: / 20-06-2014 / 21:45:21 / Jan Vrany <jan.vrany@fit.cvut.cz>"
     "Modified: / 26-03-2018 / 21:36:31 / jv"
-    "Modified: / 09-04-2018 / 15:38:13 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 28-01-2019 / 15:45:14 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 release
--- a/GDBMIPrinter.st	Mon Jan 28 14:56:14 2019 +0000
+++ b/GDBMIPrinter.st	Mon Jan 28 22:47:57 2019 +0000
@@ -161,8 +161,12 @@
         aGDBCLICommand token printOn: stream.
     ].
     stream nextPutAll: aGDBCLICommand value.
+    aGDBCLICommand runOnBackground ifTrue:[ 
+        stream space; nextPut:$&  
+    ].
 
     "Created: / 11-07-2017 / 21:33:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 28-01-2019 / 21:24:53 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 printCommandMI: aGDBMICommand
--- a/GDBThreadGroup.st	Mon Jan 28 14:56:14 2019 +0000
+++ b/GDBThreadGroup.st	Mon Jan 28 22:47:57 2019 +0000
@@ -112,28 +112,29 @@
     "Return name path of the executable (if known) or nil (if unknown)"
 
     (executable isNil and:[ pid notNil and:[self isStopped or:[debugger hasFeature:'async']]]) ifTrue:[
-        | result tg |
+        debugger send: GDBMI_list_thread_groups new andWithResultDo:[:result|
+            | tg |
 
-        result := debugger send: GDBMI_list_thread_groups new.
-        result status ~~ CommandStatusDone ifTrue:[ 
-            self error: 'Failed to send command.'
+            result status ~~ CommandStatusDone ifTrue:[ 
+                self error: 'Failed to send command.'
+            ].
+            tg := (result propertyAt: 'groups') detect: [: each | each id = id ].
+            "/ In some cases the executable is not known - it may not exist (such as
+            "/ when debugging bare-metal code) or the target does not report it
+            "/ (may happen, for example wine's winedbg GDB proxy does not report
+            "/ executable names).
+            "/ 
+            "/ In this case, we store a sentinel object in `executable` instvar
+            "/ to prevent repeated queries which are bound to fail (see the nil-check
+            "/ above.
+            executable := tg executableOrNil ? ExecutableSentinel.
         ].
-        tg := (result propertyAt: 'groups') detect: [: each | each id = id ].
-        "/ In some cases the executable is not known - it may not exist (such as
-        "/ when debugging bare-metal code) or the target does not report it
-        "/ (may happen, for example wine's winedbg GDB proxy does not report
-        "/ executable names).
-        "/ 
-        "/ In this case, we store a sentinel object in `executable` instvar
-        "/ to prevent repeated queries which are bound to fail (see the nil-check
-        "/ above.
-        executable := tg executableOrNil ? ExecutableSentinel.
     ].
     ^ executable == ExecutableSentinel ifTrue:[ nil ] ifFalse:[ executable ]
 
     "Created: / 06-06-2017 / 00:04:43 / Jan Vrany <jan.vrany@fit.cvut.cz>"
     "Modified: / 26-03-2018 / 21:44:53 / jv"
-    "Modified (comment): / 07-06-2018 / 10:09:40 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 28-01-2019 / 14:57:33 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 executableOrNil
--- a/tests/GDBMIParserTests.st	Mon Jan 28 14:56:14 2019 +0000
+++ b/tests/GDBMIParserTests.st	Mon Jan 28 22:47:57 2019 +0000
@@ -130,8 +130,18 @@
     self assert:command isCLICommand.
     self assert:command token isNil.
     self assert:command value = 'b factorial'.
+    self assert:command operation = 'break'.
+    self assert:command runOnBackground not.
+
+    command := (GDBMIParser on:'cont   &   ') parseCommand.
+    self assert:command isCLICommand.
+    self assert:command token isNil.
+    self assert:command value = 'cont   '.
+    self assert:command operation = 'continue'.
+    self assert:command runOnBackground.
 
     "Created: / 24-06-2014 / 23:21:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 28-01-2019 / 23:05:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 test_command_02