141 lines
4.3 KiB
Go
141 lines
4.3 KiB
Go
package tracing
|
|
|
|
import (
|
|
"io/fs"
|
|
"testing"
|
|
"testing/fstest"
|
|
|
|
"github.com/microsoft/typescript-go/internal/json"
|
|
"github.com/microsoft/typescript-go/internal/vfs/vfstest"
|
|
"gotest.tools/v3/assert"
|
|
)
|
|
|
|
func TestConcurrentDurationEventsUseSeparateThreadIDs(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fsys := vfstest.FromMap(fstest.MapFS{
|
|
"/trace": &fstest.MapFile{Mode: fs.ModeDir},
|
|
}, true)
|
|
|
|
tr, err := StartTracing(fsys, "/trace", "", true /*deterministic*/)
|
|
assert.NilError(t, err)
|
|
|
|
endA := tr.Push(PhaseParse, "createSourceFile", map[string]any{"path": "/a.ts"}, true)
|
|
endB := tr.Push(PhaseParse, "createSourceFile", map[string]any{"path": "/b.ts"}, true)
|
|
endA()
|
|
endB()
|
|
|
|
endCheck := tr.Push(PhaseCheck, "checkSourceFile", map[string]any{"checkerId": 0, "path": "/a.ts"}, true)
|
|
endVariance := tr.Push(PhaseCheckTypes, "getVariancesWorker", map[string]any{"checkerId": 0, "id": 1}, true)
|
|
endVariance()
|
|
endCheck()
|
|
|
|
assert.NilError(t, tr.StopTracing())
|
|
|
|
traceText, ok := fsys.ReadFile("/trace/trace.json")
|
|
assert.Assert(t, ok)
|
|
|
|
var events []traceEvent
|
|
assert.NilError(t, json.Unmarshal([]byte(traceText), &events))
|
|
|
|
aBegin := findEvent(t, events, "B", "createSourceFile", "path", "/a.ts")
|
|
aEnd := findEvent(t, events, "E", "createSourceFile", "path", "/a.ts")
|
|
bBegin := findEvent(t, events, "B", "createSourceFile", "path", "/b.ts")
|
|
bEnd := findEvent(t, events, "E", "createSourceFile", "path", "/b.ts")
|
|
assert.Equal(t, aBegin.TID, aEnd.TID)
|
|
assert.Equal(t, bBegin.TID, bEnd.TID)
|
|
assert.Assert(t, aBegin.TID != bBegin.TID)
|
|
assertThreadName(t, events, aBegin.TID, "file:/a.ts")
|
|
assertThreadName(t, events, bBegin.TID, "file:/b.ts")
|
|
|
|
checkBegin := findEvent(t, events, "B", "checkSourceFile", "path", "/a.ts")
|
|
varianceBegin := findEvent(t, events, "B", "getVariancesWorker", "id", float64(1))
|
|
assert.Equal(t, checkBegin.TID, varianceBegin.TID)
|
|
assertThreadName(t, events, checkBegin.TID, "checker:0")
|
|
|
|
assertDurationEventsAreWellNestedByThread(t, events)
|
|
}
|
|
|
|
func TestThreadIDsAreStableAcrossFirstSeenOrder(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
first := traceThreadIDsForPaths(t, []string{"/a.ts", "/b.ts"})
|
|
second := traceThreadIDsForPaths(t, []string{"/b.ts", "/a.ts"})
|
|
|
|
assert.DeepEqual(t, first, second)
|
|
}
|
|
|
|
func traceThreadIDsForPaths(t *testing.T, paths []string) map[string]int {
|
|
t.Helper()
|
|
|
|
fsys := vfstest.FromMap(fstest.MapFS{
|
|
"/trace": &fstest.MapFile{Mode: fs.ModeDir},
|
|
}, true)
|
|
|
|
tr, err := StartTracing(fsys, "/trace", "", true /*deterministic*/)
|
|
assert.NilError(t, err)
|
|
|
|
for _, path := range paths {
|
|
end := tr.Push(PhaseParse, "createSourceFile", map[string]any{"path": path}, true)
|
|
end()
|
|
}
|
|
|
|
assert.NilError(t, tr.StopTracing())
|
|
|
|
traceText, ok := fsys.ReadFile("/trace/trace.json")
|
|
assert.Assert(t, ok)
|
|
|
|
var events []traceEvent
|
|
assert.NilError(t, json.Unmarshal([]byte(traceText), &events))
|
|
|
|
threadIDs := make(map[string]int)
|
|
for _, path := range paths {
|
|
threadIDs[path] = findEvent(t, events, "B", "createSourceFile", "path", path).TID
|
|
}
|
|
return threadIDs
|
|
}
|
|
|
|
func findEvent(t *testing.T, events []traceEvent, phase string, name string, argName string, argValue any) traceEvent {
|
|
t.Helper()
|
|
for _, event := range events {
|
|
if event.PH == phase && event.Name == name && event.Args[argName] == argValue {
|
|
return event
|
|
}
|
|
}
|
|
t.Fatalf("failed to find %s event %q with %s=%v", phase, name, argName, argValue)
|
|
return traceEvent{}
|
|
}
|
|
|
|
func assertThreadName(t *testing.T, events []traceEvent, tid int, name string) {
|
|
t.Helper()
|
|
for _, event := range events {
|
|
if event.PH == "M" && event.Name == "thread_name" && event.TID == tid && event.Args["name"] == name {
|
|
return
|
|
}
|
|
}
|
|
t.Fatalf("failed to find thread_name metadata for thread %d named %q", tid, name)
|
|
}
|
|
|
|
func assertDurationEventsAreWellNestedByThread(t *testing.T, events []traceEvent) {
|
|
t.Helper()
|
|
|
|
stacks := make(map[int][]traceEvent)
|
|
for _, event := range events {
|
|
switch event.PH {
|
|
case "B":
|
|
stacks[event.TID] = append(stacks[event.TID], event)
|
|
case "E":
|
|
stack := stacks[event.TID]
|
|
assert.Assert(t, len(stack) > 0, "unmatched end event %q on thread %d", event.Name, event.TID)
|
|
begin := stack[len(stack)-1]
|
|
assert.Equal(t, begin.Cat, event.Cat)
|
|
assert.Equal(t, begin.Name, event.Name)
|
|
stacks[event.TID] = stack[:len(stack)-1]
|
|
}
|
|
}
|
|
|
|
for tid, stack := range stacks {
|
|
assert.Assert(t, len(stack) == 0, "thread %d has %d unterminated events", tid, len(stack))
|
|
}
|
|
}
|