WebFreak001 / code-debug

Native debugging for VSCode

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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., arguments). 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"
        }
VSCode Screen Capture

union_test

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}]}}

@brownts Thank you for the check and summary. Is this also related to any of the other variable printing issues #261 #248 #238?

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.