Skip to content

Instantly share code, notes, and snippets.

@egonelbre
Last active November 15, 2016 11:16
Show Gist options
  • Save egonelbre/ab6c7a4a004227a168a8229656e9863c to your computer and use it in GitHub Desktop.
Save egonelbre/ab6c7a4a004227a168a8229656e9863c to your computer and use it in GitHub Desktop.
set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
call %VCVARS% x64
cl /O1 -Feloader.64.exe loader.cpp
call %VCVARS% x86
cl /O1 -Feloader.32.exe loader.cpp
del *.obj
go build timemem.go
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
int main(int argc, char const *argv[])
{
for(int i = 1; i < argc; i++){
HINSTANCE libx = LoadLibrary(argv[i]);
}
return 0;
}
set N=1000
echo "32bit none"
timemem -n %N% loader.32.exe
echo "32bit stdlib"
timemem -n %N% loader.32.exe kernel32.dll advapi32.dll shell32.dll mswsock.dll crypt32.dll ws2_32.dll dnsapi.dll iphlpapi.dll secur32.dll netapi32.dll userenv.dll winmm.dll
echo "32bit stdlib wo shell32"
timemem -n %N% loader.32.exe kernel32.dll advapi32.dll mswsock.dll crypt32.dll ws2_32.dll dnsapi.dll iphlpapi.dll secur32.dll netapi32.dll userenv.dll winmm.dll
echo "32bit stdlib wo shell32,winmm"
timemem -n %N% loader.32.exe kernel32.dll advapi32.dll mswsock.dll crypt32.dll ws2_32.dll dnsapi.dll iphlpapi.dll secur32.dll netapi32.dll userenv.dll
echo "64bit none"
timemem -n %N% loader.64.exe
echo "64bit stdlib"
timemem -n %N% loader.64.exe kernel32.dll advapi32.dll shell32.dll mswsock.dll crypt32.dll ws2_32.dll dnsapi.dll iphlpapi.dll secur32.dll netapi32.dll userenv.dll winmm.dll
echo "64bit stdlib wo shell32"
timemem -n %N% loader.64.exe kernel32.dll advapi32.dll mswsock.dll crypt32.dll ws2_32.dll dnsapi.dll iphlpapi.dll secur32.dll netapi32.dll userenv.dll winmm.dll
echo "64bit stdlib wo shell32,winmm"
timemem -n %N% loader.64.exe kernel32.dll advapi32.dll mswsock.dll crypt32.dll ws2_32.dll dnsapi.dll iphlpapi.dll secur32.dll netapi32.dll userenv.dll
package main
import (
"errors"
"flag"
"fmt"
"os"
"runtime"
"strings"
"syscall"
"time"
"unsafe"
"golang.org/x/sys/windows"
)
var (
N = flag.Int("n", 1000, "number of measurements")
)
type Measurement struct {
N int
Elapsed time.Duration
Kernel time.Duration
User time.Duration
PageFault int
WorkingSet int
PagedPool int
NonPagedPool int
PageFileSize int
}
func (m *Measurement) Add(b *Measurement) {
m.N += b.N
m.Elapsed += b.Elapsed
m.Kernel += b.Kernel
m.User += b.User
m.PageFault += b.PageFault
m.WorkingSet += b.WorkingSet
m.PagedPool += b.PagedPool
m.NonPagedPool += b.NonPagedPool
m.PageFileSize += b.PageFileSize
}
func (m *Measurement) Print() {
fmt.Printf("Elapsed %3.6fms\n", (m.Elapsed/time.Duration(m.N)).Seconds()*1000)
fmt.Printf("Kernel %3.6fms\n", (m.Kernel/time.Duration(m.N)).Seconds()*1000)
fmt.Printf("User %3.6fms\n", (m.User/time.Duration(m.N)).Seconds()*1000)
fmt.Printf("PageFault %3v KB\n", m.PageFault/m.N)
fmt.Printf("WorkingSet %3v KB\n", m.WorkingSet/m.N)
fmt.Printf("PagedPool %3v KB\n", m.PagedPool/m.N)
fmt.Printf("NonPagedPool %3v KB\n", m.NonPagedPool/m.N)
fmt.Printf("PageFileSize %3v KB\n", m.PageFileSize/m.N)
}
func filetimeToDuration(ft windows.Filetime) time.Duration {
nsec := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime)
return time.Duration(nsec*100) * time.Nanosecond
}
func Inspect(pid windows.Handle) (*Measurement, error) {
m := &Measurement{N: 1}
var creation, exit, kernel, user windows.Filetime
if err := windows.GetProcessTimes(pid, &creation, &exit, &kernel, &user); err != nil {
return nil, err
}
m.Elapsed = filetimeToDuration(exit) - filetimeToDuration(creation)
m.Kernel = filetimeToDuration(kernel)
m.User = filetimeToDuration(user)
var pmc ProcessMemoryCounters
if err := GetProcessMemoryInfo(pid, &pmc); err != nil {
return nil, err
}
m.PageFault = int(pmc.PageFaultCount)
m.WorkingSet = int(pmc.PeakWorkingSetSize)
m.PagedPool = int(pmc.QuotaPeakPagedPoolUsage)
m.NonPagedPool = int(pmc.QuotaPeakNonPagedPoolUsage)
m.PageFileSize = int(pmc.PeakPagefileUsage)
return m, nil
}
func MeasureProcess(args string) (*Measurement, error) {
si := &windows.StartupInfo{}
pi := &windows.ProcessInformation{}
argsp := windows.StringToUTF16Ptr(args)
defer runtime.KeepAlive(argsp)
err := windows.CreateProcess(nil, argsp, nil, nil, false, 0, nil, nil, si, pi)
if err != nil {
return &Measurement{}, err
}
defer windows.CloseHandle(pi.Thread)
defer windows.CloseHandle(pi.Process)
ev, err := windows.WaitForSingleObject(pi.Process, windows.INFINITE)
if err != nil {
return &Measurement{}, err
}
if ev != windows.WAIT_OBJECT_0 {
return &Measurement{}, errors.New("cannot wait for process")
}
return Inspect(pi.Process)
}
func main() {
flag.Parse()
args := strings.Join(flag.Args(), " ")
var total Measurement
for i := 0; i < *N; i++ {
m, err := MeasureProcess(args)
if err != nil {
fmt.Fprintln(os.Stderr, err)
continue
}
total.Add(m)
}
total.Print()
}
// psapi
var (
modpsapi = windows.NewLazySystemDLL("psapi.dll")
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo")
)
type ProcessMemoryCounters struct {
CB uint32
PageFaultCount uint32
PeakWorkingSetSize uintptr
WorkingSetSize uintptr
QuotaPeakPagedPoolUsage uintptr
QuotaPagedPoolUsage uintptr
QuotaPeakNonPagedPoolUsage uintptr
QuotaNonPagedPoolUsage uintptr
PagefileUsage uintptr
PeakPagefileUsage uintptr
}
func GetProcessMemoryInfo(h windows.Handle, pmc *ProcessMemoryCounters) (err error) {
r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(h), uintptr(unsafe.Pointer(pmc)), uintptr(unsafe.Sizeof(*pmc)))
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment