This commit is contained in:
Mukul Sabharwal 2019-08-12 11:43:31 -07:00
Родитель 82e68ef443
Коммит 30b067ddb0
7 изменённых файлов: 430 добавлений и 0 удалений

Просмотреть файл

@ -1,6 +1,94 @@
#include "BPerfProfilerCallback.h"
#include <vector>
#include <stack>
#include "corhlpr.h"
#include "JittingStartedEvent.h"
thread_local static EventLogger Logger;
static std::stack<portable_wide_string> BuildClassStack(IMetaDataImport* metaDataImport, mdTypeDef classTypeDef)
{
HRESULT hr;
std::stack<portable_wide_string> classStack;
DWORD typeDefFlags;
ULONG classNameLength = 0;
for (;;)
{
hr = metaDataImport->GetTypeDefProps(classTypeDef, nullptr, 0, &classNameLength, &typeDefFlags, nullptr);
if (FAILED(hr))
{
break;
}
portable_wide_string className(classNameLength, ZEROSTRING);
metaDataImport->GetTypeDefProps(classTypeDef, addr(className), classNameLength, &classNameLength, &typeDefFlags, nullptr);
if (FAILED(hr))
{
break;
}
classStack.push(std::move(className));
if (!IsTdNested(typeDefFlags))
{
break;
}
hr = metaDataImport->GetNestedClassProps(classTypeDef, &classTypeDef);
if (FAILED(hr))
{
break;
}
}
return classStack;
}
static HRESULT Foo(ICorProfilerInfo10* corProfilerInfo, UINT_PTR functionId, std::atomic<int64_t> & totalILBytesJitted)
{
UINT_PTR moduleId;
mdToken methodToken;
IfFailRet(corProfilerInfo->GetFunctionInfo(functionId, nullptr, &moduleId, &methodToken));
ULONG methodILSize;
IfFailRet(corProfilerInfo->GetILFunctionBody(moduleId, methodToken, nullptr, &methodILSize));
totalILBytesJitted += methodILSize;
CComPtr<IMetaDataImport> metadataImport;
IfFailRet(corProfilerInfo->GetModuleMetaData(moduleId, ofRead, IID_IMetaDataImport, reinterpret_cast<IUnknown * *>(&metadataImport)));
mdTypeDef classTypeDef;
ULONG methodNameLength = 0;
ULONG sigLength = 0;
PCCOR_SIGNATURE sig;
IfFailRet(metadataImport->GetMethodProps(methodToken, &classTypeDef, nullptr, 0, &methodNameLength, nullptr, &sig, &sigLength, nullptr, nullptr));
portable_wide_string methodName(methodNameLength, ZEROSTRING);
IfFailRet(metadataImport->GetMethodProps(methodToken, nullptr, &methodName[0], methodNameLength, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr));
IfFailRet(metadataImport->GetTypeDefProps(classTypeDef, nullptr, 0, nullptr, nullptr, nullptr));
std::stack<portable_wide_string> classStack(BuildClassStack(metadataImport, classTypeDef));
portable_wide_string fullClassName;
while (!classStack.empty())
{
fullClassName += classStack.top();
classStack.pop();
if (!classStack.empty())
{
fullClassName += W("+");
}
}
CQuickBytes quickBytes;
portable_wide_string signatureString(PrettyPrintSigWorker(sig, sigLength, EMPTYSTRING, &quickBytes, metadataImport));
signatureString.resize(signatureString.length() + 1); // we want the null terminator when we serialize
Logger.LogEvent<JittingStartedEvent>(functionId, moduleId, methodToken, methodILSize, fullClassName, methodName, methodName);
}
BPerfProfilerCallback::BPerfProfilerCallback()
{
@ -65,6 +153,8 @@ BPerfProfilerCallback::BPerfProfilerCallback()
this->numberOfGCSegments = 0;
this->numberOfFrozenSegments = 0;
this->writer = nullptr;
}
BPerfProfilerCallback::~BPerfProfilerCallback()
@ -85,6 +175,8 @@ HRESULT STDMETHODCALLTYPE BPerfProfilerCallback::Initialize(IUnknown* pICorProfi
return E_FAIL;
}
this->writer = std::make_shared<FileWriter>(std::getenv("BPERF_LOG_PATH"));
const DWORD eventsMask = COR_PRF_MONITOR_THREADS |
COR_PRF_MONITOR_SUSPENDS |
COR_PRF_MONITOR_MODULE_LOADS |
@ -228,6 +320,46 @@ HRESULT STDMETHODCALLTYPE BPerfProfilerCallback::FunctionUnloadStarted(FunctionI
HRESULT STDMETHODCALLTYPE BPerfProfilerCallback::JITCompilationStarted(FunctionID functionId, BOOL fIsSafeToBlock)
{
ModuleID moduleId;
mdToken methodToken;
IfFailRet(this->corProfilerInfo->GetFunctionInfo(functionId, nullptr, &moduleId, &methodToken));
ULONG methodILSize;
IfFailRet(this->corProfilerInfo->GetILFunctionBody(moduleId, methodToken, nullptr, &methodILSize));
this->totalILBytesJitted += methodILSize;
CComPtr<IMetaDataImport> metadataImport;
IfFailRet(this->corProfilerInfo->GetModuleMetaData(moduleId, ofRead, IID_IMetaDataImport, reinterpret_cast<IUnknown **>(&metadataImport)));
mdTypeDef classTypeDef;
ULONG methodNameLength = 0;
ULONG sigLength = 0;
PCCOR_SIGNATURE sig;
IfFailRet(metadataImport->GetMethodProps(methodToken, &classTypeDef, nullptr, 0, &methodNameLength, nullptr, &sig, &sigLength, nullptr, nullptr));
portable_wide_string methodName(methodNameLength, ZEROSTRING);
IfFailRet(metadataImport->GetMethodProps(methodToken, nullptr, &methodName[0], methodNameLength, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr));
IfFailRet(metadataImport->GetTypeDefProps(classTypeDef, nullptr, 0, nullptr, nullptr, nullptr));
std::stack<portable_wide_string> classStack(BuildClassStack(metadataImport, classTypeDef));
portable_wide_string fullClassName;
while (!classStack.empty())
{
fullClassName += classStack.top();
classStack.pop();
if (!classStack.empty())
{
fullClassName += W("+");
}
}
CQuickBytes quickBytes;
portable_wide_string signatureString(PrettyPrintSigWorker(sig, sigLength, EMPTYSTRING, &quickBytes, metadataImport));
signatureString.resize(signatureString.length() + 1); // we want the null terminator when we serialize
return S_OK;
}
@ -314,6 +446,7 @@ HRESULT STDMETHODCALLTYPE BPerfProfilerCallback::ThreadDestroyed(ThreadID thread
HRESULT STDMETHODCALLTYPE BPerfProfilerCallback::ThreadAssignedToOSThread(ThreadID managedThreadId, DWORD osThreadId)
{
Logger.AttachFileWriter(this->writer, osThreadId);
return S_OK;
}

Просмотреть файл

@ -20,6 +20,8 @@
#include <fstream>
#include "cor.h"
#include "corprof.h"
#include "CQuickBytes.h"
#include "EventLogger.h"
class BPerfProfilerCallback final : public ICorProfilerCallback9
{
@ -277,6 +279,8 @@ private:
std::atomic<size_t> numberOfGCSegments;
std::atomic<size_t> numberOfFrozenSegments;
std::shared_ptr<FileWriter> writer;
};
template <class TInterface>
@ -336,3 +340,12 @@ public:
TInterface* operator->() const = delete;
};
LPCWSTR PrettyPrintSigWorker(
PCCOR_SIGNATURE& typePtr, // type to convert,
size_t typeLen, // length of type
const WCHAR* name, // can be "", the name of the method for this sig
CQuickBytes* out, // where to put the pretty printed string
IMetaDataImport* pIMDI); // Import api to use.
#define addr(x) &x[0]

Просмотреть файл

@ -0,0 +1,162 @@
#pragma once
#include <memory>
#include <atomic>
#include <vector>
#include "FileWriter.h"
#include "PortableString.h"
#include "cor.h"
#include "corprof.h"
#define THRESHOLD 64 * 1024
class EventLogger
{
public:
EventLogger()
{
this->fileWriter = nullptr;
this->usedSpace = 0;
this->threadId = 0;
this->flushOperationPending = false;
this->space.resize(THRESHOLD * 2);
this->curr = this->space.data();
this->next = this->curr + this->space.size() / 2; // must be aligned;
}
~EventLogger()
{
this->SwitchBufferAndQueueFlush(0);
this->fileWriter = nullptr;
}
void AttachFileWriter(std::shared_ptr<FileWriter> fileWriter, int threadId)
{
this->fileWriter = fileWriter;
this->threadId = threadId;
}
template <class T, class... Types>
bool LogEvent(Types&& ... Args)
{
auto ptr = this->GetPointerForNextEvent(SizeOf(std::forward<Types>(Args)...));
if (ptr == nullptr)
{
return false;
}
T t(ptr, std::forward<Types>(Args)...);
return true;
}
private:
std::vector<char> space;
char* curr;
char* next;
size_t usedSpace;
std::atomic<bool> flushOperationPending;
std::shared_ptr<FileWriter> fileWriter;
int threadId;
private:
template <typename T>
size_t SizeOf(const T &t)
{
static_assert(false, "sdd");
}
template <typename T, typename... Rest>
size_t SizeOf(const T &t, Rest... rest)
{
return SizeOf(t) + SizeOf(rest...);
}
template<>
size_t SizeOf(const int64_t &arg)
{
return 8;
}
template<>
size_t SizeOf(const int32_t &arg)
{
return 4;
}
template<>
size_t SizeOf(const int16_t &arg)
{
return 2;
}
template<>
size_t SizeOf(const portable_wide_string & arg)
{
return (arg.length() + 1) * sizeof(portable_wide_char);
}
template<>
size_t SizeOf(const UINT_PTR &arg)
{
return 8;
}
template<>
size_t SizeOf(const mdToken &arg)
{
return 4;
}
template<>
size_t SizeOf(const ULONG &arg)
{
return 4;
}
template <class T, class... Types>
size_t SizeOF(Types&& ... Args)
{
return SizeOF<T>;
}
__declspec(noinline) void* SwitchBufferAndQueueFlush(size_t size)
{
if (this->flushOperationPending)
{
return nullptr;
}
if (this->fileWriter == nullptr)
{
return nullptr;
}
this->flushOperationPending = true;
this->fileWriter->Enqueue(FileWriteOperation(this->curr, this->usedSpace, 0, 0, &this->flushOperationPending));
auto tmp = this->curr;
this->curr = this->next;
this->next = tmp;
this->usedSpace = size;
return this->curr;
}
void* GetPointerForNextEvent(size_t size)
{
auto retval = this->curr + this->usedSpace;
if (size + this->usedSpace < THRESHOLD) [[likely]]
{
this->usedSpace += size;
return retval;
}
else
{
return this->SwitchBufferAndQueueFlush(size);
}
}
};

Просмотреть файл

@ -0,0 +1,23 @@
#pragma once
#include <atomic>
class FileWriteOperation
{
public:
FileWriteOperation(void* buffer, size_t size, size_t minEventTime, size_t maxEventTime, std::atomic<bool>* flushOperationPending)
{
this->Buffer = buffer;
this->Size = size;
this->MinEventTime = minEventTime;
this->MaxEventTime = maxEventTime;
this->FlushOperationPending = flushOperationPending;
}
void* Buffer;
size_t Size;
size_t MinEventTime;
size_t MaxEventTime;
std::atomic<bool>* FlushOperationPending;
};

Просмотреть файл

@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#pragma once
#include <fstream>
#include <string>
#include <thread>
#include "FileWriteOperation.h"
#include "ThreadSafeQueue.h"
class FileWriter
{
private:
ThreadSafeQueue<FileWriteOperation> eventQueue;
std::thread diskWriteThread;
std::ofstream stream;
public:
FileWriter(std::string filepath)
{
this->stream = std::ofstream(filepath.c_str(), std::ios::out | std::ios::trunc | std::ios::binary);
this->diskWriteThread = std::thread([this] {
while (true)
{
auto t = this->eventQueue.pop();
if (t.Buffer == nullptr) // break condition
{
break;
}
this->stream.write(static_cast<char*>(t.Buffer), t.Size);
*t.FlushOperationPending = false;
}
});
}
~FileWriter()
{
this->diskWriteThread.join();
this->stream.close();
}
void Enqueue(FileWriteOperation op)
{
this->eventQueue.push(op);
}
};

Просмотреть файл

@ -0,0 +1,12 @@
#pragma once
#include "profiler_pal.h"
class JittingStartedEvent
{
public:
JittingStartedEvent(void* ptr, int64_t methodId, int64_t moduleId, int32_t methodToken, int32_t methodILSize, const portable_wide_string& methodNamespace, const portable_wide_string& methodName, const portable_wide_string& methodSignature)
{
}
};

Просмотреть файл

@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#pragma once
#include <queue>
#include <mutex>
#include <condition_variable>
template <class T>
class ThreadSafeQueue
{
public:
void push(T t)
{
{
std::lock_guard<std::mutex> lock(this->mutex);
this->queue.push(std::move(t));
}
this->condition_variable.notify_one();
}
T pop()
{
std::unique_lock<std::mutex> lock(this->mutex);
this->condition_variable.wait(lock, [this] { return !this->queue.empty(); });
T t(std::move(this->queue.front()));
this->queue.pop();
return t;
}
private:
std::queue<T> queue;
std::mutex mutex;
std::condition_variable condition_variable;
};