Skip to content

Commit

Permalink
Enable profiling on anon functions
Browse files Browse the repository at this point in the history
  • Loading branch information
sam-at-luther committed Feb 8, 2024
1 parent cdb6049 commit 3497761
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 42 deletions.
16 changes: 12 additions & 4 deletions lisp/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -866,10 +866,7 @@ func (env *LEnv) EvalSExpr(s *LVal) *LVal {
fun := call.Cells[0] // call is not an empty expression -- fun is known LFun
args := call
args.Cells = args.Cells[1:]
if env.Runtime.Profiler != nil {
stop := env.Runtime.Profiler.Start(fun)
defer stop()
}

switch fun.FunType {
case LFunNone:
return env.FunCall(fun, args)
Expand Down Expand Up @@ -979,6 +976,15 @@ func (env *LEnv) FunCall(fun, args *LVal) *LVal {
return env.funCall(fun, args)
}

func (env *LEnv) trace(fun *LVal) func() {
if env.Runtime.Profiler != nil {
// fun might be an anon function, so we need to convert it to get the
// right type of LVal for filtering and labeling
return env.Runtime.Profiler.Start(fun)
}
return func() {}
}

// FunCall invokes regular function fun with the argument list args.
func (env *LEnv) funCall(fun, args *LVal) *LVal {
if fun.Type != LFun {
Expand All @@ -988,6 +994,8 @@ func (env *LEnv) funCall(fun, args *LVal) *LVal {
return env.Errorf("not a regular function: %v", fun.FunType)
}

defer env.trace(fun)()

// Check for possible tail recursion before pushing to avoid hitting s when
// checking. But push FID onto the stack before popping to simplify
// book-keeping.
Expand Down
21 changes: 21 additions & 0 deletions lisp/lisp.go
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,27 @@ func (v *LVal) String() string {
return v.str(false)
}

// Docstring returns the docstring of the function reference v. If v is not
// a function Docstring returns the empty string.
func (v *LVal) Docstring() string {
if v.Type != LFun {
return ""
}
if v.Builtin() != nil {
if len(v.Cells) > 1 {
return v.Cells[1].Str
}
return ""
}
// Functions of the form (lambda (x) "abc") are considered constant string
// functions without documentation so there must be a length check on the
// function body.
if len(v.Cells) > 2 && v.Cells[1].Type == LString {
return v.Cells[1].Str
}
return ""
}

func (v *LVal) str(onTheRecord bool) string {
const QUOTE = `'`
// All types which may evaluate to things other than themselves must check
Expand Down
23 changes: 1 addition & 22 deletions lisp/lisplib/libhelp/libhelp.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,27 +122,6 @@ func sortedSymbols(smap map[string]*lisp.LVal) []string {
return symbols
}

// FunDocstring returns the docstring of the function reference v. If v is not
// a function FunDocstring returns the empty string.
func FunDocstring(v *lisp.LVal) string {
if v.Type != lisp.LFun {
return ""
}
if v.Builtin() != nil {
if len(v.Cells) > 1 {
return v.Cells[1].Str
}
return ""
}
// Functions of the form (lambda (x) "abc") are considered constant string
// functions without documentation so there must be a length check on the
// function body.
if len(v.Cells) > 2 && v.Cells[1].Type == lisp.LString {
return v.Cells[1].Str
}
return ""
}

// RenderPkgExported writes to w formatted documentation for exported symbols
// in the query package within env. The exact formatting of the rendered
// documentation is subject to change across elps versions.
Expand Down Expand Up @@ -211,7 +190,7 @@ func renderFun(w io.Writer, sym string, v *lisp.LVal) error {
if err != nil {
return fmt.Errorf("rendering signature: %w", err)
}
doc := cleanDocstring(FunDocstring(v))
doc := cleanDocstring(v.Docstring())
if doc != "" {
_, err = fmt.Fprintln(w, doc)
return err
Expand Down
10 changes: 3 additions & 7 deletions lisp/lisplib/libhelp/libhelp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (

"github.com/luthersystems/elps/lisp"
"github.com/luthersystems/elps/lisp/lisplib"
"github.com/luthersystems/elps/lisp/lisplib/libhelp"
"github.com/luthersystems/elps/parser"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand All @@ -30,8 +29,7 @@ func TestDocstring(t *testing.T) {
for _, name := range builtinSymbols {
fun := env.Get(lisp.Symbol(name))
if assert.Equal(t, lisp.LFun, fun.Type) {
docstring := libhelp.FunDocstring(fun)
assert.NotEqual(t, "", docstring)
assert.NotEqual(t, "", fun.Docstring())
}
}

Expand All @@ -43,12 +41,10 @@ func TestDocstring(t *testing.T) {

lisp1 := env.Get(lisp.Symbol("const-string1"))
if assert.Equal(t, lisp.LFun, lisp1.Type) {
docstring := libhelp.FunDocstring(lisp1)
assert.Equal(t, "", docstring)
assert.Equal(t, "", lisp1.Docstring())
}
lisp2 := env.Get(lisp.Symbol("const-string2"))
if assert.Equal(t, lisp.LFun, lisp2.Type) {
docstring := libhelp.FunDocstring(lisp2)
assert.Equal(t, "abc", docstring)
assert.Equal(t, "abc", lisp2.Docstring())
}
}
3 changes: 1 addition & 2 deletions lisp/x/profiler/funlabeler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"strings"

"github.com/luthersystems/elps/lisp"
"github.com/luthersystems/elps/lisp/lisplib/libhelp"
)

// FunLabeler provides an alternative name for a function label in the trace.
Expand Down Expand Up @@ -74,7 +73,7 @@ func docLabel(docStr string) string {
}

func elpsDocFunLabeler(runtime *lisp.Runtime, fun *lisp.LVal) string {
docStr := libhelp.FunDocstring(fun)
docStr := fun.Docstring()
if docStr == "" {
return ""
}
Expand Down
7 changes: 4 additions & 3 deletions lisp/x/profiler/opentelemetry_annotator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ func TestNewOpenTelemetryAnnotatorSkip(t *testing.T) {
assert.NoError(t, ppa.Complete())

spans := exporter.GetSpans()
assert.Equal(t, len(spans), 4, "Expected selective spans")
assert.Equal(t, spans[0].Name, "Add_It", "Expected 1st custom label")
assert.Equal(t, spans[3].Name, "Add_It_Again", "Expected 2nd custom label")
assert.Equal(t, 7, len(spans), "Expected selective spans")
assert.Equal(t, "Add_It", spans[0].Name, "Expected custom label")
assert.Equal(t, "Add_It_Again", spans[3].Name, "Expected custom label")
assert.Equal(t, "lambda", spans[4].Name, "Expected custom label")
}
3 changes: 1 addition & 2 deletions lisp/x/profiler/skipfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"regexp"

"github.com/luthersystems/elps/lisp"
"github.com/luthersystems/elps/lisp/lisplib/libhelp"
)

type SkipFilter func(fun *lisp.LVal) bool
Expand Down Expand Up @@ -41,7 +40,7 @@ const ELPSDocTrace = "@trace"
var elpsDocTraceRegExp = regexp.MustCompile(ELPSDocTrace)

func elpsDocSkipFilter(fun *lisp.LVal) bool {
docStr := libhelp.FunDocstring(fun)
docStr := fun.Docstring()
if docStr == "" {
return true
}
Expand Down
9 changes: 7 additions & 2 deletions lisp/x/profiler/test.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@
(add-it x 3)))

(print-it "Hello")
(print-it (add-it (add-it 3 (recurse-it 5)) 8))
(print-it (add-it (add-it 3 (recurse-it 5)) 8)) ; span 1..3

(labels
([add-it-again (x)
"@trace { Add-It-Again }"
"@trace { Add-It-Again }" ; span 4
(+ x 1)])
(add-it-again 2))

(let ([l (lambda (x) "@trace{ lambda }" x)])
(l 42)) ; span 5

(map () (lambda (x) "@trace" x) '(1 2)) ; span 6, 7

0 comments on commit 3497761

Please sign in to comment.