// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package testkit

import (
	
	
	
	
	
	

	
	
	
	
	
	
)

func ( func(*Trace)) {
	// Create an output file.
	,  := os.Create(os.Args[1])
	if  != nil {
		panic(.Error())
	}
	defer .Close()

	// Create a new trace.
	 := NewTrace()

	// Call the generator.
	()

	// Write out the generator's state.
	if ,  := .Write(.Generate());  != nil {
		panic(.Error())
	}
}

// Trace represents an execution trace for testing.
//
// It does a little bit of work to ensure that the produced trace is valid,
// just for convenience. It mainly tracks batches and batch sizes (so they're
// trivially correct), tracks strings and stacks, and makes sure emitted string
// and stack batches are valid. That last part can be controlled by a few options.
//
// Otherwise, it performs no validation on the trace at all.
type Trace struct {
	// Trace data state.
	ver             version.Version
	names           map[string]event.Type
	specs           []event.Spec
	events          []raw.Event
	gens            []*Generation
	validTimestamps bool

	// Expectation state.
	bad      bool
	badMatch *regexp.Regexp
}

// NewTrace creates a new trace.
func () *Trace {
	 := version.Go122
	return &Trace{
		names:           event.Names(.Specs()),
		specs:           .Specs(),
		validTimestamps: true,
	}
}

// ExpectFailure writes down that the trace should be broken. The caller
// must provide a pattern matching the expected error produced by the parser.
func ( *Trace) ( string) {
	.bad = true
	.badMatch = regexp.MustCompile()
}

// ExpectSuccess writes down that the trace should successfully parse.
func ( *Trace) () {
	.bad = false
}

// RawEvent emits an event into the trace. name must correspond to one
// of the names in Specs() result for the version that was passed to
// this trace.
func ( *Trace) ( event.Type,  []byte,  ...uint64) {
	.events = append(.events, .createEvent(, , ...))
}

// DisableTimestamps makes the timestamps for all events generated after
// this call zero. Raw events are exempted from this because the caller
// has to pass their own timestamp into those events anyway.
func ( *Trace) () {
	.validTimestamps = false
}

// Generation creates a new trace generation.
//
// This provides more structure than Event to allow for more easily
// creating complex traces that are mostly or completely correct.
func ( *Trace) ( uint64) *Generation {
	 := &Generation{
		trace:   ,
		gen:     ,
		strings: make(map[string]uint64),
		stacks:  make(map[stack]uint64),
	}
	.gens = append(.gens, )
	return 
}

// Generate creates a test file for the trace.
func ( *Trace) () []byte {
	// Trace file contents.
	var  bytes.Buffer
	,  := raw.NewTextWriter(&, version.Go122)
	if  != nil {
		panic(.Error())
	}

	// Write raw top-level events.
	for ,  := range .events {
		.WriteEvent()
	}

	// Write generations.
	for ,  := range .gens {
		.writeEventsTo()
	}

	// Expectation file contents.
	 := []byte("SUCCESS\n")
	if .bad {
		 = []byte(fmt.Sprintf("FAILURE %q\n", .badMatch))
	}

	// Create the test file's contents.
	return txtar.Format(&txtar.Archive{
		Files: []txtar.File{
			{Name: "expect", Data: },
			{Name: "trace", Data: .Bytes()},
		},
	})
}

func ( *Trace) ( event.Type,  []byte,  ...uint64) raw.Event {
	 := .specs[]
	if  != go122.EvStack {
		if  := len(.Args); len() !=  {
			panic(fmt.Sprintf("expected %d args for %s, got %d", , .Name, len()))
		}
	}
	return raw.Event{
		Version: version.Go122,
		Ev:      ,
		Args:    ,
		Data:    ,
	}
}

type stack struct {
	stk [32]trace.StackFrame
	len int
}

var (
	NoString = ""
	NoStack  = []trace.StackFrame{}
)

// Generation represents a single generation in the trace.
type Generation struct {
	trace   *Trace
	gen     uint64
	batches []*Batch
	strings map[string]uint64
	stacks  map[stack]uint64

	// Options applied when Trace.Generate is called.
	ignoreStringBatchSizeLimit bool
	ignoreStackBatchSizeLimit  bool
}

// Batch starts a new event batch in the trace data.
//
// This is convenience function for generating correct batches.
func ( *Generation) ( trace.ThreadID,  Time) *Batch {
	if !.trace.validTimestamps {
		 = 0
	}
	 := &Batch{
		gen:       ,
		thread:    ,
		timestamp: ,
	}
	.batches = append(.batches, )
	return 
}

// String registers a string with the trace.
//
// This is a convenience function for easily adding correct
// strings to traces.
func ( *Generation) ( string) uint64 {
	if len() == 0 {
		return 0
	}
	if ,  := .strings[];  {
		return 
	}
	 := uint64(len(.strings) + 1)
	.strings[] = 
	return 
}

// Stack registers a stack with the trace.
//
// This is a convenience function for easily adding correct
// stacks to traces.
func ( *Generation) ( []trace.StackFrame) uint64 {
	if len() == 0 {
		return 0
	}
	if len() > 32 {
		panic("stack too big for test")
	}
	var  stack
	copy(.stk[:], )
	.len = len()
	if ,  := .stacks[];  {
		return 
	}
	 := uint64(len(.stacks) + 1)
	.stacks[] = 
	return 
}

// writeEventsTo emits event batches in the generation to tw.
func ( *Generation) ( *raw.TextWriter) {
	// Write event batches for the generation.
	for ,  := range .batches {
		.writeEventsTo()
	}

	// Write frequency.
	 := .newStructuralBatch()
	.RawEvent(go122.EvFrequency, nil, 15625000)
	.writeEventsTo()

	// Write stacks.
	 = .newStructuralBatch()
	.RawEvent(go122.EvStacks, nil)
	for ,  := range .stacks {
		 := .stk[:.len]
		 := []uint64{}
		for ,  := range  {
			 = append(, .PC, .String(.Func), .String(.File), .Line)
		}
		.RawEvent(go122.EvStack, nil, ...)

		// Flush the batch if necessary.
		if !.ignoreStackBatchSizeLimit && .size > go122.MaxBatchSize/2 {
			.writeEventsTo()
			 = .newStructuralBatch()
		}
	}
	.writeEventsTo()

	// Write strings.
	 = .newStructuralBatch()
	.RawEvent(go122.EvStrings, nil)
	for ,  := range .strings {
		.RawEvent(go122.EvString, []byte(), )

		// Flush the batch if necessary.
		if !.ignoreStringBatchSizeLimit && .size > go122.MaxBatchSize/2 {
			.writeEventsTo()
			 = .newStructuralBatch()
		}
	}
	.writeEventsTo()
}

func ( *Generation) () *Batch {
	return &Batch{gen: , thread: trace.NoThread}
}

// Batch represents an event batch.
type Batch struct {
	gen       *Generation
	thread    trace.ThreadID
	timestamp Time
	size      uint64
	events    []raw.Event
}

// Event emits an event into a batch. name must correspond to one
// of the names in Specs() result for the version that was passed to
// this trace. Callers must omit the timestamp delta.
func ( *Batch) ( string,  ...any) {
	,  := .gen.trace.names[]
	if ! {
		panic(fmt.Sprintf("invalid or unknown event %s", ))
	}
	var  []uint64
	 := 0
	if .gen.trace.specs[].IsTimedEvent {
		if .gen.trace.validTimestamps {
			 = []uint64{1}
		} else {
			 = []uint64{0}
		}
		 = 1
	}
	 := .gen.trace.specs[]
	if  := len(.Args) - ; len() !=  {
		panic(fmt.Sprintf("expected %d args for %s, got %d", , .Name, len()))
	}
	for ,  := range  {
		 = append(, .uintArgFor(, .Args[+]))
	}
	.RawEvent(, nil, ...)
}

func ( *Batch) ( any,  string) uint64 {
	 := strings.SplitN(, "_", 2)
	 := [0]
	if len() == 2 {
		 = [1]
	}
	var  uint64
	switch  {
	case "value":
		 = .(uint64)
	case "stack":
		 = .gen.Stack(.([]trace.StackFrame))
	case "seq":
		 = uint64(.(Seq))
	case "pstatus":
		 = uint64(.(go122.ProcStatus))
	case "gstatus":
		 = uint64(.(go122.GoStatus))
	case "g":
		 = uint64(.(trace.GoID))
	case "m":
		 = uint64(.(trace.ThreadID))
	case "p":
		 = uint64(.(trace.ProcID))
	case "string":
		 = .gen.String(.(string))
	case "task":
		 = uint64(.(trace.TaskID))
	default:
		panic(fmt.Sprintf("unsupported arg type %q for spec %q", , ))
	}
	return 
}

// RawEvent emits an event into a batch. name must correspond to one
// of the names in Specs() result for the version that was passed to
// this trace.
func ( *Batch) ( event.Type,  []byte,  ...uint64) {
	 := .gen.trace.createEvent(, , ...)

	// Compute the size of the event and add it to the batch.
	.size += 1 // One byte for the event header.
	var  [binary.MaxVarintLen64]byte
	for ,  := range  {
		.size += uint64(binary.PutUvarint([:], ))
	}
	if len() != 0 {
		.size += uint64(binary.PutUvarint([:], uint64(len())))
		.size += uint64(len())
	}

	// Add the event.
	.events = append(.events, )
}

// writeEventsTo emits events in the batch, including the batch header, to tw.
func ( *Batch) ( *raw.TextWriter) {
	.WriteEvent(raw.Event{
		Version: version.Go122,
		Ev:      go122.EvEventBatch,
		Args:    []uint64{.gen.gen, uint64(.thread), uint64(.timestamp), .size},
	})
	for ,  := range .events {
		.WriteEvent()
	}
}

// Seq represents a sequence counter.
type Seq uint64

// Time represents a low-level trace timestamp (which does not necessarily
// correspond to nanoseconds, like trace.Time does).
type Time uint64