Commit 9fd26872 authored by Hector Chu's avatar Hector Chu Committed by Alex Brainman

runtime: implement pprof support for windows

Credit to jp for proof of concept.

R=alex.brainman, jp, rsc, dvyukov
CC=golang-dev
https://golang.org/cl/4960057
parent 44f12eb5
...@@ -212,6 +212,7 @@ struct G ...@@ -212,6 +212,7 @@ struct G
uintptr sigcode1; uintptr sigcode1;
uintptr sigpc; uintptr sigpc;
uintptr gopc; // pc of go statement that created this goroutine uintptr gopc; // pc of go statement that created this goroutine
uintptr end[];
}; };
struct M struct M
{ {
...@@ -253,9 +254,11 @@ struct M ...@@ -253,9 +254,11 @@ struct M
uint32 fflag; // floating point compare flags uint32 fflag; // floating point compare flags
#ifdef __WINDOWS__ #ifdef __WINDOWS__
void* thread; // thread handle
void* event; // event for signalling void* event; // event for signalling
M* nextwaitm; // next M waiting for lock M* nextwaitm; // next M waiting for lock
#endif #endif
uintptr end[];
}; };
struct Stktop struct Stktop
......
...@@ -10,9 +10,13 @@ enum { ...@@ -10,9 +10,13 @@ enum {
PROT_EXEC = 0x4, PROT_EXEC = 0x4,
MAP_ANON = 0x1, MAP_ANON = 0x1,
MAP_PRIVATE = 0x2, MAP_PRIVATE = 0x2,
DUPLICATE_SAME_ACCESS = 0x2,
THREAD_PRIORITY_HIGHEST = 0x2,
SIGINT = 0x2, SIGINT = 0x2,
CTRL_C_EVENT = 0, CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 0x1, CTRL_BREAK_EVENT = 0x1,
CONTEXT_CONTROL = 0x10001,
CONTEXT_FULL = 0x10007,
EXCEPTION_ACCESS_VIOLATION = 0xc0000005, EXCEPTION_ACCESS_VIOLATION = 0xc0000005,
EXCEPTION_BREAKPOINT = 0x80000003, EXCEPTION_BREAKPOINT = 0x80000003,
EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d, EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d,
......
...@@ -90,9 +90,7 @@ runtime·sighandler(ExceptionRecord *info, void *frame, Context *r) ...@@ -90,9 +90,7 @@ runtime·sighandler(ExceptionRecord *info, void *frame, Context *r)
} }
void void
runtime·resetcpuprofiler(int32 hz) runtime·dosigprof(Context *r, G *gp)
{ {
// TODO: Enable profiling interrupts. runtime·sigprof((uint8*)r->Eip, (uint8*)r->Esp, nil, gp);
m->profilehz = hz;
} }
...@@ -96,31 +96,52 @@ TEXT runtime·sigtramp1(SB),0,$16-40 ...@@ -96,31 +96,52 @@ TEXT runtime·sigtramp1(SB),0,$16-40
sigdone: sigdone:
RET RET
// Windows runs the ctrl handler in a new thread.
TEXT runtime·ctrlhandler(SB),7,$0 TEXT runtime·ctrlhandler(SB),7,$0
PUSHL $runtime·ctrlhandler1(SB)
CALL runtime·externalthreadhandler(SB)
MOVL 4(SP), CX
ADDL $12, SP
JMP CX
TEXT runtime·profileloop(SB),7,$0
PUSHL $runtime·profileloop1(SB)
CALL runtime·externalthreadhandler(SB)
MOVL 4(SP), CX
ADDL $12, SP
JMP CX
TEXT runtime·externalthreadhandler(SB),7,$0
PUSHL BP PUSHL BP
MOVL SP, BP MOVL SP, BP
PUSHL BX PUSHL BX
PUSHL SI PUSHL SI
PUSHL DI PUSHL DI
PUSHL 0x2c(FS) PUSHL 0x2c(FS)
MOVL SP, BX MOVL SP, DX
// setup dummy m, g // setup dummy m, g
SUBL $(m_fflag+4), SP // at least space for m_fflag SUBL $m_end, SP // space for M
MOVL SP, 0(SP)
MOVL $m_end, 4(SP)
CALL runtime·memclr(SB) // smashes AX,BX,CX
LEAL m_tls(SP), CX LEAL m_tls(SP), CX
MOVL CX, 0x2c(FS) MOVL CX, 0x2c(FS)
MOVL SP, m(CX) MOVL SP, m(CX)
MOVL SP, DX MOVL SP, BX
SUBL $8, SP // space for g_stack{guard,base} SUBL $g_end, SP // space for G
MOVL SP, g(CX) MOVL SP, g(CX)
MOVL SP, m_g0(DX) MOVL SP, m_g0(BX)
MOVL SP, 0(SP)
MOVL $g_end, 4(SP)
CALL runtime·memclr(SB) // smashes AX,BX,CX
LEAL -4096(SP), CX LEAL -4096(SP), CX
MOVL CX, g_stackguard(SP) MOVL CX, g_stackguard(SP)
MOVL BX, g_stackbase(SP) MOVL DX, g_stackbase(SP)
PUSHL 8(BP) PUSHL 16(BP) // arg for handler
CALL runtime·ctrlhandler1(SB) CALL 8(BP)
POPL CX POPL CX
get_tls(CX) get_tls(CX)
...@@ -131,9 +152,7 @@ TEXT runtime·ctrlhandler(SB),7,$0 ...@@ -131,9 +152,7 @@ TEXT runtime·ctrlhandler(SB),7,$0
POPL SI POPL SI
POPL BX POPL BX
POPL BP POPL BP
MOVL 0(SP), CX RET
ADDL $8, SP
JMP CX
// Called from dynamic function created by ../thread.c compilecallback, // Called from dynamic function created by ../thread.c compilecallback,
// running on Windows stack (not Go stack). // running on Windows stack (not Go stack).
......
...@@ -10,9 +10,13 @@ enum { ...@@ -10,9 +10,13 @@ enum {
PROT_EXEC = 0x4, PROT_EXEC = 0x4,
MAP_ANON = 0x1, MAP_ANON = 0x1,
MAP_PRIVATE = 0x2, MAP_PRIVATE = 0x2,
DUPLICATE_SAME_ACCESS = 0x2,
THREAD_PRIORITY_HIGHEST = 0x2,
SIGINT = 0x2, SIGINT = 0x2,
CTRL_C_EVENT = 0, CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 0x1, CTRL_BREAK_EVENT = 0x1,
CONTEXT_CONTROL = 0x100001,
CONTEXT_FULL = 0x10000b,
EXCEPTION_ACCESS_VIOLATION = 0xc0000005, EXCEPTION_ACCESS_VIOLATION = 0xc0000005,
EXCEPTION_BREAKPOINT = 0x80000003, EXCEPTION_BREAKPOINT = 0x80000003,
EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d, EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d,
......
...@@ -100,9 +100,7 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp) ...@@ -100,9 +100,7 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
} }
void void
runtime·resetcpuprofiler(int32 hz) runtime·dosigprof(Context *r, G *gp)
{ {
// TODO: Enable profiling interrupts. runtime·sigprof((uint8*)r->Rip, (uint8*)r->Rsp, nil, gp);
m->profilehz = hz;
} }
...@@ -100,31 +100,51 @@ TEXT runtime·sigtramp(SB),7,$56 ...@@ -100,31 +100,51 @@ TEXT runtime·sigtramp(SB),7,$56
sigdone: sigdone:
RET RET
// Windows runs the ctrl handler in a new thread. TEXT runtime·ctrlhandler(SB),7,$8
TEXT runtime·ctrlhandler(SB),7,$0 MOVQ CX, 16(SP) // spill
MOVQ $runtime·ctrlhandler1(SB), CX
MOVQ CX, 0(SP)
CALL runtime·externalthreadhandler(SB)
RET
TEXT runtime·profileloop(SB),7,$8
MOVQ $runtime·profileloop1(SB), CX
MOVQ CX, 0(SP)
CALL runtime·externalthreadhandler(SB)
RET
TEXT runtime·externalthreadhandler(SB),7,$0
PUSHQ BP PUSHQ BP
MOVQ SP, BP MOVQ SP, BP
PUSHQ BX PUSHQ BX
PUSHQ SI PUSHQ SI
PUSHQ DI PUSHQ DI
PUSHQ 0x58(GS) PUSHQ 0x58(GS)
MOVQ SP, BX MOVQ SP, DX
// setup dummy m, g // setup dummy m, g
SUBQ $(m_fflag+4), SP // at least space for m_fflag SUBQ $m_end, SP // space for M
MOVQ SP, 0(SP)
MOVQ $m_end, 8(SP)
CALL runtime·memclr(SB) // smashes AX,BX,CX
LEAQ m_tls(SP), CX LEAQ m_tls(SP), CX
MOVQ CX, 0x58(GS) MOVQ CX, 0x58(GS)
MOVQ SP, m(CX) MOVQ SP, m(CX)
MOVQ SP, DX MOVQ SP, BX
SUBQ $16, SP // space for g_stack{guard,base} SUBQ $g_end, SP // space for G
MOVQ SP, g(CX) MOVQ SP, g(CX)
MOVQ SP, m_g0(DX) MOVQ SP, m_g0(BX)
MOVQ SP, 0(SP)
MOVQ $g_end, 8(SP)
CALL runtime·memclr(SB) // smashes AX,BX,CX
LEAQ -8192(SP), CX LEAQ -8192(SP), CX
MOVQ CX, g_stackguard(SP) MOVQ CX, g_stackguard(SP)
MOVQ BX, g_stackbase(SP) MOVQ DX, g_stackbase(SP)
PUSHQ 16(BP) PUSHQ 32(BP) // arg for handler
CALL runtime·ctrlhandler1(SB) CALL 16(BP)
POPQ CX POPQ CX
get_tls(CX) get_tls(CX)
......
...@@ -17,10 +17,16 @@ enum { ...@@ -17,10 +17,16 @@ enum {
$MAP_ANON = 1, $MAP_ANON = 1,
$MAP_PRIVATE = 2, $MAP_PRIVATE = 2,
$DUPLICATE_SAME_ACCESS = DUPLICATE_SAME_ACCESS,
$THREAD_PRIORITY_HIGHEST = THREAD_PRIORITY_HIGHEST,
$SIGINT = SIGINT, $SIGINT = SIGINT,
$CTRL_C_EVENT = CTRL_C_EVENT, $CTRL_C_EVENT = CTRL_C_EVENT,
$CTRL_BREAK_EVENT = CTRL_BREAK_EVENT, $CTRL_BREAK_EVENT = CTRL_BREAK_EVENT,
$CONTEXT_CONTROL = CONTEXT_CONTROL,
$CONTEXT_FULL = CONTEXT_FULL,
$EXCEPTION_ACCESS_VIOLATION = STATUS_ACCESS_VIOLATION, $EXCEPTION_ACCESS_VIOLATION = STATUS_ACCESS_VIOLATION,
$EXCEPTION_BREAKPOINT = STATUS_BREAKPOINT, $EXCEPTION_BREAKPOINT = STATUS_BREAKPOINT,
$EXCEPTION_FLT_DENORMAL_OPERAND = STATUS_FLOAT_DENORMAL_OPERAND, $EXCEPTION_FLT_DENORMAL_OPERAND = STATUS_FLOAT_DENORMAL_OPERAND,
......
...@@ -13,8 +13,8 @@ extern void *runtime·GetProcAddress; ...@@ -13,8 +13,8 @@ extern void *runtime·GetProcAddress;
void runtime·asmstdcall(void *c); void runtime·asmstdcall(void *c);
void *runtime·stdcall(void *fn, int32 count, ...); void *runtime·stdcall(void *fn, int32 count, ...);
uintptr runtime·getlasterror(void); uint32 runtime·getlasterror(void);
void runtime·setlasterror(uintptr err); void runtime·setlasterror(uint32 err);
// Function to be called by windows CreateThread // Function to be called by windows CreateThread
// to start new os thread. // to start new os thread.
......
...@@ -10,32 +10,48 @@ ...@@ -10,32 +10,48 @@
#pragma dynimport runtime·CloseHandle CloseHandle "kernel32.dll" #pragma dynimport runtime·CloseHandle CloseHandle "kernel32.dll"
#pragma dynimport runtime·CreateEvent CreateEventA "kernel32.dll" #pragma dynimport runtime·CreateEvent CreateEventA "kernel32.dll"
#pragma dynimport runtime·CreateThread CreateThread "kernel32.dll" #pragma dynimport runtime·CreateThread CreateThread "kernel32.dll"
#pragma dynimport runtime·CreateWaitableTimer CreateWaitableTimerA "kernel32.dll"
#pragma dynimport runtime·DuplicateHandle DuplicateHandle "kernel32.dll"
#pragma dynimport runtime·ExitProcess ExitProcess "kernel32.dll" #pragma dynimport runtime·ExitProcess ExitProcess "kernel32.dll"
#pragma dynimport runtime·FreeEnvironmentStringsW FreeEnvironmentStringsW "kernel32.dll" #pragma dynimport runtime·FreeEnvironmentStringsW FreeEnvironmentStringsW "kernel32.dll"
#pragma dynimport runtime·GetEnvironmentStringsW GetEnvironmentStringsW "kernel32.dll" #pragma dynimport runtime·GetEnvironmentStringsW GetEnvironmentStringsW "kernel32.dll"
#pragma dynimport runtime·GetProcAddress GetProcAddress "kernel32.dll" #pragma dynimport runtime·GetProcAddress GetProcAddress "kernel32.dll"
#pragma dynimport runtime·GetStdHandle GetStdHandle "kernel32.dll" #pragma dynimport runtime·GetStdHandle GetStdHandle "kernel32.dll"
#pragma dynimport runtime·GetThreadContext GetThreadContext "kernel32.dll"
#pragma dynimport runtime·LoadLibraryEx LoadLibraryExA "kernel32.dll" #pragma dynimport runtime·LoadLibraryEx LoadLibraryExA "kernel32.dll"
#pragma dynimport runtime·QueryPerformanceCounter QueryPerformanceCounter "kernel32.dll" #pragma dynimport runtime·QueryPerformanceCounter QueryPerformanceCounter "kernel32.dll"
#pragma dynimport runtime·QueryPerformanceFrequency QueryPerformanceFrequency "kernel32.dll" #pragma dynimport runtime·QueryPerformanceFrequency QueryPerformanceFrequency "kernel32.dll"
#pragma dynimport runtime·ResumeThread ResumeThread "kernel32.dll"
#pragma dynimport runtime·SetConsoleCtrlHandler SetConsoleCtrlHandler "kernel32.dll" #pragma dynimport runtime·SetConsoleCtrlHandler SetConsoleCtrlHandler "kernel32.dll"
#pragma dynimport runtime·SetEvent SetEvent "kernel32.dll" #pragma dynimport runtime·SetEvent SetEvent "kernel32.dll"
#pragma dynimport runtime·SetThreadPriority SetThreadPriority "kernel32.dll"
#pragma dynimport runtime·SetWaitableTimer SetWaitableTimer "kernel32.dll"
#pragma dynimport runtime·SuspendThread SuspendThread "kernel32.dll"
#pragma dynimport runtime·timeBeginPeriod timeBeginPeriod "winmm.dll"
#pragma dynimport runtime·WaitForSingleObject WaitForSingleObject "kernel32.dll" #pragma dynimport runtime·WaitForSingleObject WaitForSingleObject "kernel32.dll"
#pragma dynimport runtime·WriteFile WriteFile "kernel32.dll" #pragma dynimport runtime·WriteFile WriteFile "kernel32.dll"
extern void *runtime·CloseHandle; extern void *runtime·CloseHandle;
extern void *runtime·CreateEvent; extern void *runtime·CreateEvent;
extern void *runtime·CreateThread; extern void *runtime·CreateThread;
extern void *runtime·CreateWaitableTimer;
extern void *runtime·DuplicateHandle;
extern void *runtime·ExitProcess; extern void *runtime·ExitProcess;
extern void *runtime·FreeEnvironmentStringsW; extern void *runtime·FreeEnvironmentStringsW;
extern void *runtime·GetEnvironmentStringsW; extern void *runtime·GetEnvironmentStringsW;
extern void *runtime·GetProcAddress; extern void *runtime·GetProcAddress;
extern void *runtime·GetStdHandle; extern void *runtime·GetStdHandle;
extern void *runtime·GetThreadContext;
extern void *runtime·LoadLibraryEx; extern void *runtime·LoadLibraryEx;
extern void *runtime·QueryPerformanceCounter; extern void *runtime·QueryPerformanceCounter;
extern void *runtime·QueryPerformanceFrequency; extern void *runtime·QueryPerformanceFrequency;
extern void *runtime·ResumeThread;
extern void *runtime·SetConsoleCtrlHandler; extern void *runtime·SetConsoleCtrlHandler;
extern void *runtime·SetEvent; extern void *runtime·SetEvent;
extern void *runtime·SetThreadPriority;
extern void *runtime·SetWaitableTimer;
extern void *runtime·SuspendThread;
extern void *runtime·timeBeginPeriod;
extern void *runtime·WaitForSingleObject; extern void *runtime·WaitForSingleObject;
extern void *runtime·WriteFile; extern void *runtime·WriteFile;
...@@ -44,8 +60,13 @@ static int64 timerfreq; ...@@ -44,8 +60,13 @@ static int64 timerfreq;
void void
runtime·osinit(void) runtime·osinit(void)
{ {
// -1 = current process, -2 = current thread
runtime·stdcall(runtime·DuplicateHandle, 7,
(uintptr)-1, (uintptr)-2, (uintptr)-1, &m->thread,
(uintptr)0, (uintptr)0, (uintptr)DUPLICATE_SAME_ACCESS);
runtime·stdcall(runtime·QueryPerformanceFrequency, 1, &timerfreq); runtime·stdcall(runtime·QueryPerformanceFrequency, 1, &timerfreq);
runtime·stdcall(runtime·SetConsoleCtrlHandler, 2, runtime·ctrlhandler, (uintptr)1); runtime·stdcall(runtime·SetConsoleCtrlHandler, 2, runtime·ctrlhandler, (uintptr)1);
runtime·stdcall(runtime·timeBeginPeriod, 1, (uintptr)1);
} }
void void
...@@ -211,11 +232,13 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) ...@@ -211,11 +232,13 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
USED(g); // assuming g = m->g0 USED(g); // assuming g = m->g0
USED(fn); // assuming fn = mstart USED(fn); // assuming fn = mstart
thandle = runtime·stdcall(runtime·CreateThread, 6, (uintptr)0, (uintptr)0, runtime·tstart_stdcall, m, (uintptr)0, (uintptr)0); thandle = runtime·stdcall(runtime·CreateThread, 6,
if(thandle == 0) { nil, nil, runtime·tstart_stdcall, m, nil, nil);
if(thandle == nil) {
runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount(), runtime·getlasterror()); runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount(), runtime·getlasterror());
runtime·throw("runtime.newosproc"); runtime·throw("runtime.newosproc");
} }
runtime·atomicstorep(&m->thread, thandle);
} }
// Called to initialize a new m (including the bootstrap m). // Called to initialize a new m (including the bootstrap m).
...@@ -324,6 +347,89 @@ runtime·ctrlhandler1(uint32 type) ...@@ -324,6 +347,89 @@ runtime·ctrlhandler1(uint32 type)
return 0; return 0;
} }
extern void runtime·dosigprof(Context *r, G *gp);
extern void runtime·profileloop(void);
static void *profiletimer;
static void
profilem(M *mp)
{
extern M runtime·m0;
extern uint32 runtime·tls0[];
byte rbuf[sizeof(Context)+15];
Context *r;
void *tls;
G *gp;
tls = mp->tls;
if(mp == &runtime·m0)
tls = runtime·tls0;
gp = *(G**)tls;
if(gp != nil && gp != mp->g0 && gp->status != Gsyscall) {
// align Context to 16 bytes
r = (Context*)((uintptr)(&rbuf[15]) & ~15);
r->ContextFlags = CONTEXT_CONTROL;
runtime·stdcall(runtime·GetThreadContext, 2, mp->thread, r);
runtime·dosigprof(r, gp);
}
}
void
runtime·profileloop1(void)
{
M *mp, *allm;
void *thread;
runtime·stdcall(runtime·SetThreadPriority, 2,
(uintptr)-2, (uintptr)THREAD_PRIORITY_HIGHEST);
for(;;) {
runtime·stdcall(runtime·WaitForSingleObject, 2, profiletimer, (uintptr)-1);
allm = runtime·atomicloadp(&runtime·allm);
for(mp = allm; mp != nil; mp = mp->alllink) {
thread = runtime·atomicloadp(&mp->thread);
if(thread == nil)
continue;
runtime·stdcall(runtime·SuspendThread, 1, thread);
if(mp->profilehz != 0)
profilem(mp);
runtime·stdcall(runtime·ResumeThread, 1, thread);
}
}
}
void
runtime·resetcpuprofiler(int32 hz)
{
static Lock lock;
void *timer, *thread;
int32 ms;
int64 due;
runtime·lock(&lock);
if(profiletimer == nil) {
timer = runtime·stdcall(runtime·CreateWaitableTimer, 3, nil, nil, nil);
runtime·atomicstorep(&profiletimer, timer);
thread = runtime·stdcall(runtime·CreateThread, 6,
nil, nil, runtime·profileloop, nil, nil, nil);
runtime·stdcall(runtime·CloseHandle, 1, thread);
}
runtime·unlock(&lock);
ms = 0;
due = 1LL<<63;
if(hz > 0) {
ms = 1000 / hz;
if(ms == 0)
ms = 1;
due = ms * -10000;
}
runtime·stdcall(runtime·SetWaitableTimer, 6,
profiletimer, &due, (uintptr)ms, nil, nil, nil);
runtime·atomicstore((uint32*)&m->profilehz, hz);
}
void void
os·sigpipe(void) os·sigpipe(void)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment