- Development Notes 2020-08-06, Bob Dubner
- First, of course, you have to have Visual Studio Code installed
- Everything is installed. Now what?
- Oh, you want to do development on the extension?
- The three launch configurations
- The "Extension" configuration
- The "Server" configuration
- The Client configuration
- Handling COBOL "ACCEPT" instructions needs to be improved.
Development Notes 2020-08-06, Bob Dubner
This folder is for developing the cbl-dbg extension.
First, of course, you have to have Visual Studio Code installed
Making sure you have the correct software installed so that you can install
additional correct software is the usual infinite regress bookended in insanity.
I no longer recall how I got bootstrapped in; I think I installed snap
(or
maybe it was already present on Ubuntu) and then used
sudo snap install --classic code
sudo snap install --classic code-insiders
for the regular and bleeding-edge versions of Visual Studio Code. You probably don't need the bleeding-edge code-insiders version.
I don't know at what point npm was installed. I do know that getting vsce
to work
involved running
npm install -g vsce
Everything is installed. Now what?
Creating the VSIX extension installation file is accomplished by running
npm install
vsce package
(The npm install
is often necessary to appease the Microsoft development demons. I have found that every few weeks, vsce package
will fail with mysterious error messages about missing prerequisites. npm install
, so far, seems to forestall that. I am not confident about the future.)
The resulting cbl-dbg-x.x.x.vsix package needs to be installed on a user's system. This can be done by launching Visual Studio Code and displaying the Extensions pane (Ctrl+Shift+X), then clicking the three-dot "More actions" item, and then selecting "Install from VSIX...".
You can also get there via the Command Palette (Ctrl+Shift+P) and finding "Extensions: Install from VSIX...", which you can find by typing "VSIX".
It's also possible to install the VSIX package from the command line:
code --install-extension cbl-dbg-x.x.x.vsix
With the cbl-dbg extension installed and enabled, you need to look elsewhere for the cbl-dgb-template folder for the starting point for compiling and debugging actual COBOL code.
Oh, you want to do development on the extension?
You have guts. Either that, or you are me, and you are trying to remember what you did in the first place.
Here's where you are going to be going: When you are actively debugging the debugger, there will be not one, not two, but three Visual Studio Code instances running.
The debugging adapter
, which is likely what you are most interested in changing, will be running from this folder as the Server
configuration.
The extension package
, which acts as a bridge between a user and the debugging adapter, will be running as a second instance in this folder as the Extension
configuration.
The extension configuration launches a third instance of Visual Studio Code. You'll use that one to open the folder containing the test program that will be debugged by the debugging adapter. At this writing, this development-notes.md file is in a folder named CblGdbExt/CblGdb
. Some suitable test programs can be found in CblGdbExt/samples
.
The three launch configurations
Take a look at this project's launch.json
file.
"Extension"
configuration
The The key thing about the Extension configuration is that it launches execPath
, which means yet another copy of Visual Studio Code.
"Server"
configuration
The The Server configuration launches the debug adapter in Server mode, which is determined by the presences of the "args": [ "--server=4711" ],
parameter. In this mode, it listens for an attempt to connect on port 4711, rather than doing the usual extension thing. (The "usual extension thing" is defined by the "cbl-gdb"
configuration in the extensions package.json file, also found in this folder.)
Client
configuration
The The Client configuration controls what happens when the third instance of Visual Studio Code, the one launched by the Extensions configuration, is used to F5/Start Debugging
. Here is the launch.json file I've been using for targets used for debugging the debug adapter:
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Attach to debugServer",
"type": "node",
"request": "attach",
"cwd": "${workspaceFolder}",
"debugServer": 4711,
"solibs":"${env:PRIM_LIBRARY_PATH}",
"target": "${input:attachtopid}",
},
{
"name": "Attach to COBOL debugger",
"type": "cbl-gdb",
"request": "attach",
"cwd":"${workspaceFolder}",
"solibs":"${env:PRIM_LIBRARY_PATH}",
"target": "${input:attachtopid}",
}
],
"inputs": [
{
"id": "attachtopid",
"type": "promptString",
"description": "Enter the PID to attach to",
"default": ""
}
]
}
Note how the Attach to debugServer
configuration expects to connect to a server instance through the matching port 4711.
The Attach to COBOL debugger
isn't used during sessions when one is debugging the debugger, but it is an example of how one uses the debug adapter extension once it is packaged up and installed.
It should be mentioned that the "inputs"
section defines the attachtopid
input, which causes Visual Studio Code to ask the user for the PID that is going to be debugged after gdb attaches to it.
Here's launch.json for a specific program to run when debugging the debugging adapter. This is copied from the sample/optfde01 program elsewhere in the source code repository for this VSIX package:
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Build ${file} with cobc/cobst, debug with debugServer",
"type": "node",
"request": "launch",
"skipFiles": ["<node_internals>/**"],
"preLaunchTask": "make",
"program": "${workspaceRoot}/optfde01",
"cwd": "${workspaceFolder}",
"arguments": "",
"debugServer": 4711
},
{
"name": "cobc/cobst build and debug",
"type": "cbl-gdb",
"request": "launch",
"preLaunchTask": "make",
"program": "${workspaceFolder}/optfde01",
"cwd": "${workspaceFolder}",
"arguments": ""
}
]
}
Handling COBOL "ACCEPT" instructions needs to be improved.
Version 4.2.3 has a "halfway" implementation of coping with COBOL "ACCEPT" instructions. When debugging with command-line GDB, by default both GDB and the launched inferior program accept input and generate output on the same TTY instance. GDB can be instructed to direct the inferior's input/output to a different TTY with the "-tty=" command line parameter. But having them both use the same instance works perfectly well, although it requires sorting out the GDB output from the inferior's output.
But that modality doesn't work well with Visual Studio Code, which itself is multi-threaded. It leads to race conditions, where the COBOL program is trying to ACCEPT keystrokes from STDIN while at the same time VSC is sending asynchronous commands via STDIN and expecting responses on STDOUT.
Version 4.2.3 manages to work around this. First, it keeps track of whether the program is *running or *stopped. When *running, the debugging adapter blocks VSC from sending commands to GDB. It detects inputs that are supposed to be to the inferior, and it lets them through in raw form.
When I noted that sometimes the first characters of inputs to the inferior were somehow being lost, I added a 100-millisecond "freeze" on Visual Studio Code processing after sending data to the inferior. That seems to have eliminated the truncation problem. But I don't know for sure.
I investigated how Microsoft handles this in their C/C++ debugging extension. They somehow assign the inferior TTY (using the GDB --tty= switch) to the VSC "Integrated Terminal" pane; I have been unable to figure out how the determine the /dev/pts/x determination for that pain. For communication to GDB, they set up a couple of pipes: one for input, the other for output.
To sum up, when they invoke GDB, the command being issued looks like this:
/usr/bin/gdb --interpreter=mi --tty=$DbgTerm <pipe1 >pipe2
By turning logging on in Visual Studio Code (See below, where I show the logging
launch settings), I can see the relevant sequence as
```--> E (output): {"type":"event","event":"output","body":{"category":"console","output":"1: (202) DbgCmd:echo $$ > /tmp/Microsoft-MIEngine-Pid-55bh0mvc.ze9 ; cd "/home/bob/repos/callconv" ; DbgTerm=tty
; set -o monitor ; trap 'rm "/tmp/Microsoft-MIEngine-In-csxnrfi1.6e5" "/tmp/Microsoft-MIEngine-Out-0ectnrnn.chn" "/tmp/Microsoft-MIEngine-Pid-55bh0mvc.ze9" "/tmp/Microsoft-MIEngine-Cmd-3q8dd413.zen"' EXIT ; "/usr/bin/gdb" --interpreter=mi --tty=DbgTerm < \"/tmp/Microsoft-MIEngine-In-csxnrfi1.6e5\" > \"/tmp/Microsoft-MIEngine-Out-0ectnrnn.chn\" & clear; pid=! ; echo $pid > "/tmp/Microsoft-MIEngine-Pid-55bh0mvc.ze9" ; wait $pid; \n"},"seq":4}
Somehow or other the critical `tty=$DbgTerm` variable is being set with the
DbgTerm=`tty`
command. How that is run in the proper Visual Studio Code window pane, I do not know. Nor do I see how the Intergrated Terminal is set to run without a shell program.
The difficulties and time involved in exploring these questions are why I haven't yet implemented them.
-- Bob Dubner, 2021-03-19
Here is the launch.json I used for debugging a C/C++ program. Note the logging options that cause a certain amount of feedback in the DEBUG OUTPUT window.
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "gcc - Build and debug active file",
"type": "cppdbg",
"request": "launch",
"program": "{fileDirname}/{fileBasenameNoExtension}",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "C/C++: gcc build active file",
"miDebuggerPath": "/usr/bin/gdb",
"logging": {
"moduleLoad": false,
"engineLogging": true,
"trace": true,
"exceptions": false,
"programOutput": true,
"traceResponse": true
}
}
]
}