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

Introduce CpuProfiler, CpuProfile, and CpuProfileNode #167

Merged
merged 25 commits into from
Oct 20, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8a0068f
Introduce CpuProfiler, CpuProfile, CpuProfileNode
Sep 4, 2021
65c45df
Split out cpu profile components, add tests
Sep 6, 2021
3252318
Merge branch 'master' into gl-cpuprofiler
Sep 8, 2021
8b2f3d5
Separate profile names, test invalid child index
Sep 8, 2021
00ee59b
Add method comments
Sep 8, 2021
ed9a5f0
Test no-op for subsequent disposes of cpu profiler
Sep 8, 2021
0d14d14
Verify is profiler or iso is nil within start/stop functions
Sep 30, 2021
06e7540
Update copyright year on new files.
Sep 30, 2021
b90c5e1
Merge branch 'master' into gl-cpuprofiler
Oct 1, 2021
1a5a4d9
Introduce cpu profiler, cpu profile, cpu profile node
Oct 2, 2021
7e1cd9b
First-pass implementation of CpuProfiler
ryanmoran Oct 8, 2021
bf396fb
Expand example in readme
Oct 12, 2021
7c70b11
Adjust script run time to ensure sampling
Oct 13, 2021
ad3270d
Remove unneeded unsafe pointer casts, fix time conversion, panic on nil
Oct 19, 2021
0026131
Merge remote-tracking branch 'rogchap/master' into gl-cpuprofiler
Oct 19, 2021
08fe180
Panic on nil ptr/iso in start/stop profiling
Oct 19, 2021
bc240f1
Add backports for timeUnixMicro, add comments to new objects fields
Oct 19, 2021
76505ff
Getter methods for cpu profile + cpu profile node
Oct 19, 2021
2794acf
Update Readme, add GetChildrenCount to node
Oct 19, 2021
3bd713f
Find start node on root, ignore program/garbage collector children
Oct 19, 2021
acd0219
Specify timeout for script + loosen search on children
Oct 19, 2021
6646a0d
Update readme instructions for profiler
Oct 20, 2021
439ebb1
Update changelog
Oct 20, 2021
755a126
Fix reversing of order of profile start and end time
dylanahsmith Oct 20, 2021
946873e
Expose profile duration instead of start and end time.
dylanahsmith Oct 20, 2021
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
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ go func() {

select {
case val := <- vals:
// sucess
// success
case err := <- errs:
// javascript error
case <- time.After(200 * time.Milliseconds):
Expand All @@ -111,6 +111,47 @@ case <- time.After(200 * time.Milliseconds):
}
```

### CPU Profiler

```
func createProfile() {
cpuProfiler := v8.NewCPUProfiler(v8.NewIsolate()) // create a new profiler
cpuProfiler.StartProfiling("my-profile") // start profiling

ctx.RunScript(src, filename) # src is the profile script from cpuprofiler_test.go

cpuProfile := cpuProfiler.StopProfiling("my-profile") // stop profiling returns a cpu profile

printTree("", cpuProfile.Root) // a helper function for printing the tree
}

func printTree(nest string, node *v8.CPUProfileNode) {
fmt.Printf("%s%s %s:%d:%d\n", nest, node.FunctionName, node.ScriptResourceName, node.LineNumber, node.ColumnNumber)
if len(node.Children) == 0 {
return
}
nest = fmt.Sprintf("%s ", nest)
for _, c := range node.Children {
printTree(nest, c)
}
}

<!-- Top Down Root -->
<!-- (root) :0:0 -->
<!-- (program) :0:0 -->
<!-- start script.js:23:15 -->
<!-- foo script.js:15:13 -->
<!-- delay script.js:12:15 -->
<!-- loop script.js:1:14 -->
<!-- bar script.js:13:13 -->
<!-- delay script.js:12:15 -->
<!-- loop script.js:1:14 -->
<!-- baz script.js:14:13 -->
<!-- delay script.js:12:15 -->
<!-- loop script.js:1:14 -->
<!-- (garbage collector) :0:0 -->
```

## Documentation

Go Reference & more examples: https://pkg.go.dev/rogchap.com/v8go
Expand Down
122 changes: 122 additions & 0 deletions cpuprofiler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright 2021 Roger Chapman and the v8go contributors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package v8go

/*
#include <stdlib.h>
#include "v8go.h"
*/
import "C"
import (
"time"
"unsafe"
)

type CPUProfiler struct {
p *C.CPUProfiler
iso *Isolate
}

// CPUProfiler is used to control CPU profiling.
func NewCPUProfiler(iso *Isolate) *CPUProfiler {
profiler := C.NewCPUProfiler(iso.ptr)
return &CPUProfiler{
p: (*C.CPUProfiler)(unsafe.Pointer(profiler)),
genevieve marked this conversation as resolved.
Show resolved Hide resolved
iso: iso,
}
}

// Dispose will dispose the profiler.
func (c *CPUProfiler) Dispose() {
if c.p == nil {
return
}

C.CPUProfilerDispose((*C.CPUProfiler)(unsafe.Pointer(c.p)))
c.p = nil
}

// StartProfiling starts collecting a CPU profile. Title may be an empty string. Several
// profiles may be collected at once. Attempts to start collecting several
// profiles with the same title are silently ignored.
func (c *CPUProfiler) StartProfiling(title string) {
genevieve marked this conversation as resolved.
Show resolved Hide resolved
if c.p == nil || c.iso.ptr == nil {
return
genevieve marked this conversation as resolved.
Show resolved Hide resolved
}

tstr := C.CString(title)
defer C.free(unsafe.Pointer(tstr))

C.CPUProfilerStartProfiling((*C.CPUProfiler)(unsafe.Pointer(c.p)), tstr)
}

// Stops collecting CPU profile with a given title and returns it.
// If the title given is empty, finishes the last profile started.
func (c *CPUProfiler) StopProfiling(title string) *CPUProfile {
if c.p == nil || c.iso.ptr == nil {
return nil
}

tstr := C.CString(title)
defer C.free(unsafe.Pointer(tstr))

profile := C.CPUProfilerStopProfiling((*C.CPUProfiler)(unsafe.Pointer(c.p)), tstr)
p := (*C.CPUProfile)(unsafe.Pointer(profile))

return &CPUProfile{
p: p,
Title: C.GoString(p.title),
Root: NewCPUProfileNode(p.root, nil),
StartTime: time.Unix(0, int64(p.startTime)/1000),
EndTime: time.Unix(0, int64(p.endTime)/1000),
genevieve marked this conversation as resolved.
Show resolved Hide resolved
}
}

type CPUProfile struct {
p *C.CPUProfile

Title string
Root *CPUProfileNode
StartTime time.Time
EndTime time.Time
}

type CPUProfileNode struct {
p *C.CPUProfileNode

ScriptResourceName string
FunctionName string
LineNumber int
ColumnNumber int
Children []*CPUProfileNode
Parent *CPUProfileNode
genevieve marked this conversation as resolved.
Show resolved Hide resolved
}

func NewCPUProfileNode(node *C.CPUProfileNode, parent *CPUProfileNode) *CPUProfileNode {
genevieve marked this conversation as resolved.
Show resolved Hide resolved
n := &CPUProfileNode{
p: node,
ScriptResourceName: C.GoString(node.scriptResourceName),
FunctionName: C.GoString(node.functionName),
LineNumber: int(node.lineNumber),
ColumnNumber: int(node.columnNumber),
Parent: parent,
}

if node.childrenCount > 0 {
for _, child := range (*[1 << 28]*C.CPUProfileNode)(unsafe.Pointer(node.children))[:node.childrenCount:node.childrenCount] {
n.Children = append(n.Children, NewCPUProfileNode(child, n))
}
}

return n
}

func (c *CPUProfile) Delete() {
if c.p == nil {
return
}
C.CPUProfileDelete(c.p)
c.p = nil
}
Loading