xref: /aosp_15_r20/external/starlark-go/starlark/profile_test.go (revision 4947cdc739c985f6d86941e22894f5cefe7c9e9a)
1*4947cdc7SCole Faust// Copyright 2019 The Bazel Authors. All rights reserved.
2*4947cdc7SCole Faust// Use of this source code is governed by a BSD-style
3*4947cdc7SCole Faust// license that can be found in the LICENSE file.
4*4947cdc7SCole Faust
5*4947cdc7SCole Faustpackage starlark_test
6*4947cdc7SCole Faust
7*4947cdc7SCole Faustimport (
8*4947cdc7SCole Faust	"bytes"
9*4947cdc7SCole Faust	"fmt"
10*4947cdc7SCole Faust	"io/ioutil"
11*4947cdc7SCole Faust	"os"
12*4947cdc7SCole Faust	"os/exec"
13*4947cdc7SCole Faust	"strings"
14*4947cdc7SCole Faust	"testing"
15*4947cdc7SCole Faust
16*4947cdc7SCole Faust	"go.starlark.net/starlark"
17*4947cdc7SCole Faust)
18*4947cdc7SCole Faust
19*4947cdc7SCole Faust// TestProfile is a simple integration test that the profiler
20*4947cdc7SCole Faust// emits minimally plausible pprof-compatible output.
21*4947cdc7SCole Faustfunc TestProfile(t *testing.T) {
22*4947cdc7SCole Faust	prof, err := ioutil.TempFile("", "profile_test")
23*4947cdc7SCole Faust	if err != nil {
24*4947cdc7SCole Faust		t.Fatal(err)
25*4947cdc7SCole Faust	}
26*4947cdc7SCole Faust	defer prof.Close()
27*4947cdc7SCole Faust	defer os.Remove(prof.Name())
28*4947cdc7SCole Faust	if err := starlark.StartProfile(prof); err != nil {
29*4947cdc7SCole Faust		t.Fatal(err)
30*4947cdc7SCole Faust	}
31*4947cdc7SCole Faust
32*4947cdc7SCole Faust	const src = `
33*4947cdc7SCole Faustdef fibonacci(n):
34*4947cdc7SCole Faust	res = list(range(n))
35*4947cdc7SCole Faust	for i in res[2:]:
36*4947cdc7SCole Faust		res[i] = res[i-2] + res[i-1]
37*4947cdc7SCole Faust	return res
38*4947cdc7SCole Faust
39*4947cdc7SCole Faustfibonacci(100000)
40*4947cdc7SCole Faust`
41*4947cdc7SCole Faust
42*4947cdc7SCole Faust	thread := new(starlark.Thread)
43*4947cdc7SCole Faust	if _, err := starlark.ExecFile(thread, "foo.star", src, nil); err != nil {
44*4947cdc7SCole Faust		_ = starlark.StopProfile()
45*4947cdc7SCole Faust		t.Fatal(err)
46*4947cdc7SCole Faust	}
47*4947cdc7SCole Faust	if err := starlark.StopProfile(); err != nil {
48*4947cdc7SCole Faust		t.Fatal(err)
49*4947cdc7SCole Faust	}
50*4947cdc7SCole Faust	prof.Sync()
51*4947cdc7SCole Faust	cmd := exec.Command("go", "tool", "pprof", "-top", prof.Name())
52*4947cdc7SCole Faust	cmd.Stderr = new(bytes.Buffer)
53*4947cdc7SCole Faust	cmd.Stdout = new(bytes.Buffer)
54*4947cdc7SCole Faust	if err := cmd.Run(); err != nil {
55*4947cdc7SCole Faust		t.Fatalf("pprof failed: %v; output=<<%s>>", err, cmd.Stderr)
56*4947cdc7SCole Faust	}
57*4947cdc7SCole Faust
58*4947cdc7SCole Faust	// Typical output (may vary by go release):
59*4947cdc7SCole Faust	//
60*4947cdc7SCole Faust	// Type: wall
61*4947cdc7SCole Faust	// Time: Apr 4, 2019 at 11:10am (EDT)
62*4947cdc7SCole Faust	// Duration: 251.62ms, Total samples = 250ms (99.36%)
63*4947cdc7SCole Faust	// Showing nodes accounting for 250ms, 100% of 250ms total
64*4947cdc7SCole Faust	//  flat  flat%   sum%        cum   cum%
65*4947cdc7SCole Faust	// 320ms   100%   100%      320ms   100%  fibonacci
66*4947cdc7SCole Faust	//     0     0%   100%      320ms   100%  foo.star
67*4947cdc7SCole Faust	//
68*4947cdc7SCole Faust	// We'll assert a few key substrings are present.
69*4947cdc7SCole Faust	got := fmt.Sprint(cmd.Stdout)
70*4947cdc7SCole Faust	for _, want := range []string{
71*4947cdc7SCole Faust		"flat%",
72*4947cdc7SCole Faust		"fibonacci",
73*4947cdc7SCole Faust		"foo.star",
74*4947cdc7SCole Faust	} {
75*4947cdc7SCole Faust		if !strings.Contains(got, want) {
76*4947cdc7SCole Faust			t.Errorf("output did not contain %q", want)
77*4947cdc7SCole Faust		}
78*4947cdc7SCole Faust	}
79*4947cdc7SCole Faust	if t.Failed() {
80*4947cdc7SCole Faust		t.Logf("stderr=%v", cmd.Stderr)
81*4947cdc7SCole Faust		t.Logf("stdout=%v", cmd.Stdout)
82*4947cdc7SCole Faust	}
83*4947cdc7SCole Faust}
84