ptracing долго выполняющийся процесс зависает

Я использую интерфейс Ptrace пакета системных вызовов Go для трассировки процесса. Проблема в том, что если трассировка выполняется долго, трассировка зависает. Я попытался воспроизвести проблему с реализацией C, но там все работает нормально.

Вот код Go для воспроизведения проблемы:

import (
    "fmt"
    "os"
    "os/exec"
    "syscall"
)

func main() {
    len := "9999999"
    cmd := exec.Command("openssl", "rand", "-hex", len)
    cmd.SysProcAttr = &syscall.SysProcAttr{Ptrace: true}
    cmd.Stdout = os.Stdout
    cmd.Stdin = os.Stdin
    cmd.Start()
    pid, _ := syscall.Wait4(-1, nil, syscall.WALL, nil)

    for {
        syscall.PtraceSyscall(pid, 0)
        _, err := syscall.Wait4(-1, nil, syscall.WALL, nil) 

        if err != nil {
            fmt.Println(err)
            break
        }
    }
}

При запуске приведенного выше кода процесс никогда не завершается, и его необходимо прервать. Если изменить переменную len на что-то меньшее, например 9, процесс завершится без проблем, и вывод будет выглядеть следующим образом:

$ go run main.go
d2ff963e65e8e1926b
no child processes

person NeverThoughtIWouldPostHere    schedule 22.06.2020    source источник
comment
Вы используете последнюю версию Go? Где процесс заблокирован?   -  person JimB    schedule 22.06.2020
comment
я использую последнюю версию. в приведенном выше коде процесс заблокирован на syscall.Wait4 внутри цикла for   -  person NeverThoughtIWouldPostHere    schedule 22.06.2020


Ответы (1)


Нашел. Программа зависает, когда среда выполнения Go изменяет поток, в котором выполняется горутина. Можно проверить в примере кода, напечатав fmt.Println(syscall.Gettid()) внутри цикла:

package main

import (
    "fmt"
    "os/exec"
    "syscall"
)

func main() {
    len := "9999999"
    cmd := exec.Command("openssl", "rand", "-hex", len)
    cmd.SysProcAttr = &syscall.SysProcAttr{Ptrace: true}
    cmd.Start()

    pid, _ := syscall.Wait4(-1, nil, syscall.WALL, nil)

    for {
        fmt.Println(syscall.Gettid())
        syscall.PtraceSyscall(pid, 0)
        _, err := syscall.Wait4(-1, nil, syscall.WALL, nil)

        if err != nil {
            fmt.Println(err)
            break
        }
    }
}

Решение: заблокируйте выполнение горутины в текущем потоке, используя runtime.LockOSThread():

....
func main() {
    runtime.LockOSThread()
    len := "9999999"
    cmd := exec.Command("openssl", "rand", "-hex", len)
....
person NeverThoughtIWouldPostHere    schedule 23.06.2020