Подтвердить что ты не робот

Неизвестное поле в трассировке стека паники

В попытке узнать, как отлаживать трассировки стека из паники, я наткнулся на что-то запутанное.

package main

func F(a int) {
    panic(nil)
}

func main() {
    F(1)
}

выводит следующие данные, когда я запускаю его на присоединенной ссылке для воспроизведения:

panic: nil

goroutine 1 [running]:
main.F(0x1, 0x10436000)
    /tmp/sandbox090887108/main.go:4 +0x20
main.main()
    /tmp/sandbox090887108/main.go:8 +0x20

Я не могу расшифровать то, что означает второе число (0x10436000 в main.F(0x1, 0x10436000)). Он не появляется, если есть второй аргумент int или если что-то еще, если он передан в качестве первого аргумента (можно увидеть во второй ссылке воспроизведения).

Один аргумент: https://play.golang.org/p/3iV48xlNFR

Два аргумента: https://play.golang.org/p/4jA7ueI86K

4b9b3361

Ответ 1

Данные, напечатанные в трассировке стека, являются аргументами, но значения не соответствуют непосредственно переданным аргументам, это необработанные данные, напечатанные в значениях размера указателя (хотя обычно это то же самое, что и размер родного слова). Игровая площадка немного уникальна, поскольку она представляет собой 64-битную архитектуру слова с 32-битными указателями (GOARCH=amd64p32).

В traceback.go вы можете видеть, что значения печатаются путем перехода по аргументам на основе размера указателя;

for i := uintptr(0); i < frame.arglen/sys.PtrSize; i++ {

Так как размер слова в два раза больше размера указателя на игровой площадке, вы всегда будете иметь четное количество значений, напечатанных в аргументах фрейма.

Другой пример того, как данные представлены, можно увидеть на игровой площадке, используя меньшие типы в аргументах функции: https://play.golang.org/p/vHZDEHQZLh

func F(a uint8) {
    panic(nil)
}

// F(1)
// main.F(0x97301, 0x10436000)

Используются только первые 8 бит 64-битного слова, которое равно 0x97301 & 0x0f или просто 1. Дополнительный 0x97300 от первого значения и всего 0x10436000 - это всего лишь остаток этого первого 64-битного слова, которое не используется функцией.

Вы можете увидеть то же поведение в системах amd64, используя больше аргументов. Эта подпись, например,

func F(a, b, c uint32)

при вызове через F(1, 1, 1) он распечатает трассировку стека, например:

main.F(0x100000001, 0xc400000001)

потому что 3 32-битные значения принимают 2 слова

Окончательный набор значений для заметок - это возвращаемые значения, которые также выделяются в стеке. Следующая сигнатура функции:

func F(a int64) (int, int)

на amd64, будут показаны аргументы фрейма стека, такие как:

main.F(0xa, 0x1054d60, 0xc420078058)

С одним словом для a и еще двумя для возвращаемых значений (int, int). Однако возвращаемые значения не инициализируются, поэтому мне не так много полезного, кроме как понять, почему эти значения есть.