Added a rudimentary profiler to the Windows backend.

This commit is contained in:
Pietro Gagliardi 2015-05-10 17:35:52 -04:00
parent e1e9dddfdd
commit cb8a4872d5
3 changed files with 172 additions and 0 deletions

View File

@ -44,3 +44,9 @@ else
RC = i686-w64-mingw32-windres RC = i686-w64-mingw32-windres
archmflag = -m32 archmflag = -m32
endif endif
ifeq ($(PROFILE),1)
osCFILES += windows/profiler.c
osCFLAGS += -finstrument-functions
osLDFLAGS += -finstrument-functions
endif

35
windows/profiler.c Normal file
View File

@ -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);
}

131
windows/profiler.go Normal file
View File

@ -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")
}
}