// Copyright 2022 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package repro

import (
	"fmt"

	"github.com/google/syzkaller/pkg/instance"
	"github.com/google/syzkaller/pkg/log"
	"github.com/google/syzkaller/pkg/mgrconfig"
	"github.com/google/syzkaller/pkg/report"
	"github.com/google/syzkaller/vm"
)

type StraceResult struct {
	Report *report.Report
	Output []byte
	Error  error
}

const (
	straceOutputLogSize = 2048 << 10
)

func RunStrace(result *Result, cfg *mgrconfig.Config, reporter *report.Reporter,
	vmPool *vm.Pool, vmIndex int) *StraceResult {
	if cfg.StraceBin == "" {
		return straceFailed(fmt.Errorf("strace binary is not set in the config"))
	}
	inst, err := instance.CreateExecProgInstance(vmPool, vmIndex, cfg, reporter,
		&instance.OptionalConfig{
			StraceBin:        cfg.StraceBin,
			BeforeContextLen: straceOutputLogSize,
		})
	if err != nil {
		return straceFailed(fmt.Errorf("failed to set up instance: %v", err))
	}
	defer inst.VMInstance.Close()

	var runRes *instance.RunResult
	if result.CRepro {
		log.Logf(1, "running C repro under strace")
		runRes, err = inst.RunCProg(result.Prog, result.Duration, result.Opts)
	} else {
		log.Logf(1, "running syz repro under strace")
		runRes, err = inst.RunSyzProg(result.Prog.Serialize(), result.Duration, result.Opts)
	}
	if err != nil {
		return straceFailed(fmt.Errorf("failed to generate strace log: %v", err))
	}
	return &StraceResult{
		Report: runRes.Report,
		Output: runRes.RawOutput,
	}
}

func straceFailed(err error) *StraceResult {
	return &StraceResult{Error: err}
}

func (strace *StraceResult) IsSameBug(repro *Result) bool {
	if strace == nil || strace.Report == nil || repro.Report == nil {
		return false
	}
	return strace.Report.Title == repro.Report.Title
}
