Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extract keys from a map #3164

Closed
aktau opened this issue Oct 10, 2022 · 5 comments
Closed

extract keys from a map #3164

aktau opened this issue Oct 10, 2022 · 5 comments

Comments

@aktau
Copy link

aktau commented Oct 10, 2022

  1. What version of Delve are you using (dlv version)? 1.9.1
  2. What version of Go are you using? (go version)? 1.19
  3. What operating system and processor architecture are you using? Linux AMD64
  4. What did you do? I open a core dump with Delve and try to access the key(s) of a map that I cannot construct myself
  5. What did you expect to see? I expected to be able to do this somehow.
  6. What did you see instead? I couldn't.

Imagine a map from interface to bool:

type Int interface {
  Integrate() // Some dummy function
}
var m map[Int]bool // Lookup map

I find myself in a situation where I really want to inspect the key (stored as an interface), but I can't find how to actually get at (name) this key in Delve. I've tried things like:

(dlv) print for k := range m { print(k) }
Command failed: 1:1: expected operand, found 'for'
(dlv) print maps.Keys(m)
Command failed: could not evaluate function or type maps.Keys: could not find symbol value for maps
(dlv) print m[0]
Command failed: can not convert 0 constant to Int

Pingback to #1465, whose problem can (I think) be worked around if something like this were available.

@aarzilli
Copy link
Member

Printing the map (print m) will also print the keys.

@derekparker
Copy link
Member

You can write a starlark script and load it into Delve which can perform this task for you:

def command_iterate_kv(m):
    c = eval(None, m).Variable.Children
    window_size = 2
    for i in range(len(c) - window_size + 1):
        print("key=", c[i].Value)
        print("value=", c[i+1].Value)

Name the file <some file name>.star (note the .star extension) and then load it into Delve using the source command, e.g. source foo.star.

@derekparker
Copy link
Member

For more information on our starlark scripting interface see: https://github.com/go-delve/delve/blob/master/Documentation/cli/starlark.md.

@aktau
Copy link
Author

aktau commented Oct 11, 2022

Printing the map (print m) will also print the keys.

Indeed it will, but it won't allow me access/inspect this value. In the specific case of my example, I'm dealing with an interface backed by a struct with many deeply nested data fields, only a few of which I actually want to see. I would love to be able to select fields of this value.

You can write a starlark script and load it into Delve which can perform this task for you:

Thanks, this looks like it could be a good workaround and I will try it out. That said, I do posit that this would be a nice thing to have "natively":

(dlv) print keys(myMap)[0]
(dlv) print values(myMap)[0] # Not really necessary, we can just do `print(myMap(keys(myMap)[0])`

Of course, the ordering of the keys could be a problem, at the very least it should be deterministic.

@aktau
Copy link
Author

aktau commented Oct 11, 2022

I've adjusted the example above a little bit. It didn't work as-is when I first tried it because if you don't name the argument to the new command args, Delve tries to parse what the user wrote as a starlark expression. In the end I made a variant that works this way too (mapidx) because I needed it to take two arguments.

# Adapted from https://github.com/go-delve/delve/issues/3164.

def command_mapkeys(args):
    """Print the keys of the map (one per line).

    Example (if myMap is a map variable):
      (dlv) mapkeys myMap
      myPackage.myInterface(*myPackage.concrete) 0xc00634c150
      myPackage.myInterface(*myPackage.concrete) 0xc00634c160
    """
    # Implementation note: if the first (and only) argument is called "args"
    # https://github.com/go-delve/delve/blob/master/Documentation/cli/starlark.md#creating-new-commands,
    # it gets passed as a string.
    c = eval(None, args).Variable.Children
    # Maps are rendered in Starlark as a list of flat key, value pairs:
    #  [key1, val1, key2, val2, key3, val3, ...].
    # So iterate every with a step of 2.
    for i in range(int(len(c) / 2)):
        print(c[i*2].Value)

def command_mapidx(mapName, index):
    """Print the key of the map at index.

    Args:
      mapName: name of a map variable, SHOULD BE A STRING (enclosed in quotes).
      index: the index of the map to access, index order is undefined.

    Usage:
      Get the key at index 1 (i.e.: the second key)

        (dlv) mapidx 'myMap', 1
          myPackage.myInterface(*myPackage.concrete) 0xc00634c160

      To inspect this type, cast it:

        (dlv) print *("*myPackage.concrete")(0xc00634c160)
        myPackage.concrete {
          target: "",
          lastUsed: time.Time(0001-01-01T00:00:00Z){
                  wall: 0,
                  ext: 0,
                  loc: *time.Location nil,},}
    """
    c = eval(None, mapName).Variable.Children
    # Maps are rendered in Starlark as a list of flat key, value pairs:
    #  [key1, val1, key2, val2, key3, val3, ...]
    # Hence double the index.
    print(c[index*2].Value)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants