package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"sort"
	"strings"

	"code.justin.tv/rhys/nursery/internal/_vendor/trace"
)

func main() {
	log.SetFlags(0)

	flag.Parse()
	if flag.NArg() != 2 {
		log.Printf("%s: binary.exe trace.out", os.Args[0])
		flag.Usage()
		os.Exit(2)
	}

	exe, err := os.Open(flag.Arg(0))
	if err != nil {
		log.Fatalf("open exe: %v", err)
	}
	defer exe.Close()

	tr, err := os.Open(flag.Arg(1))
	if err != nil {
		log.Fatalf("open trace: %v", err)
	}
	defer tr.Close()

	evs, err := trace.Parse(tr)
	if err != nil {
		log.Fatalf("parse err: %v", err)
	}

	log.Printf("evs: %d", len(evs))
	creates := make(map[string]costStack)
	for _, ev := range evs {

		if ev.Type == trace.EvGoCreate && ev.G != 0 {
			id := costStack{stk: ev.Stk}.stack()
			cs, ok := creates[id]
			cs.cost++
			if !ok {
				cs.stk = ev.Stk
			}
			creates[id] = cs
		}
	}

	var sites []costStack
	for _, site := range creates {
		sites = append(sites, site)
	}
	sort.Sort(byCost(sites))

	for _, site := range sites {
		log.Printf("%d @ %s", site.cost, site.stack())
	}
}

type costStack struct {
	cost int64
	stk  []*trace.Frame
}

func (cs costStack) stack() string {
	var stack []string
	for _, frame := range cs.stk {
		stack = append(stack, fmt.Sprintf("%#x", frame.PC))
	}
	return strings.Join(stack, " ")
}

type byCost []costStack

func (l byCost) Len() int      { return len(l) }
func (l byCost) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l byCost) Less(i, j int) bool {
	if l[i].cost != l[j].cost {
		return l[i].cost < l[j].cost
	}
	return l[i].stack() < l[j].stack()
}
