-
Notifications
You must be signed in to change notification settings - Fork 2.2k
pkg/proc: process spawned event #4171
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
base: master
Are you sure you want to change the base?
Changes from all commits
9d5daaf
85802ff
07972a1
27f66ee
27e08aa
a955866
24620f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| @echo off | ||
| echo Hello World |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| #!/bin/sh | ||
| echo "Hello World" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "os" | ||
| "os/exec" | ||
| "path/filepath" | ||
| "runtime" | ||
| ) | ||
|
|
||
| func main() { | ||
| file := "child.sh" | ||
| if runtime.GOOS == "windows" { | ||
| // This path is not actually exercised currently because prog_windows.go | ||
| // doesn't handle ErrBadBinaryInfo. | ||
| file = "child.bat" | ||
| } | ||
| file, err := filepath.Abs(file) | ||
| if err != nil { | ||
| panic(err) | ||
| } | ||
|
|
||
| output, err := exec.Command(file).Output() | ||
| if err != nil { | ||
| panic(err) | ||
| } | ||
| os.Stdout.Write(output) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -106,12 +106,40 @@ func Restart(grp, oldgrp *TargetGroup, discard func(*LogicalBreakpoint, error)) | |
| func (grp *TargetGroup) addTarget(p ProcessInternal, pid int, currentThread Thread, path string, stopReason StopReason, cmdline string) (*Target, error) { | ||
| logger := logflags.DebuggerLogger() | ||
| if len(grp.targets) > 0 { | ||
| willFollow := true | ||
| if !grp.followExecEnabled { | ||
| logger.Debugf("Detaching from child target (follow-exec disabled) %d %q", pid, cmdline) | ||
| return nil, nil | ||
| willFollow = false | ||
| } | ||
| if grp.followExecRegex != nil && !grp.followExecRegex.MatchString(cmdline) { | ||
| logger.Debugf("Detaching from child target (follow-exec regex not matched) %d %q", pid, cmdline) | ||
| willFollow = false | ||
| } | ||
|
|
||
| // Notify listeners that a child process was spawned. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you are doing this regardless just do it before the two previous ifs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I want to include |
||
| // | ||
| // We do this regardless of whether the process is debuggable and | ||
| // regardless of whether follow-exec is enabled; when run by an IDE in | ||
| // DAP mode, we want to give the IDE a chance to handle the spawned | ||
| // process. | ||
| // | ||
| // Defer the call so that - if the process _can_ be debugged - listeners | ||
| // are notified after the new target is set up. | ||
| if grp.Selected != nil { | ||
| if fn := grp.Selected.BinInfo().eventsFn; fn != nil { | ||
| defer fn(&Event{ | ||
| Kind: EventProcessSpawned, | ||
| ProcessSpawnedEventDetails: &ProcessSpawnedEventDetails{ | ||
| PID: pid, | ||
| ThreadID: currentThread.ThreadID(), | ||
| Cmdline: cmdline, | ||
| WillFollow: willFollow, | ||
| }, | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| if !willFollow { | ||
| return nil, nil | ||
| } | ||
| } | ||
|
|
@@ -576,6 +604,7 @@ type Event struct { | |
| Kind EventKind | ||
| *BinaryInfoDownloadEventDetails | ||
| *BreakpointMaterializedEventDetails | ||
| *ProcessSpawnedEventDetails | ||
| } | ||
|
|
||
| type EventKind uint8 | ||
|
|
@@ -585,6 +614,7 @@ const ( | |
| EventStopped | ||
| EventBinaryInfoDownload | ||
| EventBreakpointMaterialized | ||
| EventProcessSpawned | ||
| ) | ||
|
|
||
| // BinaryInfoDownloadEventDetails describes the details of a BinaryInfoDownloadEvent | ||
|
|
@@ -596,3 +626,11 @@ type BinaryInfoDownloadEventDetails struct { | |
| type BreakpointMaterializedEventDetails struct { | ||
| Breakpoint *LogicalBreakpoint | ||
| } | ||
|
|
||
| // ProcessSpawnedEventDetails describes the details of a ProcessSpawnedEvent | ||
| type ProcessSpawnedEventDetails struct { | ||
| PID int | ||
| ThreadID int | ||
| Cmdline string | ||
| WillFollow bool | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Continuing from #4171 (comment)
I still don't see how this is different from the follow-exec-disabled case. The calls to
Process.EntryPointorBinaryInfo.LoadBinaryInfoare literally the only difference between the follow-exec-disabled and unable-to-load-binary-info paths (excluding read-only operations) so unless those function calls somehow cause the process to be restarted I don't see how that's possible. Unless the follow-exec-disabled case is also restarting the process, but if that's the case then it's not caused by my changes.I was not able to reproduce the TestBadAttachRequest failure locally. Based on the stack traces of the exceptions, I am guessing the failure comes down to
TargetGroup.addTargetbeing called from other places, so I moved the "log and ignore" code here. Which means it's platform specific. I found what I believe to be the equivalent code for Windows so I added a TODO, but I was unable to identify an equivalent block of code for darwin or freebsd. And I don't want to make changes that I can't test myself.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see from
TestFollowExecthat follow-exec appears not to be implemented on darwin or freebsd, which explains why I couldn't find it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is the same but that path detaches from the child process, which restarts. Which means that by the time you see the process event that child will have made some progress executing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, are you saying "the process resumes execution and thus cannot be debugged from the entry point"? I misunderstood, I thought you were saying "the process is stopped and relaunched" or something along those lines. Though I suppose if the process hasn't executed any code there's not much of a difference.
I think this is just something I/we will have to live with. Having process events is better than not having process events, so I think this is worth implementing as-is. Maybe someone thinks up a better solution down the road. For my purposes, the child resuming is acceptable, because I need to use a non-ptrace debugger anyways, and if I use node's --inspect-brk flag then the part of the child I care about (the javascript, not the runtime) will be paused at entry anyways.
As far as handoff to another debugger, I found PTRACE_SEIZE which might be usable, but apparently it is "fragile and kernel-version dependent". Another thought that occurs to me is having delve act as a gdb rpc server and letting the caller connect that way. But that seems like something that needs a lot more planning before it would be worth considering.