Skip to content

Commit

Permalink
Start/End Time on profile + ScriptResourceName on node
Browse files Browse the repository at this point in the history
  • Loading branch information
Genevieve L'Esperance committed Oct 8, 2021
1 parent 807e49e commit 38767cb
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 63 deletions.
16 changes: 16 additions & 0 deletions cpuprofile.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package v8go
// #include <stdlib.h>
// #include "v8go.h"
import "C"
import "time"

// CPUProfile contains a CPU profile in a form of top-down call tree
// (from main() down to functions that do all the work).
Expand All @@ -16,6 +17,8 @@ type CPUProfile struct {

title string
topDownRoot *CPUProfileNode
startTime time.Time
endTime time.Time
}

// Returns the root node of the top down call tree.
Expand All @@ -28,6 +31,19 @@ func (c *CPUProfile) GetTitle() string {
return c.title
}

// Returns time when the profile recording was started (in microseconds)
// since some unspecified starting point.
func (c *CPUProfile) GetStartTime() time.Time {
return c.startTime
}

// Returns time when the profile recording was stopped (in microseconds)
// since some unspecified starting point.
// The point is equal to the starting point used by GetStartTime.
func (c *CPUProfile) GetEndTime() time.Time {
return c.endTime
}

func (c *CPUProfile) Delete() {
if c.ptr == nil {
return
Expand Down
10 changes: 9 additions & 1 deletion cpuprofile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package v8go_test

import (
"testing"
"time"

v8 "rogchap.com/v8go"
)
Expand Down Expand Up @@ -39,9 +40,12 @@ func TestCPUProfile(t *testing.T) {

cpuProfiler.StartProfiling("cpuprofiletest")

_, err := ctx.RunScript(`function foo() {}; foo();`, "script.js")
_, err := ctx.RunScript(`function foo() { }; foo();`, "script.js")
fatalIf(t, err)

// Ensure different start/end time
time.Sleep(10 * time.Microsecond)

cpuProfile := cpuProfiler.StopProfiling("cpuprofiletest")
if cpuProfile == nil {
t.Fatal("expected profiler not to be nil")
Expand All @@ -55,4 +59,8 @@ func TestCPUProfile(t *testing.T) {
if cpuProfile.GetTopDownRoot() == nil {
t.Fatal("expected root not to be nil")
}

if cpuProfile.GetStartTime().Equal(cpuProfile.GetEndTime()) {
t.Fatal("expected different start and end times")
}
}
27 changes: 19 additions & 8 deletions cpuprofilenode.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@ import "C"

// CPUProfileNode represents a node in a call graph.
type CPUProfileNode struct {
functionName string
lineNumber int
columnNumber int
children []*CPUProfileNode
}
scriptResourceName string
functionName string
lineNumber int
columnNumber int

// Returns function name (empty string for anonymous functions.)
func (c *CPUProfileNode) GetFunctionName() string {
return c.functionName
parent *CPUProfileNode
children []*CPUProfileNode
}

// Retrieves number of children.
Expand All @@ -34,6 +32,19 @@ func (c *CPUProfileNode) GetChild(index int) *CPUProfileNode {
return c.children[index]
}

func (c *CPUProfileNode) GetParent() *CPUProfileNode {
return c.parent
}

func (c *CPUProfileNode) GetScriptResourceName() string {
return c.scriptResourceName
}

// Returns function name (empty string for anonymous functions.)
func (c *CPUProfileNode) GetFunctionName() string {
return c.functionName
}

// Returns the number, 1-based, of the line where the function originates.
func (c *CPUProfileNode) GetLineNumber() int {
return c.lineNumber
Expand Down
24 changes: 13 additions & 11 deletions cpuprofilenode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func TestCPUProfileNode(t *testing.T) {

cpuProfiler.StartProfiling("cpuprofilenodetest")

_, _ = ctx.RunScript(profileScript, "")
_, _ = ctx.RunScript(profileScript, "script.js")
val, _ := ctx.Global().Get("start")
fn, _ := val.AsFunction()
_, _ = fn.Call(ctx.Global())
Expand All @@ -32,17 +32,11 @@ func TestCPUProfileNode(t *testing.T) {
defer cpuProfile.Delete()

node := cpuProfile.GetTopDownRoot()
err := checkNode(node, "(root)", 0, 0)
fatalIf(t, err)

if node.GetFunctionName() != "(root)" {
t.Fatalf("expected start but got %s", node.GetFunctionName())
}

if node.GetLineNumber() != 0 {
t.Fatalf("expected 0 but got %d", node.GetLineNumber())
}

if node.GetColumnNumber() != 0 {
t.Fatalf("expected 0 but got %d", node.GetColumnNumber())
if node.GetParent() != nil {
t.Fatal("expected root node to have nil parent")
}

if node.GetChildrenCount() < 2 {
Expand All @@ -52,4 +46,12 @@ func TestCPUProfileNode(t *testing.T) {
if node.GetChild(1).GetFunctionName() != "start" {
t.Fatalf("expected child node with name `start` but got %s", node.GetChild(1).GetFunctionName())
}

if node.GetChild(1).GetScriptResourceName() != "script.js" {
t.Fatalf("expected child to have script resource name `script.js` but had `%s`", node.GetScriptResourceName())
}

if node.GetChild(0).GetParent() != node {
t.Fatal("expected child's parent to be the same node")
}
}
36 changes: 20 additions & 16 deletions cpuprofiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package v8go
// #include "v8go.h"
import "C"
import (
"time"
"unsafe"
)

Expand Down Expand Up @@ -62,41 +63,44 @@ func (c *CPUProfiler) StopProfiling(title string) *CPUProfile {

rootPtr := C.CpuProfileGetTopDownRoot(profilePtr)
rootNode := &CPUProfileNode{
functionName: C.GoString(C.CpuProfileNodeGetFunctionName(rootPtr)),
lineNumber: int(C.CpuProfileNodeGetLineNumber(rootPtr)),
columnNumber: int(C.CpuProfileNodeGetColumnNumber(rootPtr)),
parent: nil,
scriptResourceName: C.GoString(C.CpuProfileNodeGetScriptResourceName(rootPtr)),
functionName: C.GoString(C.CpuProfileNodeGetFunctionName(rootPtr)),
lineNumber: int(C.CpuProfileNodeGetLineNumber(rootPtr)),
columnNumber: int(C.CpuProfileNodeGetColumnNumber(rootPtr)),
}
rootNode.children = getChildren(rootPtr)
rootNode.children = getChildren(rootNode, rootPtr)

return &CPUProfile{
ptr: profilePtr,
iso: c.iso,
title: title,
topDownRoot: rootNode,
startTime: time.Unix(0, int64(C.CpuProfileGetStartTime(profilePtr))/1000),
endTime: time.Unix(0, int64(C.CpuProfileGetEndTime(profilePtr))/1000),
}
}

func getChildren(ptr C.CpuProfileNodePtr) []*CPUProfileNode {
func getChildren(parent *CPUProfileNode, ptr C.CpuProfileNodePtr) []*CPUProfileNode {
count := C.CpuProfileNodeGetChildrenCount(ptr)
if int(count) == 0 {
return []*CPUProfileNode{}
}

node := &CPUProfileNode{
children: make([]*CPUProfileNode, count),
}
parent.children = make([]*CPUProfileNode, count)

for i := 0; i < int(count); i++ {
childNodePtr := C.CpuProfileNodeGetChild(ptr, C.int(i))

children := getChildren(childNodePtr)

node.children[i] = &CPUProfileNode{
functionName: C.GoString(C.CpuProfileNodeGetFunctionName(childNodePtr)),
lineNumber: int(C.CpuProfileNodeGetLineNumber(childNodePtr)),
columnNumber: int(C.CpuProfileNodeGetColumnNumber(childNodePtr)),
children: children,
childNode := &CPUProfileNode{
parent: parent,
scriptResourceName: C.GoString(C.CpuProfileNodeGetScriptResourceName(childNodePtr)),
functionName: C.GoString(C.CpuProfileNodeGetFunctionName(childNodePtr)),
lineNumber: int(C.CpuProfileNodeGetLineNumber(childNodePtr)),
columnNumber: int(C.CpuProfileNodeGetColumnNumber(childNodePtr)),
}
childNode.children = getChildren(childNode, childNodePtr)
parent.children[i] = childNode
}
return node.children
return parent.children
}
56 changes: 31 additions & 25 deletions cpuprofiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package v8go_test

import (
"fmt"
"testing"

"rogchap.com/v8go"
Expand Down Expand Up @@ -34,7 +35,7 @@ func TestCPUProfilerDispose(t *testing.T) {
}

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

ctx := v8go.NewContext(nil)
iso := ctx.Isolate()
Expand All @@ -61,63 +62,68 @@ func TestCPUProfiler(t *testing.T) {
}
defer cpuProfile.Delete()

if cpuProfile.GetTitle() != "cpuprofilertest" {
t.Fatalf("expected cpu profile to be %s, but got %s", "cpuprofilertest", cpuProfile.GetTitle())
}

root := cpuProfile.GetTopDownRoot()
if root == nil {
t.Fatal("expected root not to be nil")
}
if root.GetFunctionName() != "(root)" {
t.Fatalf("expected (root), but got %v", root.GetFunctionName())
}
checkNode(t, root, "(root)", 0, 0)
checkChildren(t, root, []string{"(program)", "start", "(garbage collector)"})
err = checkNode(root, "(root)", 0, 0)
fatalIf(t, err)
err = checkChildren(root, []string{"(program)", "start", "(garbage collector)"})
fatalIf(t, err)

start := root.GetChild(1)
checkNode(t, start, "start", 23, 15)
checkChildren(t, start, []string{"foo"})
err = checkNode(start, "start", 23, 15)
fatalIf(t, err)
err = checkChildren(start, []string{"foo"})
fatalIf(t, err)

foo := start.GetChild(0)
checkNode(t, foo, "foo", 15, 13)
checkChildren(t, foo, []string{"delay", "bar", "baz"})
err = checkNode(foo, "foo", 15, 13)
fatalIf(t, err)
err = checkChildren(foo, []string{"delay", "bar", "baz"})
fatalIf(t, err)

baz := foo.GetChild(2)
checkNode(t, baz, "baz", 14, 13)
checkChildren(t, baz, []string{"delay"})
err = checkNode(baz, "baz", 14, 13)
fatalIf(t, err)
err = checkChildren(baz, []string{"delay"})
fatalIf(t, err)

delay := baz.GetChild(0)
checkNode(t, delay, "delay", 12, 15)
checkChildren(t, delay, []string{"loop"})
err = checkNode(delay, "delay", 12, 15)
fatalIf(t, err)
err = checkChildren(delay, []string{"loop"})
fatalIf(t, err)
}

func checkChildren(t *testing.T, node *v8go.CPUProfileNode, names []string) {
func checkChildren(node *v8go.CPUProfileNode, names []string) error {
nodeName := node.GetFunctionName()
if node.GetChildrenCount() != len(names) {
present := []string{}
for i := 0; i < node.GetChildrenCount(); i++ {
present = append(present, node.GetChild(i).GetFunctionName())
}
t.Fatalf("child count for node %s should be %d but was %d: %v", nodeName, len(names), node.GetChildrenCount(), present)
return fmt.Errorf("child count for node %s should be %d but was %d: %v", nodeName, len(names), node.GetChildrenCount(), present)
}
for i, n := range names {
if node.GetChild(i).GetFunctionName() != n {
t.Fatalf("expected %s child %d to have name %s", nodeName, i, n)
return fmt.Errorf("expected %s child %d to have name %s", nodeName, i, n)
}
}
return nil
}

func checkNode(t *testing.T, node *v8go.CPUProfileNode, name string, line, column int) {
func checkNode(node *v8go.CPUProfileNode, name string, line, column int) error {
if node.GetFunctionName() != name {
t.Fatalf("expected node to have function name `%s` but had `%s`", name, node.GetFunctionName())
return fmt.Errorf("expected node to have function name `%s` but had `%s`", name, node.GetFunctionName())
}
if node.GetLineNumber() != line {
t.Fatalf("expected node %s at line %d, but got %d", name, line, node.GetLineNumber())
return fmt.Errorf("expected node %s at line %d, but got %d", name, line, node.GetLineNumber())
}
if node.GetColumnNumber() != column {
t.Fatalf("expected node %s at column %d, but got %d", name, column, node.GetColumnNumber())
return fmt.Errorf("expected node %s at column %d, but got %d", name, column, node.GetColumnNumber())
}
return nil
}

// const profileTree = `
Expand Down
15 changes: 15 additions & 0 deletions v8go.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ struct m_cpuProfile {
struct m_cpuProfileNode {
const CpuProfileNode* ptr;
const char* functionName;
const char* scriptResourceName;
int lineNumber;
int columnNumber;
int childrenCount;
Expand Down Expand Up @@ -272,6 +273,7 @@ CpuProfilePtr CpuProfilerStopProfiling(IsolatePtr iso_ptr, CpuProfilerPtr cpuPro
m_cpuProfileNode* root = new m_cpuProfileNode;
root->ptr = r;
root->childrenCount = r->GetChildrenCount();
root->scriptResourceName = r->GetScriptResourceNameStr();
root->functionName = r->GetFunctionNameStr();
root->lineNumber = r->GetLineNumber();
root->columnNumber = r->GetColumnNumber();
Expand All @@ -287,10 +289,22 @@ CpuProfileNodePtr CpuProfileGetTopDownRoot(CpuProfilePtr ptr) {
return ptr->root;
}

int CpuProfileGetStartTime(CpuProfilePtr cpuProfilePtr) {
return cpuProfilePtr->ptr->GetStartTime();
}

int CpuProfileGetEndTime(CpuProfilePtr cpuProfilePtr) {
return cpuProfilePtr->ptr->GetEndTime();
}

int CpuProfileNodeGetChildrenCount(CpuProfileNodePtr ptr) {
return ptr->childrenCount;
}

const char* CpuProfileNodeGetScriptResourceName(CpuProfileNodePtr ptr) {
return ptr->scriptResourceName;
}

const char* CpuProfileNodeGetFunctionName(CpuProfileNodePtr ptr) {
return ptr->functionName;
}
Expand All @@ -307,6 +321,7 @@ CpuProfileNodePtr CpuProfileNodeGetChild(CpuProfileNodePtr cpuProfileNode, int i
const CpuProfileNode* child = cpuProfileNode->ptr->GetChild(index);
m_cpuProfileNode* c = new m_cpuProfileNode;
c->ptr = child;
c->scriptResourceName = child->GetScriptResourceNameStr();
c->functionName = child->GetFunctionNameStr();
c->lineNumber = child->GetLineNumber();
c->columnNumber = child->GetColumnNumber();
Expand Down
Loading

0 comments on commit 38767cb

Please sign in to comment.