Skip to content
This repository was archived by the owner on Oct 12, 2023. It is now read-only.

Support CollectSample + SetSamplingInterval on profiler #14

Open
wants to merge 1 commit into
base: gl-cpuprofiler
Choose a base branch
from
Open
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
33 changes: 23 additions & 10 deletions cpuprofiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,54 +14,67 @@ import (
)

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

// CPUProfiler is used to control CPU profiling.
func NewCPUProfiler(iso *Isolate) *CPUProfiler {
profiler := C.NewCPUProfiler(iso.ptr)
return &CPUProfiler{
p: profiler,
ptr: C.NewCPUProfiler(iso.ptr),
iso: iso,
}
}

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

C.CPUProfilerDispose(c.p)
c.p = nil
C.CPUProfilerDispose(c.ptr)
c.ptr = nil
}

// Changes default CPU profiler sampling interval to the specified number
// of microseconds. Default interval is 1000us. This method must be called
// when there are no profiles being recorded.
func (c *CPUProfiler) SetSamplingInterval(us int) {
C.CPUProfilerSetSamplingInterval(c.ptr, C.int32_t(us))
}

// 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) {
if c.p == nil || c.iso.ptr == nil {
if c.ptr == nil || c.iso.ptr == nil {
panic("profiler or isolate are nil")
}

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

C.CPUProfilerStartProfiling(c.p, tstr)
C.CPUProfilerStartProfiling(c.ptr, tstr)
}

// Synchronously collect current stack sample in all profilers attached to
// the isolate. The call does not affect number of ticks recorded for
// the current top node.
func (c *CPUProfiler) CollectSample() {
C.CPUProfilerCollectSample(c.ptr)
}

// 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 {
if c.ptr == nil || c.iso.ptr == nil {
panic("profiler or isolate are nil")
}

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

profile := C.CPUProfilerStopProfiling(c.p, tstr)
profile := C.CPUProfilerStopProfiling(c.ptr, tstr)

return &CPUProfile{
p: profile,
Expand Down
55 changes: 46 additions & 9 deletions cpuprofiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,48 @@ func TestCPUProfiler_Dispose(t *testing.T) {
}
}

func TestCPUProfiler_Sampling(t *testing.T) {
t.Parallel()

iso := v8.NewIsolate()
defer iso.Dispose()

cpuProfiler := v8.NewCPUProfiler(iso)
defer cpuProfiler.Dispose()

// Force sampling interval to be large so we know the sample is from CollectSample call
cpuProfiler.SetSamplingInterval(10000000)

title := "cpuprofilersamplingtest"
cpuProfiler.StartProfiling(title)

foo := v8.NewFunctionTemplate(iso, func(info *v8.FunctionCallbackInfo) *v8.Value {
cpuProfiler.CollectSample()
return nil
})
global := v8.NewObjectTemplate(iso)
global.Set("foo", foo)
ctx := v8.NewContext(iso, global)
ctx.RunScript("function one() { foo() };", "")
val, err := ctx.Global().Get("one")
fatalIf(t, err)
fn, err := val.AsFunction()
fatalIf(t, err)
_, err = fn.Call(ctx.Global())
fatalIf(t, err)

cpuProfile := cpuProfiler.StopProfiling(title)
defer cpuProfile.Delete()

root := cpuProfile.GetTopDownRoot()
if root == nil {
t.Fatal("expected profile top down root not to be nil")
}
if findChild(t, root, "one") == nil {
t.Fatal("expected sample to capture node from script")
}
}

func TestCPUProfiler(t *testing.T) {
t.Parallel()

Expand All @@ -58,15 +100,7 @@ func TestCPUProfiler(t *testing.T) {
title := "cpuprofilertest"
cpuProfiler.StartProfiling(title)

_, err := ctx.RunScript(profileScript, "script.js")
fatalIf(t, err)
val, err := ctx.Global().Get("start")
fatalIf(t, err)
fn, err := val.AsFunction()
fatalIf(t, err)
timeout, err := v8.NewValue(iso, int32(0))
fatalIf(t, err)
_, err = fn.Call(ctx.Global(), timeout)
_, err := ctx.RunScript("function noop() {}", "script.js")
fatalIf(t, err)

cpuProfile := cpuProfiler.StopProfiling(title)
Expand All @@ -75,6 +109,9 @@ func TestCPUProfiler(t *testing.T) {
if cpuProfile.GetTitle() != title {
t.Errorf("expected %s, but got %s", title, cpuProfile.GetTitle())
}
if cpuProfile.GetTopDownRoot() == nil {
t.Fatal("expected profile top down root not to be nil")
}
}

const profileScript = `function loop(timeout) {
Expand Down
8 changes: 8 additions & 0 deletions v8go.cc
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,14 @@ void CPUProfilerStartProfiling(CPUProfiler* profiler, const char* title) {
profiler->ptr->StartProfiling(title_str);
}

void CPUProfilerCollectSample(CPUProfiler* profiler) {
profiler->ptr->CollectSample(profiler->iso);
}

void CPUProfilerSetSamplingInterval(CPUProfiler* profiler, int us) {
profiler->ptr->SetSamplingInterval(us);
}

CPUProfileNode* NewCPUProfileNode(const CpuProfileNode* ptr_) {
int count = ptr_->GetChildrenCount();
CPUProfileNode** children = new CPUProfileNode*[count];
Expand Down
2 changes: 2 additions & 0 deletions v8go.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ extern IsolateHStatistics IsolationGetHeapStatistics(IsolatePtr ptr);

extern CPUProfiler* NewCPUProfiler(IsolatePtr iso_ptr);
extern void CPUProfilerDispose(CPUProfiler* ptr);
extern void CPUProfilerSetSamplingInterval(CPUProfiler* ptr, int us);
extern void CPUProfilerStartProfiling(CPUProfiler* ptr, const char* title);
extern void CPUProfilerCollectSample(CPUProfiler* ptr);
extern CPUProfile* CPUProfilerStopProfiling(CPUProfiler* ptr, const char* title);
extern void CPUProfileDelete(CPUProfile* ptr);

Expand Down