blob: 9abd4c75cb3b459533ae102e4a1885a952598e45 [file] [log] [blame]
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SYZYGY_AGENT_PROFILER_RETURN_THUNK_FACTORY_H_
#define SYZYGY_AGENT_PROFILER_RETURN_THUNK_FACTORY_H_
#include "base/basictypes.h"
#include "syzygy/common/assertions.h"
#include "syzygy/trace/protocol/call_trace_defs.h"
namespace agent {
namespace profiler {
// A factory for return thunks as used by the profiler. These are
// packed as tight as possible into whole pages of memory. All pages
// are freed on destruction, but currently-unused pages are not freed
// in between times, on the assumption that the call stack will grow
// as deep again as it has before.
//
// This class is currently somewhat specific to profiling, as it
// calls rdtsc in the return hook and stores data needed for profiling,
// but it could be generalized if needed.
class ReturnThunkFactoryBase {
public:
struct Thunk;
struct ThunkData;
// Provides a pointer to the thunk data associated with a thunk that,
// when called, will invoke OnFunctionExit and then return
// to |real_ret|.
//
// Ownership of the data and thunk remains with the factory, which
// reuses it only after it, or the return thunk of a function below
// it on the stack, has been returned to.
ThunkData* MakeThunk(RetAddr real_ret);
// If @p ret is a thunk belonging to this factory, return that thunk,
// or NULL otherwise.
Thunk* CastToThunk(RetAddr ret);
// Returns the thunk data corresponding to a thunk.
static ThunkData* DataFromThunk(Thunk* thunk);
// The thunk itself is opaque, but must be declared here
// for the size calculations below.
struct Thunk {
// This must match the size of the code generated for the thunk.
// Currently the code is:
// push <address of thunk>
// call thunk_main_asm
// each of which consumes 5 bytes on x86.
uint8 instr[10];
};
COMPILE_ASSERT_IS_POD(Thunk);
// The data associated with each thunk.
struct ThunkData {
// A pointer to the thunk that owns us.
Thunk* thunk;
// A back-pointer to the return thunk factory.
ReturnThunkFactoryBase* self;
// The caller and the function invoked.
RetAddr caller;
FuncAddr function;
// The time of entry.
uint64 cycles_entry;
};
COMPILE_ASSERT_IS_POD(ThunkData);
protected:
// Thunk function type.
typedef void (*ThunkMainFunc)();
explicit ReturnThunkFactoryBase(ThunkMainFunc main_func);
~ReturnThunkFactoryBase();
// Must be called after construction of this class to
// initialize it for use.
// @note Calling from a subclass constructor is fine.
void Initialize();
// Must be called before destruction of this class to
// free all resources.
// @note Calling from a subclass destructor is fine.
void Uninitialize();
// @name To be implemented by subclasses.
// @{
// Invoked after the factory has allocated a new page of thunks.
// @param page the page of thunks, @p page is 4K and aligned on a
// 4K boundary.
virtual void OnPageAdded(const void* page) = 0;
// Invoked before the factory deallocates a page of thunks.
// @param page the page of thunks, @p page is 4K in size and aligned on a
// 4K boundary.
virtual void OnPageRemoved(const void* page) = 0;
// @}
struct Page {
Page* previous_page;
Page* next_page;
ReturnThunkFactoryBase* factory;
Thunk thunks[1]; // In fact, as many as fit.
};
static const size_t kPageSize = 0x00001000; // 4096
static const size_t kPageMask = 0xFFFFF000;
static const size_t kNumThunksPerPage =
(kPageSize - offsetof(Page, thunks)) / sizeof(Thunk);
void AddPage();
static Page* PageFromThunk(Thunk* thunk);
static Thunk* LastThunk(Page* page);
// The thunk main function we delegate to.
ThunkMainFunc main_func_;
// At all times, this points to the memory area we can use the next time
// we need a thunk.
//
// Thunks form a stack since they correspond to stack invocations. When
// a thunk is invoked on, it means a stack frame is being returned from,
// so we know that all thunks above it are now free. This is true even
// in the context of an exception handler, since the stack has been unwound.
//
// We can get the Page* for this Thunk by finding the page boundary,
// and pages are linked together, so this is all we need to store.
Thunk* first_free_thunk_;
DISALLOW_COPY_AND_ASSIGN(ReturnThunkFactoryBase);
};
// The ImplClass must derive from the return factory base, and implement
// a member function with the following signature:
// void OnFunctionExit(const ThunkData* data, uint64 cycles);
template <typename ImplClass> class ReturnThunkFactoryImpl
: public ReturnThunkFactoryBase {
public:
ReturnThunkFactoryImpl()
: ReturnThunkFactoryBase(thunk_main_asm) {
}
protected:
// Static assembly function called by all thunks. It ends up calling to
// ReturnThunkFactoryImpl::ThunkMain, which in turn calls
// ImpClass::OnFunctionExit.
static void thunk_main_asm();
static RetAddr WINAPI ThunkMain(ThunkData* thunk, uint64 cycles);
};
template <class ImplClass> void __declspec(naked)
ReturnThunkFactoryImpl<ImplClass>::thunk_main_asm() {
__asm {
// Stash volatile registers.
push eax
push edx
// Get the current cycle time ASAP.
rdtsc
push ecx
// Save eax, we need the register to grab the flags.
mov ecx, eax
// Save the low byte of the flags into AH.
lahf
// Save the overflow flag into AL.
seto al
// Stash the flags to stack.
push eax
// Push the cycle time arg for the ThunkMain function.
push edx
push ecx
// Get the thunk address and push it to the top of the stack.
push DWORD PTR[esp + 0x18]
call ThunkMain
// Save eax, we need it for later.
mov ecx, eax
pop eax
// AL is set to 1 if the overflow flag was set before the call to
// our hook, 0 otherwise. We add 0x7f to it so it'll restore the
// flag.
add al, 0x7f
// Restore the low byte of the flags.
sahf
// Restore eax.
mov eax, ecx
// Restore volatile registers, except eax.
pop ecx
pop edx
// At this point we have:
// EAX: real ret-address
// stack:
// pushed EAX
// ret-address to thunk
push eax
mov eax, DWORD PTR[esp+4]
// Return and discard the stored eax and discarded thunk address.
ret 8
}
}
template <class ImplClass>
RetAddr WINAPI ReturnThunkFactoryImpl<ImplClass>::
ThunkMain(ThunkData* data, uint64 cycles) {
ImplClass* factory = static_cast<ImplClass*>(data->self);
factory->first_free_thunk_ = data->thunk;
factory->OnFunctionExit(data, cycles);
return data->caller;
}
} // namespace profiler
} // namespace agent
#endif // SYZYGY_AGENT_PROFILER_RETURN_THUNK_FACTORY_H_