Skip to content
New issue

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

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

Already on GitHub? # to your account

pkg/proc: support swiss table map implementation #3838

Merged
merged 1 commit into from
Dec 5, 2024

Conversation

aarzilli
Copy link
Member

Adds support for the swiss table implementation for Go's maps.

@aarzilli aarzilli changed the title pkg/proc: support swiss table map implementation pkg/proc: support swiss table map implementation [WIP] Oct 22, 2024
@aarzilli aarzilli marked this pull request as draft October 22, 2024 09:22
@aarzilli
Copy link
Member Author

Marking WIP until the swisstable implementation in go doesn't have TODO comments.

@aarzilli aarzilli marked this pull request as ready for review November 5, 2024 10:44
@aarzilli aarzilli changed the title pkg/proc: support swiss table map implementation [WIP] pkg/proc: support swiss table map implementation Nov 5, 2024
@aarzilli
Copy link
Member Author

aarzilli commented Nov 5, 2024

The swissmap backend has been enabled by default on 1.24 so I think we can review this now.

Copy link
Contributor

@prattmic prattmic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took a look at the swiss implementation (not the classic one). LGTM, the only thing I saw that looks actually incorrect is the comment about <= on it.tab.groups.Len.

@@ -1992,12 +1991,7 @@ func (v *Variable) loadMap(recurseLevel int, cfg LoadConfig) {
errcount := 0
for it.next() {
key := it.key()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How were indirect keys handled previously? It looks like they simply weren't supported at all? (Unlike indirect values, which are handled just below)

(I'm just curious, this does seem to be handled for old and swiss maps in the new implementation.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We didn't handle either. I missed it when I first implemented it years ago and nobody ever reported it as a bug.
I've added support to both with this PR, for classic map is the code in (*mapIteratorClassic).key() that does the k.Kind == reflect.Ptr && !it.keyTypeIsPtr, for swiss maps it's in next, the if for it.curKey.Kind == reflect.Ptr && !it.keyTypeIsPtr (and their equivalents for value).

return false
}

if it.curKey.Kind == reflect.Ptr && !it.keyTypeIsPtr {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how you detect indirect keys/values.

IIUC, the idea here is that if the key field has a pointer type, but the key type itself is not a pointer, then it must be an indirect key. On the other hand, if the key type is a pointer, then it is small enough to never be indirect.

I don't see any problems with this logic, though IMO it is worth a comment.

FWIW, the way the runtime does this is that the map type flag field records whether the key/value are indirect. It seems you could do that too, though I'm not sure it would strictly be better or worse.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The map type is no longer stored in the map object, if I'm not mistaken to get to the map type I would have to use the DW_AT_go_runtime_type. It's very annoying.


// loadTypes determines the correct type for it.dirPtr: the linker records
// this type as **table but in reality it is either *[dirLen]*table for
// large maps or *group for small maps, when it.dirLen <= 0.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: in the submitted version, dirLen is always 0 for small maps.

}
}

for ; it.groupIdx <= uint64(it.tab.groups.Len); it.nextGroup() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it.tab.groups.Len is the array length, right? Thus shouldn't this be < not <=?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right.

return it.curValue
}

func (it *mapIteratorSwiss) slotIsEmpty(k uint32) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: slotIsEmptyOrDeleted

(The implementation is correct; this matches empty or deleted slots)

}

it.slotIdx++
if it.slotIdx >= uint32(it.group.slots.Len) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is strictly necessary. If you simply return after it.slotIdx++, then the next call will fail the for ; it.slotIdx < uint32(it.group.slots.Len) condition and fall through to the end of the group loop at L514.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

it.dirPtr.RealType = it.dirPtr.DwarfType
it.dirPtr = it.dirPtr.maybeDereference()
it.dirLen = 1
it.tab = &swissTable{groups: it.dirPtr} // so that we don't try to load this later on
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to do this in the runtime implementation but couldn't afford an allocation. You are lucky to get that luxury!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cross-process memory reads are slow anyway.

Adds support for the swiss table implementation for Go's maps.
Copy link
Member

@derekparker derekparker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@derekparker derekparker merged commit 477e46e into go-delve:master Dec 5, 2024
2 checks passed
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants