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

proc,terminal: read command line of new processes #3346

Merged
merged 1 commit into from
May 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkg/proc/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ func OpenCore(corePath, exePath string, debugInfoDirs []string) (*proc.TargetGro
DisableAsyncPreempt: false,
CanDump: false,
})
_, err = addTarget(p, p.pid, currentThread, exePath, proc.StopAttached)
_, err = addTarget(p, p.pid, currentThread, exePath, proc.StopAttached, "")
return grp, err
}

Expand Down
24 changes: 12 additions & 12 deletions pkg/proc/gdbserial/gdbserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ func newProcess(process *os.Process) *gdbProcess {
}

// Listen waits for a connection from the stub.
func (p *gdbProcess) Listen(listener net.Listener, path string, pid int, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) {
func (p *gdbProcess) Listen(listener net.Listener, path, cmdline string, pid int, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) {
acceptChan := make(chan net.Conn)

go func() {
Expand All @@ -292,19 +292,19 @@ func (p *gdbProcess) Listen(listener net.Listener, path string, pid int, debugIn
if conn == nil {
return nil, errors.New("could not connect")
}
return p.Connect(conn, path, pid, debugInfoDirs, stopReason)
return p.Connect(conn, path, cmdline, pid, debugInfoDirs, stopReason)
case status := <-p.waitChan:
listener.Close()
return nil, fmt.Errorf("stub exited while waiting for connection: %v", status)
}
}

// Dial attempts to connect to the stub.
func (p *gdbProcess) Dial(addr string, path string, pid int, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) {
func (p *gdbProcess) Dial(addr string, path, cmdline string, pid int, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) {
for {
conn, err := net.Dial("tcp", addr)
if err == nil {
return p.Connect(conn, path, pid, debugInfoDirs, stopReason)
return p.Connect(conn, path, cmdline, pid, debugInfoDirs, stopReason)
}
select {
case status := <-p.waitChan:
Expand All @@ -321,7 +321,7 @@ func (p *gdbProcess) Dial(addr string, path string, pid int, debugInfoDirs []str
// program and the PID of the target process, both are optional, however
// some stubs do not provide ways to determine path and pid automatically
// and Connect will be unable to function without knowing them.
func (p *gdbProcess) Connect(conn net.Conn, path string, pid int, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) {
func (p *gdbProcess) Connect(conn net.Conn, path, cmdline string, pid int, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) {
p.conn.conn = conn
p.conn.pid = pid
err := p.conn.handshake(p.regnames)
Expand All @@ -345,7 +345,7 @@ func (p *gdbProcess) Connect(conn net.Conn, path string, pid int, debugInfoDirs
p.gcmdok = false
}

tgt, err := p.initialize(path, debugInfoDirs, stopReason)
tgt, err := p.initialize(path, cmdline, debugInfoDirs, stopReason)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -569,9 +569,9 @@ func LLDBLaunch(cmd []string, wd string, flags proc.LaunchFlags, debugInfoDirs [

var grp *proc.TargetGroup
if listener != nil {
grp, err = p.Listen(listener, cmd[0], 0, debugInfoDirs, proc.StopLaunched)
grp, err = p.Listen(listener, cmd[0], strings.Join(cmd, " "), 0, debugInfoDirs, proc.StopLaunched)
} else {
grp, err = p.Dial(port, cmd[0], 0, debugInfoDirs, proc.StopLaunched)
grp, err = p.Dial(port, cmd[0], strings.Join(cmd, " "), 0, debugInfoDirs, proc.StopLaunched)
}
if p.conn.pid != 0 && foreground && isatty.IsTerminal(os.Stdin.Fd()) {
// Make the target process the controlling process of the tty if it is a foreground process.
Expand Down Expand Up @@ -635,9 +635,9 @@ func LLDBAttach(pid int, path string, debugInfoDirs []string) (*proc.TargetGroup

var grp *proc.TargetGroup
if listener != nil {
grp, err = p.Listen(listener, path, pid, debugInfoDirs, proc.StopAttached)
grp, err = p.Listen(listener, path, "", pid, debugInfoDirs, proc.StopAttached)
} else {
grp, err = p.Dial(port, path, pid, debugInfoDirs, proc.StopAttached)
grp, err = p.Dial(port, path, "", pid, debugInfoDirs, proc.StopAttached)
}
return grp, err
}
Expand Down Expand Up @@ -673,7 +673,7 @@ func (p *gdbProcess) EntryPoint() (uint64, error) {
// initialize uses qProcessInfo to load the inferior's PID and
// executable path. This command is not supported by all stubs and not all
// stubs will report both the PID and executable path.
func (p *gdbProcess) initialize(path string, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) {
func (p *gdbProcess) initialize(path, cmdline string, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) {
var err error
if path == "" {
// If we are attaching to a running process and the user didn't specify
Expand Down Expand Up @@ -730,7 +730,7 @@ func (p *gdbProcess) initialize(path string, debugInfoDirs []string, stopReason
StopReason: stopReason,
CanDump: runtime.GOOS == "darwin",
})
_, err = addTarget(p, p.conn.pid, p.currentThread, path, stopReason)
_, err = addTarget(p, p.conn.pid, p.currentThread, path, stopReason, cmdline)
if err != nil {
p.Detach(true)
return nil, err
Expand Down
6 changes: 3 additions & 3 deletions pkg/proc/gdbserial/rr.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func Record(cmd []string, wd string, quiet bool, redirects [3]string) (tracedir

// Replay starts an instance of rr in replay mode, with the specified trace
// directory, and connects to it.
func Replay(tracedir string, quiet, deleteOnDetach bool, debugInfoDirs []string, rrOnProcessPid int) (*proc.TargetGroup, error) {
func Replay(tracedir string, quiet, deleteOnDetach bool, debugInfoDirs []string, rrOnProcessPid int, cmdline string) (*proc.TargetGroup, error) {
if err := checkRRAvailable(); err != nil {
return nil, err
}
Expand Down Expand Up @@ -168,7 +168,7 @@ func Replay(tracedir string, quiet, deleteOnDetach bool, debugInfoDirs []string,
safeRemoveAll(p.tracedir)
}
}
tgt, err := p.Dial(init.port, init.exe, 0, debugInfoDirs, proc.StopLaunched)
tgt, err := p.Dial(init.port, init.exe, cmdline, 0, debugInfoDirs, proc.StopLaunched)
if err != nil {
rrcmd.Process.Kill()
return nil, err
Expand Down Expand Up @@ -293,7 +293,7 @@ func RecordAndReplay(cmd []string, wd string, quiet bool, debugInfoDirs []string
if tracedir == "" {
return nil, "", err
}
t, err := Replay(tracedir, quiet, true, debugInfoDirs, 0)
t, err := Replay(tracedir, quiet, true, debugInfoDirs, 0, strings.Join(cmd, " "))
return t, tracedir, err
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/proc/native/nonative_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,4 @@ func (t *nativeThread) SoftExc() bool {
panic(ErrNativeBackendDisabled)
}

func initialize(dbp *nativeProcess) error { return nil }
func initialize(dbp *nativeProcess) (string, error) { return "", nil }
26 changes: 16 additions & 10 deletions pkg/proc/native/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,14 @@ func (procgrp *processGroup) procForThread(tid int) *nativeProcess {
return nil
}

func (procgrp *processGroup) add(p *nativeProcess, pid int, currentThread proc.Thread, path string, stopReason proc.StopReason) (*proc.Target, error) {
tgt, err := procgrp.addTarget(p, pid, currentThread, path, stopReason)
func (procgrp *processGroup) add(p *nativeProcess, pid int, currentThread proc.Thread, path string, stopReason proc.StopReason, cmdline string) (*proc.Target, error) {
tgt, err := procgrp.addTarget(p, pid, currentThread, path, stopReason, cmdline)
if err != nil {
return nil, err
}
procgrp.procs = append(procgrp.procs, p)
if tgt != nil {
procgrp.procs = append(procgrp.procs, p)
}
return tgt, nil
}

Expand Down Expand Up @@ -285,20 +287,24 @@ func (dbp *nativeProcess) FindBreakpoint(pc uint64, adjustPC bool) (*proc.Breakp
return nil, false
}

func (dbp *nativeProcess) initializeBasic() error {
if err := initialize(dbp); err != nil {
return err
func (dbp *nativeProcess) initializeBasic() (string, error) {
cmdline, err := initialize(dbp)
if err != nil {
return "", err
}
if err := dbp.updateThreadList(); err != nil {
return err
return "", err
}
return nil
return cmdline, nil
}

// initialize will ensure that all relevant information is loaded
// so the process is ready to be debugged.
func (dbp *nativeProcess) initialize(path string, debugInfoDirs []string) (*proc.TargetGroup, error) {
dbp.initializeBasic()
cmdline, err := dbp.initializeBasic()
if err != nil {
return nil, err
}
stopReason := proc.StopLaunched
if !dbp.childProcess {
stopReason = proc.StopAttached
Expand All @@ -321,7 +327,7 @@ func (dbp *nativeProcess) initialize(path string, debugInfoDirs []string) (*proc
CanDump: runtime.GOOS == "linux" || runtime.GOOS == "freebsd" || (runtime.GOOS == "windows" && runtime.GOARCH == "amd64"),
})
procgrp.addTarget = addTarget
tgt, err := procgrp.add(dbp, dbp.pid, dbp.memthread, path, stopReason)
tgt, err := procgrp.add(dbp, dbp.pid, dbp.memthread, path, stopReason, cmdline)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/proc/native/proc_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,4 +489,4 @@ func (dbp *nativeProcess) GetBufferedTracepoints() []ebpf.RawUProbeParams {
panic("not implemented")
}

func initialize(dbp *nativeProcess) error { return nil }
func initialize(dbp *nativeProcess) (string, error) { return "", nil }
4 changes: 0 additions & 4 deletions pkg/proc/native/proc_freebsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@
#include <sys/mount.h>
#include <sys/queue.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <sys/types.h>

#include <libprocstat.h>
#include <libutil.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
Expand Down
22 changes: 20 additions & 2 deletions pkg/proc/native/proc_freebsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,12 @@ func Attach(pid int, debugInfoDirs []string) (*proc.TargetGroup, error) {
return tgt, nil
}

func initialize(dbp *nativeProcess) error {
func initialize(dbp *nativeProcess) (string, error) {
comm, _ := C.find_command_name(C.int(dbp.pid))
defer C.free(unsafe.Pointer(comm))
comm_str := C.GoString(comm)
dbp.os.comm = strings.ReplaceAll(string(comm_str), "%", "%%")
return nil
return getCmdLine(dbp.pid), nil
}

// kill kills the target process.
Expand Down Expand Up @@ -227,6 +227,24 @@ func findExecutable(path string, pid int) string {
return path
}

func getCmdLine(pid int) string {
ps := C.procstat_open_sysctl()
kp := C.kinfo_getproc(C.int(pid))
argv := C.procstat_getargv(ps, kp, 0)
goargv := []string{}
for {
arg := *argv
if arg == nil {
break
}
argv = (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(argv)) + unsafe.Sizeof(*argv)))
goargv = append(goargv, C.GoString(arg))
}
C.free(unsafe.Pointer(kp))
C.procstat_close(ps)
return strings.Join(goargv, " ")
}

func trapWait(procgrp *processGroup, pid int) (*nativeThread, error) {
return procgrp.procs[0].trapWaitInternal(pid, trapWaitNormal)
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/proc/native/proc_freebsd.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#include <sys/types.h>
#include <sys/user.h>
#include <libutil.h>
#include <libprocstat.h>

char * find_command_name(int pid);
char * find_executable(int pid);
Expand Down
42 changes: 30 additions & 12 deletions pkg/proc/native/proc_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func Attach(pid int, debugInfoDirs []string) (*proc.TargetGroup, error) {
return tgt, nil
}

func initialize(dbp *nativeProcess) error {
func initialize(dbp *nativeProcess) (string, error) {
comm, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/comm", dbp.pid))
if err == nil {
// removes newline character
Expand All @@ -179,22 +179,22 @@ func initialize(dbp *nativeProcess) error {
if comm == nil || len(comm) <= 0 {
stat, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", dbp.pid))
if err != nil {
return fmt.Errorf("could not read proc stat: %v", err)
return "", fmt.Errorf("could not read proc stat: %v", err)
}
expr := fmt.Sprintf("%d\\s*\\((.*)\\)", dbp.pid)
rexp, err := regexp.Compile(expr)
if err != nil {
return fmt.Errorf("regexp compile error: %v", err)
return "", fmt.Errorf("regexp compile error: %v", err)
}
match := rexp.FindSubmatch(stat)
if match == nil {
return fmt.Errorf("no match found using regexp '%s' in /proc/%d/stat", expr, dbp.pid)
return "", fmt.Errorf("no match found using regexp '%s' in /proc/%d/stat", expr, dbp.pid)
}
comm = match[1]
}
dbp.os.comm = strings.ReplaceAll(string(comm), "%", "%%")

return nil
return getCmdLine(dbp.pid), nil
}

func (dbp *nativeProcess) GetBufferedTracepoints() []ebpf.RawUProbeParams {
Expand Down Expand Up @@ -460,21 +460,25 @@ func trapWaitInternal(procgrp *processGroup, pid int, options trapWaitOptions) (
}
dbp = newChildProcess(procgrp.procs[0], wpid)
dbp.followExec = true
dbp.initializeBasic()
_, err := procgrp.add(dbp, dbp.pid, dbp.memthread, findExecutable("", dbp.pid), proc.StopLaunched)
cmdline, _ := dbp.initializeBasic()
tgt, err := procgrp.add(dbp, dbp.pid, dbp.memthread, findExecutable("", dbp.pid), proc.StopLaunched, cmdline)
if err != nil {
_ = dbp.Detach(false)
return nil, err
}
if halt {
return nil, nil
}
// TODO(aarzilli): if we want to give users the ability to stop the target
// group on exec here is where we should return
err = dbp.threads[dbp.pid].Continue()
if err != nil {
return nil, err
if tgt != nil {
// If tgt is nil we decided we are not interested in debugging this
// process, and we have already detached from it.
err = dbp.threads[dbp.pid].Continue()
if err != nil {
return nil, err
}
}
//TODO(aarzilli): if we want to give users the ability to stop the target
//group on exec here is where we should return
continue
}
if th == nil {
Expand Down Expand Up @@ -917,3 +921,17 @@ func (dbp *nativeProcess) FollowExec(v bool) error {
func killProcess(pid int) error {
return sys.Kill(pid, sys.SIGINT)
}

func getCmdLine(pid int) string {
buf, _ := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
args := strings.SplitN(string(buf), "\x00", -1)
for i := range args {
if strings.Contains(args[i], " ") {
args[i] = strconv.Quote(args[i])
}
}
if len(args) > 0 && args[len(args)-1] == "" {
args = args[:len(args)-1]
}
return strings.Join(args, " ")
}
Loading