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

Support to add a new suboption --follow-calls to trace subcommand #3594

Merged
merged 39 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
b6240a8
rebasing on master to implement --followcalls
archanaravindar Nov 7, 2023
dd623ef
in progress changes to enable --followcalls
archanaravindar Nov 7, 2023
4cfe9ff
rebase to master: modified function to add children to funcs array
archanaravindar Nov 8, 2023
17a15a3
modify main traversal loop
archanaravindar Nov 9, 2023
830feaa
added tests to check different scenarios
archanaravindar Nov 10, 2023
d798c46
added tests to check different scenarios
archanaravindar Nov 10, 2023
bce2a66
added tests to check different scenarios
archanaravindar Nov 10, 2023
bd4c3fc
add test to check for overlapping regular expression
archanaravindar Nov 10, 2023
53a4e23
modified type of strings array as a return only
archanaravindar Nov 15, 2023
cbc7280
changed depth to a simple integer instead of a global map
archanaravindar Nov 15, 2023
8310cdb
avoid calling traverse on recursive calls
archanaravindar Nov 15, 2023
fa29715
Added tests for various call graphs to test trace followfuncs
archanaravindar Nov 17, 2023
4639d98
Added tests for various call graphs to test trace followfuncs
archanaravindar Nov 17, 2023
3d73270
Added tests for various call graphs to test trace followfuncs
archanaravindar Nov 17, 2023
485e575
made auxillary changes for build to go through for new option follow-…
archanaravindar Nov 21, 2023
494ee89
Add support to print depth of the function calls as well
archanaravindar Nov 22, 2023
aea618c
Added two sample output files for checking
archanaravindar Nov 29, 2023
8cf9109
Bypass morestack_noctxt in output for verification testing
archanaravindar Dec 4, 2023
458e68c
Corrected newline error by adding newlines only if the line does not …
archanaravindar Dec 5, 2023
329be51
Added more tests
archanaravindar Dec 7, 2023
1003f86
Cleanup
archanaravindar Dec 7, 2023
e70a08f
Updated documentation
archanaravindar Dec 7, 2023
dccee6c
fixed error message in fmt.Errorf
archanaravindar Dec 8, 2023
39a9b84
Fixed result of Errorf not used error
archanaravindar Dec 10, 2023
d7b6c62
Addressing review comments to fix depth reporting and other issues
archanaravindar Jan 9, 2024
b2d6339
dont invoke stacktrace if tracefollowcalls is enabled, compute depth …
archanaravindar Jan 10, 2024
1a575c0
Addressing a part of review comments
archanaravindar Jan 19, 2024
d290e3e
Added changes to allow deferred functions to be picked up for tracing
archanaravindar Feb 1, 2024
014ee62
Fix issue to avoid printing stack for a simple trace option
archanaravindar Feb 7, 2024
47bc274
Moving most tests to integration2_test.go and keeping only one in dlv…
archanaravindar Feb 15, 2024
d42a35a
Moving most tests to integration2_test.go and keeping only one in dlv…
archanaravindar Feb 15, 2024
7f09ee9
Adding panic-defer test case
archanaravindar Feb 15, 2024
d1e805c
Moved rest of the tests to integration2_test.go
archanaravindar Feb 15, 2024
97f173c
addressing review comments: folding Functions and FunctionsDeep, redu…
archanaravindar Mar 7, 2024
ef01a06
Optimize traversal and parts of printing trace point function and mod…
archanaravindar Apr 25, 2024
4b7eb6c
Resolved error occurring due to staticcheck
archanaravindar Apr 25, 2024
5749f71
Implemented traversal algorithm using breadth first search
archanaravindar May 24, 2024
c21ce1b
Addressing review comments on the breadth first search implementation…
archanaravindar Jun 6, 2024
7de371b
Inline filterRuntimeFuncs and remove duplicate initialization
archanaravindar Jun 10, 2024
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 Documentation/cli/starlark.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ breakpoints(All) | Equivalent to API call [ListBreakpoints](https://godoc.org/gi
checkpoints() | Equivalent to API call [ListCheckpoints](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListCheckpoints)
dynamic_libraries() | Equivalent to API call [ListDynamicLibraries](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListDynamicLibraries)
function_args(Scope, Cfg) | Equivalent to API call [ListFunctionArgs](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListFunctionArgs)
functions(Filter) | Equivalent to API call [ListFunctions](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListFunctions)
functions(Filter, FollowCalls) | Equivalent to API call [ListFunctions](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListFunctions)
goroutines(Start, Count, Filters, GoroutineGroupingOptions, EvalScope) | Equivalent to API call [ListGoroutines](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListGoroutines)
local_vars(Scope, Cfg) | Equivalent to API call [ListLocalVars](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListLocalVars)
package_vars(Filter, Cfg) | Equivalent to API call [ListPackageVars](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListPackageVars)
Expand Down
17 changes: 9 additions & 8 deletions Documentation/usage/dlv_trace.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ dlv trace [package] regexp [flags]
### Options

```
--ebpf Trace using eBPF (experimental).
-e, --exec string Binary file to exec and trace.
-h, --help help for trace
--output string Output path for the binary.
-p, --pid int Pid to attach to.
-s, --stack int Show stack trace with given depth. (Ignored with --ebpf)
-t, --test Trace a test binary.
--timestamp Show timestamp in the output
--ebpf Trace using eBPF (experimental).
-e, --exec string Binary file to exec and trace.
--follow-calls int Trace all children of the function to the required depth
-h, --help help for trace
--output string Output path for the binary.
-p, --pid int Pid to attach to.
-s, --stack int Show stack trace with given depth. (Ignored with --ebpf)
-t, --test Trace a test binary.
--timestamp Show timestamp in the output
```

### Options inherited from parent commands
Expand Down
23 changes: 23 additions & 0 deletions _fixtures/leaf4.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import "fmt"

func D(i int) int {
return i * i * i
}
func C(i int) int {

return i + 20
}
func B(i int) int {
d := C(i) + 40
return d + D(i)
}
func A(i int) int {
return 10 + B(i)
}
func main() {
j := 0
j += A(2)
fmt.Println(j)
}
23 changes: 23 additions & 0 deletions _fixtures/leafcommon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import "fmt"

func D(i int) int {
return i * i * i
}
func C(i int) int {

return D(i+10) + 20
}
func B(i int) int {
return i * D(i)
}
func A(i int) int {
d := 10 + B(i)
return d + C(i)
}
func main() {
j := 0
j += A(2)
fmt.Println(j)
}
23 changes: 23 additions & 0 deletions _fixtures/leafindrec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import "fmt"

func B(i int) int {
if i > 0 {
return A(i - 1)
} else {
return 0
}
}
func A(n int) int {
if n <= 1 {
return n
} else {
return B(n - 3)
}
}
func main() {
j := 0
j += B(12)
fmt.Println(j)
}
17 changes: 17 additions & 0 deletions _fixtures/leafrec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import "fmt"

func A(i int, n int) int {
if n == 1 {
return i
} else {
n--
return (i * A(i-1, n))
}
}
func main() {
j := 0
j += A(5, 5)
fmt.Println(j)
}
23 changes: 23 additions & 0 deletions _fixtures/leafregex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import "fmt"

func callmed(i int) int {
return i * i * i
}
func callmee(i int) int {

return i + 20
}
func callme2(i int) int {
d := callmee(i) + 40
return d + callmed(i)
}
func callme(i int) int {
return 10 + callme2(i)
}
func main() {
j := 0
j += callme(2)
fmt.Println(j)
}
28 changes: 28 additions & 0 deletions _fixtures/panicex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package main

func F0() {
defer func() {
recover()
}()
F1()
}

func F1() {
F2()
}

func F2() {
F3()
}

func F3() {
F4()
}

func F4() {
panic("blah")
}

func main() {
F0()
}
81 changes: 81 additions & 0 deletions _fixtures/testtracefns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package main

import "fmt"

func D(i int) int {
return i * i * i
}
func C(i int) int {

return D(i+10) + 20
}
func B(i int) int {
return i * D(i)
}
func A(i int) int {
d := 10 + B(i)
return d + C(i)
}
func second(i int) int {
if i > 0 {
return first(i - 1)
} else {
return 0
}
}
func first(n int) int {
if n <= 1 {
return n
} else {
return second(n - 3)
}
}

func callmed(i int) int {
return i * i * i
}
func callmee(i int) int {

return i + 20
}
func callme2(i int) int {
d := callmee(i) + 40
return d + callmed(i)
}
func callme(i int) int {
return 10 + callme2(i)
}

func F0() {
defer func() {
recover()
}()
F1()
}

func F1() {
F2()
}

func F2() {
F3()
}

func F3() {
F4()
}

func F4() {
panic("blah")
}

func main() {
j := 0
j += A(2)

j += first(6)
j += callme(2)
fmt.Println(j)
F0()

}
40 changes: 28 additions & 12 deletions cmd/dlv/cmds/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ var (
traceStackDepth int
traceUseEBPF bool
traceShowTimestamp bool
traceFollowCalls int

// redirect specifications for target process
redirects []string
Expand Down Expand Up @@ -363,6 +364,7 @@ only see the output of the trace operations you can redirect stdout.`,
must(traceCommand.RegisterFlagCompletionFunc("stack", cobra.NoFileCompletions))
traceCommand.Flags().String("output", "", "Output path for the binary.")
must(traceCommand.MarkFlagFilename("output"))
traceCommand.Flags().IntVarP(&traceFollowCalls, "follow-calls", "", 0, "Trace all children of the function to the required depth")
rootCommand.AddCommand(traceCommand)

coreCommand := &cobra.Command{
Expand Down Expand Up @@ -702,6 +704,10 @@ func traceCmd(cmd *cobra.Command, args []string, conf *config.Config) int {

processArgs = append([]string{debugname}, targetArgs...)
}
if dlvArgsLen >= 3 && traceFollowCalls <= 0 {
fmt.Fprintln(os.Stderr, "Need to specify a trace depth of atleast 1")
aarzilli marked this conversation as resolved.
Show resolved Hide resolved
return 1
}

// Make a local in-memory connection that client and server use to communicate
listener, clientConn := service.ListenerPipe()
Expand Down Expand Up @@ -738,8 +744,7 @@ func traceCmd(cmd *cobra.Command, args []string, conf *config.Config) int {
<-ch
client.Halt()
}()

funcs, err := client.ListFunctions(regexp)
funcs, err := client.ListFunctions(regexp, traceFollowCalls)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
Expand All @@ -755,13 +760,22 @@ func traceCmd(cmd *cobra.Command, args []string, conf *config.Config) int {
}
} else {
// Fall back to breakpoint based tracing if we get an error.
var stackdepth int
// Default size of stackdepth to trace function calls and descendants=20
stackdepth = traceStackDepth
if traceFollowCalls > 0 && stackdepth == 0 {
stackdepth = 20
}
archanaravindar marked this conversation as resolved.
Show resolved Hide resolved
_, err = client.CreateBreakpoint(&api.Breakpoint{
FunctionName: funcs[i],
Tracepoint: true,
Line: -1,
Stacktrace: traceStackDepth,
LoadArgs: &terminal.ShortLoadConfig,
FunctionName: funcs[i],
Tracepoint: true,
Line: -1,
Stacktrace: stackdepth,
LoadArgs: &terminal.ShortLoadConfig,
TraceFollowCalls: traceFollowCalls,
RootFuncName: regexp,
})

if err != nil && !isBreakpointExistsErr(err) {
fmt.Fprintf(os.Stderr, "unable to set tracepoint on function %s: %#v\n", funcs[i], err)
continue
Expand All @@ -775,11 +789,13 @@ func traceCmd(cmd *cobra.Command, args []string, conf *config.Config) int {
}
for i := range addrs {
_, err = client.CreateBreakpoint(&api.Breakpoint{
Addr: addrs[i],
TraceReturn: true,
Stacktrace: traceStackDepth,
Line: -1,
LoadArgs: &terminal.ShortLoadConfig,
Addr: addrs[i],
TraceReturn: true,
Stacktrace: stackdepth,
Line: -1,
LoadArgs: &terminal.ShortLoadConfig,
TraceFollowCalls: traceFollowCalls,
RootFuncName: regexp,
})
if err != nil && !isBreakpointExistsErr(err) {
fmt.Fprintf(os.Stderr, "unable to set tracepoint on function %s: %#v\n", funcs[i], err)
Expand Down
33 changes: 33 additions & 0 deletions cmd/dlv/dlv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,39 @@ func TestTrace2(t *testing.T) {
assertNoError(cmd.Wait(), t, "cmd.Wait()")
}

func TestTraceDirRecursion(t *testing.T) {
dlvbin := getDlvBin(t)

expected := []byte("> goroutine(1):frame(1) main.A(5, 5)\n > goroutine(1):frame(2) main.A(4, 4)\n > goroutine(1):frame(3) main.A(3, 3)\n > goroutine(1):frame(4) main.A(2, 2)\n > goroutine(1):frame(5) main.A(1, 1)\n >> goroutine(1):frame(5) main.A => (1)\n >> goroutine(1):frame(4) main.A => (2)\n >> goroutine(1):frame(3) main.A => (6)\n >> goroutine(1):frame(2) main.A => (24)\n>> goroutine(1):frame(1) main.A => (120)\n")

fixtures := protest.FindFixturesDir()
cmd := exec.Command(dlvbin, "trace", "--output", filepath.Join(t.TempDir(), "__debug"), filepath.Join(fixtures, "leafrec.go"), "main.A", "--follow-calls", "4")
rdr, err := cmd.StderrPipe()
assertNoError(err, t, "stderr pipe")
defer rdr.Close()

cmd.Dir = filepath.Join(fixtures, "buildtest")

assertNoError(cmd.Start(), t, "running trace")
// Parse output to ignore calls to morestack_noctxt for comparison
scan := bufio.NewScanner(rdr)
text := ""
outputtext := ""
for scan.Scan() {
text = scan.Text()
if !strings.Contains(text, "morestack_noctxt") {
outputtext += text
outputtext += "\n"
}
}
output := []byte(outputtext)

if !bytes.Contains(output, expected) {
t.Fatalf("expected:\n%s\ngot:\n%s", string(expected), string(output))
}
assertNoError(cmd.Wait(), t, "cmd.Wait()")
}

func TestTraceMultipleGoroutines(t *testing.T) {
dlvbin := getDlvBin(t)

Expand Down
Loading