From cb8a4872d57c90a2bff593c565212c80b181f2da Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 10 May 2015 17:35:52 -0400 Subject: [PATCH] Added a rudimentary profiler to the Windows backend. --- windows/GNUmakeinc.mk | 6 ++ windows/profiler.c | 35 +++++++++++ windows/profiler.go | 131 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 windows/profiler.c create mode 100644 windows/profiler.go diff --git a/windows/GNUmakeinc.mk b/windows/GNUmakeinc.mk index 538c3ae2..fe40521c 100644 --- a/windows/GNUmakeinc.mk +++ b/windows/GNUmakeinc.mk @@ -44,3 +44,9 @@ else RC = i686-w64-mingw32-windres archmflag = -m32 endif + +ifeq ($(PROFILE),1) + osCFILES += windows/profiler.c + osCFLAGS += -finstrument-functions + osLDFLAGS += -finstrument-functions +endif diff --git a/windows/profiler.c b/windows/profiler.c new file mode 100644 index 00000000..9573de20 --- /dev/null +++ b/windows/profiler.c @@ -0,0 +1,35 @@ +// 10 may 2015 +#include "uipriv_windows.h" + +static FILE *fprof = NULL; + +__attribute__((no_instrument_function)) static void init(void) +{ + if (fprof != NULL) + return; + fprof = fopen("profiler.out", "w"); + if (fprof == NULL) { + fprintf(stderr, "error opening profiler output file\n"); + abort(); + } +} + +__attribute__((no_instrument_function)) void __cyg_profile_func_enter(void *this_fn, void *call_site) +{ + LARGE_INTEGER counter; + + init(); + QueryPerformanceCounter(&counter); + fprintf(fprof, "enter %p %I64d\n", this_fn, counter.QuadPart); + fflush(fprof); +} + +__attribute__((no_instrument_function)) void __cyg_profile_func_exit(void *this_fn, void *call_site) +{ + LARGE_INTEGER counter; + + init(); + QueryPerformanceCounter(&counter); + fprintf(fprof, "leave %p %I64d\n", this_fn, counter.QuadPart); + fflush(fprof); +} diff --git a/windows/profiler.go b/windows/profiler.go new file mode 100644 index 00000000..9dc074eb --- /dev/null +++ b/windows/profiler.go @@ -0,0 +1,131 @@ +// 10 may 2015 +package main + +import ( + "fmt" + "os" + "bufio" + "debug/pe" + "strings" + "strconv" +) + +func getsymbols(filename string) map[uint64]string { + dll, err := pe.Open(os.Args[1]) + if err != nil { + panic(err) + } + defer dll.Close() + + imagebase := uint64(0) + switch o := dll.OptionalHeader.(type) { + case *pe.OptionalHeader32: + imagebase = uint64(o.ImageBase) + case *pe.OptionalHeader64: + imagebase = o.ImageBase + default: + panic("unknown image base; can't continue") + } + + names := make(map[uint64]string) + for _, s := range dll.Symbols { + switch s.SectionNumber { + case 0, -1, -2: // undefined, constant, debugging + continue + } + addr := uint64(s.Value) + addr += imagebase + section := dll.Sections[s.SectionNumber - 1] + addr += uint64(section.VirtualAddress) + names[addr] = s.Name + } + + return names +} + +type Entry struct { + Leave bool + Func string + PerfCtr uint64 +} + +func getentries(filename string, names map[uint64]string) []Entry { + f, err := os.Open(os.Args[2]) + if err != nil { + panic(err) + } + defer f.Close() + + entries := make([]Entry, 0, 10240) + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + l := scanner.Text() + l = strings.TrimSpace(l) + parts := strings.Split(l, " ") + addr, err := strconv.ParseUint(parts[1], 16, 64) + if err != nil { + panic(err) + } + e := Entry{} + e.Leave = parts[0] == "leave" + e.Func = names[addr] + e.PerfCtr, err = strconv.ParseUint(parts[2], 10, 64) + if err != nil { + panic(err) + } + entries = append(entries, e) + } + if err := scanner.Err(); err != nil { + panic(err) + } + + return entries +} + +type Profile struct { + Calls uint + TotalTime uint64 + PerfCtr []uint64 +} + +var profile map[string]*Profile + +func run(e Entry) { + p := profile[e.Func] + if p == nil { + p = new(Profile) + } + if !e.Leave { + p.Calls++ + p.PerfCtr = append(p.PerfCtr, e.PerfCtr) + } else { + start := p.PerfCtr[len(p.PerfCtr) - 1] + p.PerfCtr = p.PerfCtr[:len(p.PerfCtr) - 1] + p.TotalTime += (e.PerfCtr - start) + } + profile[e.Func] = p +} + +func main() { + if len(os.Args) != 3 { + fmt.Fprintf(os.Stderr, "usage: %s dll profout\n", os.Args[0]) + os.Exit(1) + } + + names := getsymbols(os.Args[1]) + entries := getentries(os.Args[2], names) + profile = make(map[string]*Profile) + for _, e := range entries { + run(e) + } + for f, p := range profile { + fmt.Printf("%s %v %v ", f, p.Calls, p.TotalTime) + if p.Calls != 0 { + fmt.Printf("%v", float64(p.TotalTime) / float64(p.Calls)) + } else { + fmt.Printf("%v", float64(0)) + } + fmt.Printf("\n") + } +}