| package main |
| |
| import ( |
| "bufio" |
| "debug/elf" |
| "debug/macho" |
| "fmt" |
| "os" |
| "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 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 |
| } |
| |
| 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) |
| } |
| } |
| } |