package main import ( "bufio" "debug/elf" "debug/macho" "fmt" "os" "path/filepath" "sort" "strconv" "strings" ) func symsizes(path string) map[string]float64 { m := make(map[string]float64) f, err := elf.Open(path) if err != nil { panic(err.Error()) } syms, err := f.Symbols() if err != nil { panic(err.Error()) } for _, sym := range syms { if sym.Section < elf.SectionIndex(len(f.Sections)) && strings.HasPrefix(f.Sections[sym.Section].Name, ".text") { m[sym.Name] = float64(sym.Size) } } return m } type bySectionThenOffset []macho.Symbol func (syms bySectionThenOffset) Len() int { return len(syms) } func (syms bySectionThenOffset) Less(i, j int) bool { if syms[i].Sect < syms[j].Sect { return true } if syms[i].Sect > syms[j].Sect { return false } return syms[i].Value < syms[j].Value } func (syms bySectionThenOffset) Swap(i, j int) { syms[i], syms[j] = syms[j], syms[i] } func macho_symsizes(path string) map[string]float64 { m := make(map[string]float64) f, err := macho.Open(path) if err != nil { panic(err.Error()) } syms := make([]macho.Symbol, len(f.Symtab.Syms)) copy(syms, f.Symtab.Syms) sort.Sort(bySectionThenOffset(syms)) for i, sym := range syms { if sym.Sect == 0 { continue } var nextOffset uint64 if i == len(syms)-1 || syms[i+1].Sect != sym.Sect { nextOffset = f.Sections[sym.Sect-1].Size } else { nextOffset = syms[i+1].Value } m[sym.Name] = float64(nextOffset - sym.Value) } return m } func benchnums(path, stat string) map[string]float64 { m := make(map[string]float64) fh, err := os.Open(path) if err != nil { panic(err.Error()) } scanner := bufio.NewScanner(fh) for scanner.Scan() { elems := strings.Split(scanner.Text(), "\t") if !strings.HasPrefix(elems[0], "Benchmark") || len(elems) < 3 { continue } var s string for _, elem := range elems[2:] { selems := strings.Split(strings.TrimSpace(elem), " ") if selems[1] == stat { s = selems[0] } } if s != "" { ns, err := strconv.ParseFloat(s, 64) if err != nil { panic(scanner.Text() + " ---- " + err.Error()) } m[elems[0]] = ns } } if err := scanner.Err(); err != nil { panic(err) } return m } func ninja_logs(path string) map[string]float64 { m := make(map[string]float64) fh, err := os.Open(path) if err != nil { panic(err.Error()) } scanner := bufio.NewScanner(fh) for scanner.Scan() { elems := strings.Split(scanner.Text(), "\t") if len(elems) < 4 { continue } begin, err := strconv.ParseInt(elems[0], 10, 64) if err != nil { continue } end, err := strconv.ParseInt(elems[1], 10, 64) if err != nil { panic(err.Error()) } m[elems[3]] = float64(end-begin) } return m } func filesizes(root string) map[string]float64 { m := make(map[string]float64) err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if info.Mode().IsRegular() { m[path[len(root):]] = float64(info.Size()) } return nil }) if err != nil { panic(err.Error()) } return m } func main() { var cmp func(string) map[string]float64 switch os.Args[1] { case "symsizes": cmp = symsizes case "macho_symsizes": cmp = macho_symsizes case "benchns": cmp = func(path string) map[string]float64 { return benchnums(path, "ns/op") } case "benchallocs": cmp = func(path string) map[string]float64 { return benchnums(path, "allocs/op") } case "ninja_logs": cmp = ninja_logs case "filesizes": cmp = filesizes } syms1 := cmp(os.Args[2]) syms2 := cmp(os.Args[3]) for n, z1 := range syms1 { if z2, ok := syms2[n]; ok && z2 != 0 { fmt.Printf("%s %f %f %f\n", n, z1, z2, z1/z2) } } }