зеркало из https://github.com/microsoft/BPerf.git
This commit is contained in:
Родитель
82e68ef443
Коммит
30b067ddb0
|
@ -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;
|
||||
};
|
Загрузка…
Ссылка в новой задаче