Broken `value` field in a `VariablesResponse`, should be of type string, not array
tinloaf opened this issue · comments
Hi, I'm trying to use this debugger as a debug adapter with Emacs' dap-mode
. I realize that this is not the primary purpose of code-debug
, but it's 'officially' supported by dap-mode
. I'm using gdb
v. 9.2 as debugger. dap-mode
does some auto-download-magic for the debug adapter, so I did not download a specific version. But when looking into the downloaded extensions.vsixmanifest
file, it looks to me like I'm using version 0.26.0
:
<Identity Language="en-US" Id="debug" Version="0.26.0" Publisher="webfreak" />
I encountered a crash in dap-mode
, which I think I traced back to a problem in code-debug
.
I set dap-mode
to print all I/O with the debug adapter (i.e., code-debug
), and below is (some part of) what I see once a breakpoint is hit. This looks Okay so far - dap-mode
is first requesting some info about the top stack frame, and then requests the local variables inside that stack frame. The response looks Okay to me.
Sending:
{
"command": "scopes",
"arguments": {
"frameId": 1
},
"type": "request",
"seq": 14
}
Received:
{
"seq": 6865,
"type": "response",
"request_seq": 14,
"command": "scopes",
"success": true,
"body": {
"scopes": [
{
"name": "Local",
"variablesReference": 1001,
"expensive": null
}
]
}
}
Sending:
{
"command": "variables",
"arguments": {
"variablesReference": 1001
},
"type": "request",
"seq": 15
}
Received:
{
"seq": 6866,
"type": "response",
"request_seq": 15,
"command": "variables",
"success": true,
"body": {
"variables": [
{
"name": "this",
"value": "<args>",
"variablesReference": 132072
},
{
"name": "tripNotification",
"value": "Ref@0x7ffffffecdb0",
"variablesReference": 132073
},
{
"name": "incoming",
"type": "std::shared_ptr<rtd::CIncomingRBLData>",
"value": "<unknown>",
"variablesReference": 132074
},
{
"name": "tripData",
"type": "rtd::CRBLTripData",
"value": "<unknown>",
"variablesReference": 132075
},
{
"name": "avmError",
"value": "Ref@0x962a068",
"variablesReference": 132076
},
{
"name": "sanityCheckResult",
"type": "EFAPTKernelLib::SAVMError",
"value": "<unknown>",
"variablesReference": 132077
},
{
"name": "processingTime_msec",
"value": "135841511",
"variablesReference": 0
}
]
}
}
The crash now happens when I try to expand the this
variable. This is the I/O for that request:
Sending:
{
"command": "variables",
"arguments": {
"variablesReference": 132072
},
"type": "request",
"seq": 16
}
Received:
{
"seq": 6867,
"type": "response",
"request_seq": 16,
"command": "variables",
"success": true,
"body": {
"variables": [
{
"name": "[err]",
"value": [
{
"name": "m_lMessageCount",
"value": "0",
"variablesReference": 0
},
{
"name": "m_accumulatedTime_msec",
"value": "8675833939940",
"variablesReference": 0
},
{
"name": "m_ptKernelVersion",
"value": "Object",
"variablesReference": 132079
},
{
"name": "m_ptrRblTripDataBuilder",
"value": "std",
"variablesReference": 0
}
],
"variablesReference": 0
}
]
}
}
Those variables
look wrong to me. The DAP specification for the Variable type specifies that the value
member must be a string, not a list.
Please let me know if there is anything I can do to help debug this. My (completely unsubstantiated) feeling is that this might be related to #342 .
One more remark: The m_ptrRblTripDataBuilder
member is of type std::unique_ptr<Something>
. All other items in the list in the value
field all look more-or-less okay to me and should be the variables in the variables
array one level up. Only the m_ptrRblTripDataBuilder
member seems broken. It's value is not std
- I assume that this is somehow generated from the type std::unique_pointer<Something>
? Also, Something
is a class, so the value
for m_ptrRblTripDataBuilder
should probably be Object
and it should have a variablesReference
.
I can reproduce this with all versions down to (at least) 0.24.0
of code-debug
, so this does not seem to be a recent regression.
Hi @tinloaf, thanks for this report.
Have you experimented with different values for the valuesFormatting
setting?. If your version of GDB supports this, usually the prettyPrinters
value works well for most people. Your report seems similar to the #197, which seemed to show similar behavior (i.e., "[err]") and changing the value of valuesFormatting
addressed that problem. The default setting of valuesFormatting
(i.e., parseText
) does not have strong language-specific formatting, so typically best to use prettyPrinters
when it is available.
Great, that worked! I just realized that the possible options in the launch.json
config are described in the package.json
file. dap-mode
does not parse these and therefore I did not have an overview over the possible options. I think I'll open a PR with dap-mode
to always (or at least as a default) use prettyPrinters
, since parseText
seems to break dap-mode
.
Thanks!
the valuesFormatting
property is specific to code-debug, it's probably not good to ask dap-mode
The parseText
using arrays in values is definitely a bug I think, so I'm reopening this.
dap-mode
includes a special package (called dap-gdb-lldb
for some reason) to interface with code-debug
. I think it would be prudent that this dap-gdb-lldb
sets valuesFormatting=prettyPrinters
as a default. But also fixing the parseText
bug in code-debug
probably is the best way to go. :)
@tinloaf You have note above how variables
currently do look like, can you please also post how it should look?
@WebFreak001, I created a test example based on the similar issue described in #197. From there, I was able to duplicate the problem reported by @tinloaf. I've attached an example application. launch.json, screen capture, trace, etc., in case someone wants to repeat this procedure. After I looked into this, it appears that any parameter passed as a pointer is being treated as an array. This is due to the check in gdb_expansion.ts which creates an 'ExtendedVariable' for pointer parameters (i.e., arg
uments). Additionally, the subsequent expansion logic in mibase.ts, appears to treat this like an array. Looking back at the history of when 'ExtendedVariable' was added, it appears it was an attempt to address issues with *argv[]
in C (see the commit). This doesn't seem correct though as this logic is used for any parameter passed as a pointer to a function. I tried commenting out the special "arg" logic in gdb_expansion.ts, and that works as well as the 'prettyPrinters' configuration does.
- if (extra && MINode.valueOf(extra, "arg") == "1") {
- ref = variableCreate(getNamespace("*(" + name), { arg: true });
- val = "<args>";
- } else {
ref = variableCreate(getNamespace("*" + name));
val = "Object@" + val;
- }
I'm hoping you might remember the history behind this. Maybe this is just obsolete at this point and can be removed. I also didn't see any other usage of the 'ExtendedVariable' except for this special case, so it's possible that all of that processing surrounding it in mibase.ts might also be able to be removed, if nothing else uses it.
launch.json
{
"name": "Debug",
"type": "gdb",
"request": "launch",
"target": "union_test",
"cwd": "${workspaceRoot}",
"valuesFormatting": "parseText"
}
C Source File
#include <stdio.h>
union example
{
struct common
{
enum
{
TYPE_A,
TYPE_B
} id;
} common;
struct type_a
{
struct common c;
int a_specific_data;
} a;
struct type_b
{
struct common c;
int b_specific_data;
} b;
};
static void foo (const union example *const ex)
{
switch (ex->common.id)
{
case TYPE_A:
printf("TYPE_A\n");
break;
case TYPE_B:
printf("TYPE_B\n");
break;
}
}
int main(int argc, char* argv[])
{
union example my_example;
my_example.a.c.id = TYPE_A;
my_example.a.a_specific_data = 0;
foo(&my_example);
return 0;
}
VSCode <-> Debug Adapter Trace
{"command":"scopes","arguments":{"frameId":1},"type":"request","seq":10}
{"seq":53,"type":"response","request_seq":10,"command":"scopes","success":true,"body":{"scopes":[{"name":"Local","variablesReference":1000,"expensive":false}]}}
{"command":"variables","arguments":{"variablesReference":1000},"type":"request","seq":11}
{"seq":56,"type":"response","request_seq":11,"command":"variables","success":true,"body":{"variables":[{"name":"ex","value":"<args>","variablesReference":1001}]}}
{"command":"variables","arguments":{"variablesReference":1001},"type":"request","seq":13}
{"seq":62,"type":"response","request_seq":13,"command":"variables","success":true,"body":{"variables":[{"name":"[err]","value":[{"name":"common","value":"Object","variablesReference":1002},{"name":"a","value":"Object","variablesReference":1004},{"name":"b","value":"Object","variablesReference":1006}],"variablesReference":0}]}}
Is this also related to any of the other variable printing issues #261 #248 #238?
These don't appear to be the same issue as the main issue identified here. You'd see "<args>" shown next to a local variable if it was executing the "arg" logic as described above. However, I do believe that #261 is the same as the secondary issue described by @tinloaf in a follow-up post here, where it displays "std" for one of the values. That is due to the fact that the Debug Adapter's expression parser (i.e., gdb_expansion.ts) is not sophisticated enough to handle something like the following (taken from the example in #261):
15-data-evaluate-expression "aeew"
GDB -> App: {"token":15,"outOfBandRecord":[],"resultRecords":{"resultClass":"done","results":[["value","std::vector of length 1, capacity 1 = {2}"]]}}
I'm not sure if there is a published syntax format for these expressions or not. If so, the builtin parser could be updated to account for them. However, using the prettyPrinters
option for valuesFormatting
is likely the best solution and probably should be made the default setting to avoid future issues surrounding the sub-optimal parseText
default.
Moving this to the next feature (i.e., non-patch) release milestone as I believe the best correction for this is to change the default setting for valuesFormatting
to prettyPrinters
. Since this changes a default setting, I'd consider this an "incompatible API change" as far a semantic versioning is concerned and thus I don't believe making this change in a patch version make sense.