зеркало из https://github.com/microsoft/BPerf.git
Initial import to GitHub
This commit is contained in:
Коммит
78be480e8c
|
@ -0,0 +1,258 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
.vscode/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# DNX
|
||||
project.lock.json
|
||||
artifacts/
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignoreable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
orleans.codegen.cs
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.DB-wal
|
||||
*.db
|
|
@ -0,0 +1,248 @@
|
|||
#include <thread>
|
||||
#include <Windows.h>
|
||||
#include <Evntprov.h>
|
||||
#include <Evntcons.h>
|
||||
#include <Evntrace.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#define USAGE L"Usage: BPerfSymbolDataCollector LogsDirectory [ProcesNameFilters]\n"
|
||||
#define EXAMPLE L"Example: BPerfSymbolDataCollector D:\\Data\\BPerfSymbols w3wp.exe\n"
|
||||
#define EXAMPLE2 L"Example: BPerfSymbolDataCollector D:\\Data\\BPerfSymbols w3wp.exe;sql.exe\n"
|
||||
|
||||
#define CTRLCHANDLDED L"Ctrl+C handled, stopping session.\n"
|
||||
#define LOGSESSION_NAME L"BPerfSymbols"
|
||||
#define SYMBOLS_FILE_NAME L"Symbols.bin"
|
||||
#define MAX_EVENT_SIZE 65536
|
||||
#define UCHAR_SIZE 1
|
||||
#define USHORT_SIZE 2
|
||||
#define ULONG_SIZE 4
|
||||
#define LARGE_INTEGER_SIZE 8
|
||||
#define ULONGLONGSIZE 8
|
||||
#define GUID_SIZE 16
|
||||
#define CLRJITANDLOADERKEYWORD 0x00000018
|
||||
|
||||
constexpr GUID ClrRundownProviderGuid = {0xA669021C, 0xC450, 0x4609, {0xA0, 0x35, 0x5A, 0xF5, 0x9A, 0xF4, 0xDF, 0x18}}; // {A669021C-C450-4609-A035-5AF59AF4DF18}
|
||||
constexpr GUID ClrProviderGuid = {0xE13C0D23, 0xCCBC, 0x4E12, {0x93, 0x1B, 0xD9, 0xCC, 0x2E, 0xEE, 0x27, 0xE4}}; // {E13C0D23-CCBC-4E12-931B-D9CC2EEE27E4}
|
||||
constexpr GUID CoreClrProviderGuid = {0x319DC449, 0xADA5, 0x50F7, {0x42, 0x8E, 0x95, 0x7D, 0xB6, 0x79, 0x16, 0x68}}; // {319DC449-ADA5-50F7-428E-957DB6791668}
|
||||
|
||||
constexpr USHORT DCStopComplete = 146;
|
||||
|
||||
/* Begin Global Data */
|
||||
|
||||
TRACEHANDLE sessionHandle;
|
||||
HANDLE outputFile;
|
||||
BYTE writeBuffer[MAX_EVENT_SIZE];
|
||||
bool ctrlCTriggered = false;
|
||||
|
||||
/* End Global Data */
|
||||
|
||||
BOOL WINAPI ConsoleControlHandler(DWORD ctrlType)
|
||||
{
|
||||
switch (ctrlType)
|
||||
{
|
||||
case CTRL_C_EVENT:
|
||||
case CTRL_BREAK_EVENT:
|
||||
wprintf(CTRLCHANDLDED);
|
||||
ctrlCTriggered = true;
|
||||
EVENT_TRACE_PROPERTIES properties;
|
||||
ZeroMemory(&properties, sizeof(EVENT_TRACE_PROPERTIES));
|
||||
properties.Wnode.BufferSize = sizeof(EVENT_TRACE_PROPERTIES);
|
||||
ControlTrace(0, LOGSESSION_NAME, &properties, EVENT_TRACE_CONTROL_STOP);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ULONG WINAPI BufferCallback(_In_ PEVENT_TRACE_LOGFILEW callBack)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(callBack);
|
||||
return 1;
|
||||
}
|
||||
|
||||
VOID WINAPI EventCallback(_In_ PEVENT_RECORD eventRecord)
|
||||
{
|
||||
CopyMemory(writeBuffer, &eventRecord->EventHeader.TimeStamp, LARGE_INTEGER_SIZE);
|
||||
CopyMemory(writeBuffer + LARGE_INTEGER_SIZE, &eventRecord->EventHeader.ProcessId, ULONG_SIZE);
|
||||
CopyMemory(writeBuffer + LARGE_INTEGER_SIZE + ULONG_SIZE, &eventRecord->EventHeader.ThreadId, ULONG_SIZE);
|
||||
CopyMemory(writeBuffer + LARGE_INTEGER_SIZE + ULONG_SIZE + ULONG_SIZE, &eventRecord->EventHeader.ProviderId, GUID_SIZE);
|
||||
CopyMemory(writeBuffer + LARGE_INTEGER_SIZE + ULONG_SIZE + ULONG_SIZE + GUID_SIZE, &eventRecord->EventHeader.EventDescriptor.Id, USHORT_SIZE);
|
||||
CopyMemory(writeBuffer + LARGE_INTEGER_SIZE + ULONG_SIZE + ULONG_SIZE + GUID_SIZE + USHORT_SIZE, &eventRecord->EventHeader.EventDescriptor.Version, UCHAR_SIZE);
|
||||
CopyMemory(writeBuffer + LARGE_INTEGER_SIZE + ULONG_SIZE + ULONG_SIZE + GUID_SIZE + USHORT_SIZE + UCHAR_SIZE, &eventRecord->EventHeader.EventDescriptor.Level, UCHAR_SIZE);
|
||||
CopyMemory(writeBuffer + LARGE_INTEGER_SIZE + ULONG_SIZE + ULONG_SIZE + GUID_SIZE + USHORT_SIZE + UCHAR_SIZE + UCHAR_SIZE, &eventRecord->EventHeader.EventDescriptor.Channel, UCHAR_SIZE);
|
||||
CopyMemory(writeBuffer + LARGE_INTEGER_SIZE + ULONG_SIZE + ULONG_SIZE + GUID_SIZE + USHORT_SIZE + UCHAR_SIZE + UCHAR_SIZE + UCHAR_SIZE, &eventRecord->EventHeader.EventDescriptor.Opcode, UCHAR_SIZE);
|
||||
CopyMemory(writeBuffer + LARGE_INTEGER_SIZE + ULONG_SIZE + ULONG_SIZE + GUID_SIZE + USHORT_SIZE + UCHAR_SIZE + UCHAR_SIZE + UCHAR_SIZE + UCHAR_SIZE, &eventRecord->EventHeader.EventDescriptor.Task, USHORT_SIZE);
|
||||
CopyMemory(writeBuffer + LARGE_INTEGER_SIZE + ULONG_SIZE + ULONG_SIZE + GUID_SIZE + USHORT_SIZE + UCHAR_SIZE + UCHAR_SIZE + UCHAR_SIZE + UCHAR_SIZE + USHORT_SIZE, &eventRecord->EventHeader.EventDescriptor.Keyword, ULONGLONGSIZE);
|
||||
CopyMemory(writeBuffer + LARGE_INTEGER_SIZE + ULONG_SIZE + ULONG_SIZE + GUID_SIZE + USHORT_SIZE + UCHAR_SIZE + UCHAR_SIZE + UCHAR_SIZE + UCHAR_SIZE + USHORT_SIZE + ULONGLONGSIZE, &eventRecord->UserDataLength, USHORT_SIZE);
|
||||
CopyMemory(writeBuffer + LARGE_INTEGER_SIZE + ULONG_SIZE + ULONG_SIZE + GUID_SIZE + USHORT_SIZE + UCHAR_SIZE + UCHAR_SIZE + UCHAR_SIZE + UCHAR_SIZE + USHORT_SIZE + ULONGLONGSIZE + USHORT_SIZE, eventRecord->UserData, eventRecord->UserDataLength);
|
||||
|
||||
DWORD numbBytesToWrite = LARGE_INTEGER_SIZE + ULONG_SIZE + ULONG_SIZE + GUID_SIZE + USHORT_SIZE + UCHAR_SIZE + UCHAR_SIZE + UCHAR_SIZE + UCHAR_SIZE + USHORT_SIZE + ULONGLONGSIZE + USHORT_SIZE + eventRecord->UserDataLength, offset = 0;
|
||||
DWORD numberOfBytesWritten = 0;
|
||||
|
||||
while (WriteFile(outputFile, writeBuffer + offset, numbBytesToWrite, &numberOfBytesWritten, nullptr))
|
||||
{
|
||||
if (numberOfBytesWritten == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
offset += numberOfBytesWritten;
|
||||
numbBytesToWrite -= numberOfBytesWritten;
|
||||
}
|
||||
|
||||
if (eventRecord->EventHeader.ProviderId == ClrRundownProviderGuid && eventRecord->EventHeader.EventDescriptor.Id == DCStopComplete)
|
||||
{
|
||||
EnableTraceEx2(sessionHandle, &ClrRundownProviderGuid, EVENT_CONTROL_CODE_DISABLE_PROVIDER, TRACE_LEVEL_VERBOSE, 0, 0, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT StartSymbolicDataSession(_In_ LPWSTR sessionName, _In_ LPCWSTR outputFileName, _In_opt_ LPCWSTR exeNamesFilter)
|
||||
{
|
||||
auto sessionNameSizeInBytes = (wcslen(sessionName) + 1) * sizeof(WCHAR);
|
||||
auto bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sessionNameSizeInBytes + sizeof(WCHAR); // for '\0'
|
||||
auto sessionProperties = static_cast<PEVENT_TRACE_PROPERTIES>(malloc(bufferSize));
|
||||
|
||||
if (sessionProperties == nullptr)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
ZeroMemory(sessionProperties, bufferSize);
|
||||
|
||||
sessionProperties->Wnode.BufferSize = static_cast<ULONG>(bufferSize);
|
||||
sessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
||||
sessionProperties->Wnode.ClientContext = 1;
|
||||
|
||||
sessionProperties->MinimumBuffers = max(64, std::thread::hardware_concurrency() * 2);
|
||||
sessionProperties->MaximumBuffers = sessionProperties->MinimumBuffers * 2;
|
||||
|
||||
sessionProperties->BufferSize = 1024; // 1MB
|
||||
sessionProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_INDEPENDENT_SESSION_MODE;
|
||||
sessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
|
||||
sessionProperties->LogFileNameOffset = 0; // real time session has no file name
|
||||
sessionProperties->FlushTimer = 1; // 1 second
|
||||
|
||||
StringCbCopy(reinterpret_cast<LPWSTR>(reinterpret_cast<char *>(sessionProperties) + sessionProperties->LoggerNameOffset), sessionNameSizeInBytes, sessionName);
|
||||
|
||||
HRESULT hr;
|
||||
auto retryCount = 1;
|
||||
|
||||
while (retryCount < 10)
|
||||
{
|
||||
hr = StartTrace(&sessionHandle, sessionName, sessionProperties);
|
||||
if (hr != ERROR_SUCCESS)
|
||||
{
|
||||
if (hr == ERROR_ALREADY_EXISTS || hr == ERROR_INVALID_PARAMETER)
|
||||
{
|
||||
hr = ControlTrace(sessionHandle, sessionName, sessionProperties, EVENT_TRACE_CONTROL_STOP);
|
||||
if (hr == ERROR_WMI_INSTANCE_NOT_FOUND || hr == ERROR_SUCCESS)
|
||||
{
|
||||
Sleep(100 * retryCount); // sleep for 100-milliseconds to let it propogate.
|
||||
retryCount++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
outputFile = CreateFile(outputFileName, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (outputFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return HRESULT_FROM_WIN32(::GetLastError());
|
||||
}
|
||||
|
||||
EVENT_TRACE_LOGFILE logFile;
|
||||
ZeroMemory(&logFile, sizeof(EVENT_TRACE_LOGFILE));
|
||||
logFile.LogFileName = nullptr;
|
||||
logFile.LoggerName = sessionName;
|
||||
logFile.LogFileMode = PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_RAW_TIMESTAMP;
|
||||
logFile.BufferCallback = BufferCallback;
|
||||
logFile.EventRecordCallback = EventCallback;
|
||||
|
||||
TRACEHANDLE traceHandle = OpenTrace(&logFile);
|
||||
|
||||
if (traceHandle == reinterpret_cast<TRACEHANDLE>(INVALID_HANDLE_VALUE))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(::GetLastError());
|
||||
}
|
||||
|
||||
ENABLE_TRACE_PARAMETERS enableParameters;
|
||||
ZeroMemory(&enableParameters, sizeof(ENABLE_TRACE_PARAMETERS));
|
||||
enableParameters.FilterDescCount = 0;
|
||||
enableParameters.Version = ENABLE_TRACE_PARAMETERS_VERSION_2;
|
||||
|
||||
EVENT_FILTER_DESCRIPTOR descriptor[1];
|
||||
|
||||
if (exeNamesFilter != nullptr)
|
||||
{
|
||||
descriptor[0].Ptr = ULONGLONG(exeNamesFilter);
|
||||
descriptor[0].Size = static_cast<ULONG>((wcslen(exeNamesFilter) + 1) * sizeof(WCHAR));
|
||||
descriptor[0].Type = EVENT_FILTER_TYPE_EXECUTABLE_NAME;
|
||||
|
||||
enableParameters.EnableFilterDesc = descriptor;
|
||||
enableParameters.FilterDescCount = 1;
|
||||
}
|
||||
|
||||
hr = EnableTraceEx2(sessionHandle, &ClrProviderGuid, EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_VERBOSE, CLRJITANDLOADERKEYWORD, 0, 0, &enableParameters);
|
||||
if (hr != ERROR_SUCCESS)
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = EnableTraceEx2(sessionHandle, &CoreClrProviderGuid, EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_VERBOSE, CLRJITANDLOADERKEYWORD, 0, 0, &enableParameters);
|
||||
if (hr != ERROR_SUCCESS)
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = EnableTraceEx2(sessionHandle, &ClrRundownProviderGuid, EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_VERBOSE, 0x00020118, 0, 0, &enableParameters);
|
||||
if (hr != ERROR_SUCCESS)
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
|
||||
ProcessTrace(&traceHandle, 1, nullptr, nullptr);
|
||||
CloseHandle(outputFile);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int wmain(_In_ int argc, _In_ PWSTR *argv)
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
wprintf(USAGE);
|
||||
wprintf(EXAMPLE);
|
||||
wprintf(EXAMPLE2);
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto flushThread = std::thread([] {
|
||||
while (true)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
FlushFileBuffers(outputFile);
|
||||
}
|
||||
});
|
||||
|
||||
SetConsoleCtrlHandler(ConsoleControlHandler, true);
|
||||
SetCurrentDirectory(argv[1]);
|
||||
|
||||
wprintf(L"\n BPerfSymbolDataCollector is running ...\n");
|
||||
wprintf(L" ETW Session Name: ");
|
||||
wprintf(LOGSESSION_NAME);
|
||||
wprintf(L"\n Symbol File: ");
|
||||
wprintf(argv[1]);
|
||||
wprintf(L"\\");
|
||||
wprintf(SYMBOLS_FILE_NAME);
|
||||
wprintf(L"\n\n ** Provide this file location when starting up BPerfCPUSampleCollector **\n");
|
||||
|
||||
StartSymbolicDataSession(LOGSESSION_NAME, SYMBOLS_FILE_NAME, argc == 3 ? argv[2] : nullptr);
|
||||
|
||||
flushThread.detach(); // let process tear down take care of this
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26020.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BPerfSymbolDataCollector", "BPerfSymbolDataCollector.vcxproj", "{41DC00DB-09FE-4938-AE08-DE478CF5BC88}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{41DC00DB-09FE-4938-AE08-DE478CF5BC88}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{41DC00DB-09FE-4938-AE08-DE478CF5BC88}.Debug|x64.Build.0 = Debug|x64
|
||||
{41DC00DB-09FE-4938-AE08-DE478CF5BC88}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{41DC00DB-09FE-4938-AE08-DE478CF5BC88}.Debug|x86.Build.0 = Debug|Win32
|
||||
{41DC00DB-09FE-4938-AE08-DE478CF5BC88}.Release|x64.ActiveCfg = Release|x64
|
||||
{41DC00DB-09FE-4938-AE08-DE478CF5BC88}.Release|x64.Build.0 = Release|x64
|
||||
{41DC00DB-09FE-4938-AE08-DE478CF5BC88}.Release|x86.ActiveCfg = Release|Win32
|
||||
{41DC00DB-09FE-4938-AE08-DE478CF5BC88}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,122 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{41DC00DB-09FE-4938-AE08-DE478CF5BC88}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>BPerfSymbolDataCollector</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BPerfSymbolDataCollector.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BPerfSymbolDataCollector.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26020.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BPerfCPUSamplesCollector", "BPerfCPUSamplesCollector.vcxproj", "{628BB6D0-1469-4652-9DF9-B7FEC89F0548}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{628BB6D0-1469-4652-9DF9-B7FEC89F0548}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{628BB6D0-1469-4652-9DF9-B7FEC89F0548}.Debug|x64.Build.0 = Debug|x64
|
||||
{628BB6D0-1469-4652-9DF9-B7FEC89F0548}.Release|x64.ActiveCfg = Release|x64
|
||||
{628BB6D0-1469-4652-9DF9-B7FEC89F0548}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{628BB6D0-1469-4652-9DF9-B7FEC89F0548}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>BPerfCPUSamplesCollector</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>version.lib;dbghelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>version.lib;dbghelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="JittedCodeResolutionSupportCode.cpp" />
|
||||
<ClCompile Include="KernelTraceControlSupportCode.cpp" />
|
||||
<ClCompile Include="Program.cpp" />
|
||||
<ClCompile Include="TraceEventCallback.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="TraceEventCallback.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="JittedCodeResolutionSupportCode.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KernelTraceControlSupportCode.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Program.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TraceEventCallback.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="TraceEventCallback.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,276 @@
|
|||
#include <relogger.h>
|
||||
|
||||
#define MAX_EVENT_SIZE 65536
|
||||
#define ULONG_SIZE 4
|
||||
#define LARGE_INTEGER_SIZE 8
|
||||
#define ULONGLONGSIZE 8
|
||||
#define USHORT_SIZE 2
|
||||
#define GUID_SIZE 16
|
||||
#define UCHAR_SIZE 1
|
||||
|
||||
BYTE userData[MAX_EVENT_SIZE];
|
||||
|
||||
bool Read(PBYTE buf, LARGE_INTEGER &retVal, DWORD &bytesAvailable)
|
||||
{
|
||||
if (bytesAvailable >= LARGE_INTEGER_SIZE)
|
||||
{
|
||||
buf -= bytesAvailable;
|
||||
retVal.LowPart = buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0];
|
||||
retVal.HighPart = buf[7] << 24 | buf[6] << 16 | buf[5] << 8 | buf[4];
|
||||
bytesAvailable -= LARGE_INTEGER_SIZE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Read(PBYTE buf, ULONGLONG &retVal, DWORD &bytesAvailable)
|
||||
{
|
||||
if (bytesAvailable >= ULONGLONGSIZE)
|
||||
{
|
||||
buf -= bytesAvailable;
|
||||
retVal = static_cast<ULONGLONG>(buf[0]) | static_cast<ULONGLONG>(buf[1]) << 8 | static_cast<ULONGLONG>(buf[2]) << 16 | static_cast<ULONGLONG>(buf[3]) << 24 | static_cast<ULONGLONG>(buf[4]) << 32 | static_cast<ULONGLONG>(buf[5]) << 40 | static_cast<ULONGLONG>(buf[6]) << 48 | static_cast<ULONGLONG>(buf[7]) << 56;
|
||||
bytesAvailable -= ULONGLONGSIZE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Read(PBYTE buf, ULONG &retVal, DWORD &bytesAvailable)
|
||||
{
|
||||
if (bytesAvailable >= ULONG_SIZE)
|
||||
{
|
||||
buf -= bytesAvailable;
|
||||
retVal = buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0];
|
||||
bytesAvailable -= ULONG_SIZE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Read(PBYTE buf, USHORT &retVal, DWORD &bytesAvailable)
|
||||
{
|
||||
if (bytesAvailable >= USHORT_SIZE)
|
||||
{
|
||||
buf -= bytesAvailable;
|
||||
retVal = buf[1] << 8 | buf[0];
|
||||
bytesAvailable -= USHORT_SIZE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Read(PBYTE buf, UCHAR &retVal, DWORD &bytesAvailable)
|
||||
{
|
||||
if (bytesAvailable >= UCHAR_SIZE)
|
||||
{
|
||||
buf -= bytesAvailable;
|
||||
retVal = buf[0];
|
||||
bytesAvailable -= UCHAR_SIZE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Read(PBYTE buf, GUID &retVal, DWORD &bytesAvailable)
|
||||
{
|
||||
if (bytesAvailable >= GUID_SIZE)
|
||||
{
|
||||
buf -= bytesAvailable;
|
||||
retVal.Data1 = buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0];
|
||||
retVal.Data2 = buf[5] << 8 | buf[4];
|
||||
retVal.Data3 = buf[7] << 8 | buf[6];
|
||||
retVal.Data4[0] = buf[8];
|
||||
retVal.Data4[1] = buf[9];
|
||||
retVal.Data4[2] = buf[10];
|
||||
retVal.Data4[3] = buf[11];
|
||||
retVal.Data4[4] = buf[12];
|
||||
retVal.Data4[5] = buf[13];
|
||||
retVal.Data4[6] = buf[14];
|
||||
retVal.Data4[7] = buf[15];
|
||||
|
||||
bytesAvailable -= GUID_SIZE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
|
||||
bool ReadAndRelogEvent(ITraceRelogger *relogger, PBYTE buf, DWORD bytesAvailable, DWORD &bytesProcessed)
|
||||
{
|
||||
bytesProcessed = 0;
|
||||
DWORD bytesAvailableABeginningOfFunction = bytesAvailable;
|
||||
buf += bytesAvailableABeginningOfFunction;
|
||||
|
||||
LARGE_INTEGER timestamp;
|
||||
ULONG processId;
|
||||
ULONG threadId;
|
||||
GUID providerId;
|
||||
|
||||
USHORT eventId;
|
||||
UCHAR version;
|
||||
UCHAR level;
|
||||
UCHAR channel;
|
||||
UCHAR opcode;
|
||||
USHORT task;
|
||||
ULONGLONG keyword;
|
||||
|
||||
USHORT userDataLength;
|
||||
|
||||
if (!Read(buf, timestamp, bytesAvailable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Read(buf, processId, bytesAvailable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Read(buf, threadId, bytesAvailable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Read(buf, providerId, bytesAvailable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Read(buf, eventId, bytesAvailable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Read(buf, version, bytesAvailable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Read(buf, level, bytesAvailable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Read(buf, channel, bytesAvailable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Read(buf, opcode, bytesAvailable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Read(buf, task, bytesAvailable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Read(buf, keyword, bytesAvailable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Read(buf, userDataLength, bytesAvailable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bytesAvailable >= userDataLength)
|
||||
{
|
||||
buf -= bytesAvailable;
|
||||
CopyMemory(userData, buf, userDataLength);
|
||||
bytesAvailable -= userDataLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ITraceEvent *evt;
|
||||
relogger->CreateEventInstance(0, 0, &evt);
|
||||
|
||||
EVENT_DESCRIPTOR eventDescriptor;
|
||||
eventDescriptor.Id = eventId;
|
||||
eventDescriptor.Version = version;
|
||||
eventDescriptor.Level = level;
|
||||
eventDescriptor.Channel = channel;
|
||||
eventDescriptor.Opcode = opcode;
|
||||
eventDescriptor.Task = task;
|
||||
eventDescriptor.Keyword = keyword;
|
||||
|
||||
evt->SetProviderId(&providerId);
|
||||
evt->SetTimeStamp(×tamp);
|
||||
evt->SetProcessId(processId);
|
||||
evt->SetThreadId(threadId);
|
||||
evt->SetEventDescriptor(&eventDescriptor);
|
||||
evt->SetPayload(userData, userDataLength);
|
||||
|
||||
relogger->Inject(evt);
|
||||
|
||||
evt->Release();
|
||||
|
||||
bytesProcessed = bytesAvailableABeginningOfFunction - bytesAvailable;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LogJittedCodeSymbolicData(HANDLE hFile, ITraceRelogger *relogger)
|
||||
{
|
||||
LARGE_INTEGER fileSize;
|
||||
auto disk_buffer = static_cast<PBYTE>(malloc(MAX_EVENT_SIZE));
|
||||
auto memory_buffer = static_cast<PBYTE>(malloc(MAX_EVENT_SIZE));
|
||||
|
||||
if (GetFileSizeEx(hFile, &fileSize) == FALSE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LONGLONG remainingFileSize = fileSize.QuadPart;
|
||||
|
||||
while (remainingFileSize > 0)
|
||||
{
|
||||
DWORD bytesToRead = static_cast<DWORD>(min(remainingFileSize, MAX_EVENT_SIZE));
|
||||
bool shouldExitAfterRetry = bytesToRead < MAX_EVENT_SIZE;
|
||||
|
||||
{
|
||||
DWORD bytesRead = 0, offset = 0;
|
||||
while (ReadFile(hFile, disk_buffer, bytesToRead - offset, &bytesRead, nullptr))
|
||||
{
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
CopyMemory(memory_buffer + offset, disk_buffer, bytesRead);
|
||||
offset += bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
remainingFileSize -= bytesToRead;
|
||||
|
||||
{
|
||||
DWORD bytesAvailableToProcess = bytesToRead, bytesProcessed = 0, offsetIntoBuffer = 0;
|
||||
while (ReadAndRelogEvent(relogger, memory_buffer + offsetIntoBuffer, bytesAvailableToProcess, bytesProcessed))
|
||||
{
|
||||
bytesAvailableToProcess -= bytesProcessed;
|
||||
offsetIntoBuffer += bytesProcessed;
|
||||
}
|
||||
|
||||
if (!shouldExitAfterRetry)
|
||||
{
|
||||
SetFilePointer(hFile, 0 - bytesAvailableToProcess, nullptr, FILE_CURRENT);
|
||||
remainingFileSize += bytesAvailableToProcess;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(disk_buffer);
|
||||
free(memory_buffer);
|
||||
}
|
|
@ -0,0 +1,367 @@
|
|||
#include <windows.h>
|
||||
#include <evntcons.h>
|
||||
#include <dbghelp.h>
|
||||
#include "TraceEventCallback.h"
|
||||
|
||||
#define SystemRoot L"\\SystemRoot"
|
||||
#define GlobalRoot L"\\\\?\\GLOBALROOT"
|
||||
|
||||
std::wstring InitWindowsDir()
|
||||
{
|
||||
auto dest = static_cast<PWCHAR>(malloc((MAX_PATH + 1) * 2));
|
||||
GetWindowsDirectory(dest, MAX_PATH);
|
||||
std::wstring windowsDirectory(dest);
|
||||
free(dest);
|
||||
return windowsDirectory;
|
||||
}
|
||||
|
||||
static std::wstring &&windowsDirectory = InitWindowsDir();
|
||||
|
||||
auto ReadInt32(PBYTE &data)
|
||||
{
|
||||
uint32_t retVal;
|
||||
char *p = reinterpret_cast<char *>(&retVal);
|
||||
|
||||
p[0] = data[0];
|
||||
p[1] = data[1];
|
||||
p[2] = data[2];
|
||||
p[3] = data[3];
|
||||
|
||||
data += 4;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
auto ReadInt64(PBYTE &data)
|
||||
{
|
||||
uint64_t retVal;
|
||||
char *p = reinterpret_cast<char *>(&retVal);
|
||||
|
||||
p[0] = data[0];
|
||||
p[1] = data[1];
|
||||
p[2] = data[2];
|
||||
p[3] = data[3];
|
||||
p[4] = data[4];
|
||||
p[5] = data[5];
|
||||
p[6] = data[6];
|
||||
p[7] = data[7];
|
||||
|
||||
data += 8;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T ReadPointer(PBYTE &data)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(data);
|
||||
throw std::logic_error("T must be uint64_t or uint32_t");
|
||||
}
|
||||
|
||||
template <>
|
||||
uint64_t ReadPointer<uint64_t>(PBYTE &data)
|
||||
{
|
||||
return ReadInt64(data);
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t ReadPointer<uint32_t>(PBYTE &data)
|
||||
{
|
||||
return ReadInt32(data);
|
||||
}
|
||||
|
||||
template <class PtrSizeType>
|
||||
class ImageLoad
|
||||
{
|
||||
public:
|
||||
PtrSizeType ImageBase;
|
||||
PtrSizeType ImageSize;
|
||||
uint32_t ProcessId;
|
||||
uint32_t ImageChecksum;
|
||||
uint32_t TimeDateStamp;
|
||||
uint32_t Reserved0;
|
||||
PtrSizeType DefaultBase;
|
||||
uint32_t Reserved1;
|
||||
uint32_t Reserved2;
|
||||
uint32_t Reserved3;
|
||||
uint32_t Reserved4;
|
||||
std::wstring FileName;
|
||||
|
||||
ImageLoad(PBYTE data)
|
||||
{
|
||||
this->ImageBase = ReadPointer<PtrSizeType>(data);
|
||||
this->ImageSize = ReadPointer<PtrSizeType>(data);
|
||||
this->ProcessId = ReadInt32(data);
|
||||
this->ImageChecksum = ReadInt32(data);
|
||||
this->TimeDateStamp = ReadInt32(data);
|
||||
this->Reserved0 = ReadInt32(data);
|
||||
this->DefaultBase = ReadPointer<PtrSizeType>(data);
|
||||
this->Reserved1 = ReadInt32(data);
|
||||
this->Reserved2 = ReadInt32(data);
|
||||
this->Reserved3 = ReadInt32(data);
|
||||
this->Reserved4 = ReadInt32(data);
|
||||
this->FileName = wstring(reinterpret_cast<const wchar_t *>(data));
|
||||
}
|
||||
};
|
||||
|
||||
typedef struct _CV_INFO_PDB70
|
||||
{
|
||||
DWORD CvSignature;
|
||||
GUID Signature;
|
||||
DWORD Age;
|
||||
char PdbFileName[1];
|
||||
} CV_INFO_PDB70, *PCV_INFO_PDB70;
|
||||
|
||||
constexpr GUID KernelTraceControlGuid = {0xB3E675D7, 0x2554, 0x4F18, {0x83, 0x0B, 0x27, 0x62, 0x73, 0x25, 0x60, 0xDE}};
|
||||
|
||||
template <typename T, size_t N>
|
||||
constexpr size_t count_of(T (&)[N])
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
constexpr size_t size_of(T (&)[N])
|
||||
{
|
||||
return N * sizeof(T);
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
constexpr size_t length_of(T (&)[N])
|
||||
{
|
||||
return N - 1;
|
||||
}
|
||||
|
||||
void InjectDbgIdEvent(ITraceRelogger *relogger, PEVENT_RECORD eventRecord, PCV_INFO_PDB70 data, int eventPointerSize, int flags, DWORD processId)
|
||||
{
|
||||
ITraceEvent *duplicateEvent;
|
||||
relogger->CreateEventInstance(0, flags, &duplicateEvent);
|
||||
|
||||
EVENT_DESCRIPTOR eventDescriptor;
|
||||
eventDescriptor.Id = -1;
|
||||
eventDescriptor.Version = 2;
|
||||
eventDescriptor.Channel = 0;
|
||||
eventDescriptor.Level = 1;
|
||||
eventDescriptor.Opcode = 36;
|
||||
eventDescriptor.Task = 0;
|
||||
eventDescriptor.Keyword = 0;
|
||||
|
||||
duplicateEvent->SetEventDescriptor(&eventDescriptor);
|
||||
|
||||
duplicateEvent->SetTimeStamp(&eventRecord->EventHeader.TimeStamp);
|
||||
duplicateEvent->SetProcessorIndex(GetEventProcessorIndex(eventRecord));
|
||||
duplicateEvent->SetProviderId(&KernelTraceControlGuid);
|
||||
duplicateEvent->SetProcessId(processId);
|
||||
duplicateEvent->SetThreadId(eventRecord->EventHeader.ThreadId);
|
||||
|
||||
auto pdbFileNameBufferLength = static_cast<int>(strlen(data->PdbFileName)) + 1;
|
||||
auto payloadSize = eventPointerSize + pdbFileNameBufferLength + 2 * sizeof(int) + sizeof(GUID);
|
||||
auto ptr = static_cast<PBYTE>(malloc(payloadSize));
|
||||
auto payload = ptr;
|
||||
|
||||
CopyMemory(ptr, eventRecord->UserData, eventPointerSize);
|
||||
ptr += eventPointerSize;
|
||||
CopyMemory(ptr, &eventRecord->EventHeader.ProcessId, sizeof(int));
|
||||
ptr += sizeof(int);
|
||||
CopyMemory(ptr, &data->Signature, sizeof(GUID));
|
||||
ptr += sizeof(GUID);
|
||||
CopyMemory(ptr, &data->Age, sizeof(int));
|
||||
ptr += sizeof(int);
|
||||
CopyMemory(ptr, data->PdbFileName, pdbFileNameBufferLength);
|
||||
|
||||
duplicateEvent->SetPayload(payload, static_cast<ULONG>(payloadSize));
|
||||
relogger->Inject(duplicateEvent);
|
||||
duplicateEvent->Release();
|
||||
free(payload);
|
||||
}
|
||||
|
||||
void InjectImgIdEvent(ITraceRelogger *relogger, PEVENT_RECORD eventRecord, PCWCHAR originalFileName, DWORD imageSize, DWORD timestamp, int eventPointerSize, int flags, DWORD processId)
|
||||
{
|
||||
ITraceEvent *duplicateEvent;
|
||||
relogger->CreateEventInstance(0, flags, &duplicateEvent);
|
||||
|
||||
EVENT_DESCRIPTOR eventDescriptor;
|
||||
eventDescriptor.Id = -1;
|
||||
eventDescriptor.Version = 2;
|
||||
eventDescriptor.Channel = 0;
|
||||
eventDescriptor.Level = 1;
|
||||
eventDescriptor.Opcode = 0;
|
||||
eventDescriptor.Task = 0;
|
||||
eventDescriptor.Keyword = 0;
|
||||
|
||||
duplicateEvent->SetEventDescriptor(&eventDescriptor);
|
||||
|
||||
duplicateEvent->SetTimeStamp(&eventRecord->EventHeader.TimeStamp);
|
||||
duplicateEvent->SetProcessorIndex(GetEventProcessorIndex(eventRecord));
|
||||
duplicateEvent->SetProviderId(&KernelTraceControlGuid);
|
||||
duplicateEvent->SetProcessId(processId);
|
||||
duplicateEvent->SetThreadId(eventRecord->EventHeader.ThreadId);
|
||||
|
||||
auto originalFileNameBufferLength = (wcslen(originalFileName) + 1) * 2;
|
||||
auto payloadSize = eventPointerSize + originalFileNameBufferLength + 4 * sizeof(int);
|
||||
auto ptr = static_cast<PBYTE>(malloc(payloadSize));
|
||||
auto payload = ptr;
|
||||
|
||||
DWORD *zeroAddr = nullptr;
|
||||
CopyMemory(ptr, eventRecord->UserData, eventPointerSize);
|
||||
ptr += eventPointerSize;
|
||||
CopyMemory(ptr, &imageSize, sizeof(int));
|
||||
ptr += sizeof(int);
|
||||
CopyMemory(ptr, &zeroAddr, sizeof(int));
|
||||
ptr += sizeof(int);
|
||||
CopyMemory(ptr, &eventRecord->EventHeader.ProcessId, sizeof(int));
|
||||
ptr += sizeof(int);
|
||||
CopyMemory(ptr, ×tamp, sizeof(int));
|
||||
ptr += sizeof(int);
|
||||
CopyMemory(ptr, originalFileName, originalFileNameBufferLength);
|
||||
|
||||
duplicateEvent->SetPayload(payload, static_cast<ULONG>(payloadSize));
|
||||
relogger->Inject(duplicateEvent);
|
||||
duplicateEvent->Release();
|
||||
free(payload);
|
||||
}
|
||||
|
||||
HRESULT InjectKernelTraceControlEventsInner(PCWCHAR peFileName, PCWCHAR originalFileName, ITraceRelogger *relogger, PEVENT_RECORD eventRecord, DWORD processId)
|
||||
{
|
||||
auto file = CreateFile(peFileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (file == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
auto mapFile = CreateFileMapping(file, nullptr, PAGE_READONLY, 0, 0, nullptr);
|
||||
if (mapFile == nullptr)
|
||||
{
|
||||
CloseHandle(file);
|
||||
return ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
auto imageBase = reinterpret_cast<PBYTE>(MapViewOfFile(mapFile, FILE_MAP_READ, 0, 0, 0));
|
||||
auto dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(imageBase);
|
||||
auto ntHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(imageBase + dosHeader->e_lfanew);
|
||||
auto header = &ntHeader->OptionalHeader;
|
||||
|
||||
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE || ntHeader->Signature != IMAGE_NT_SIGNATURE)
|
||||
{
|
||||
CloseHandle(mapFile);
|
||||
CloseHandle(file);
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
auto flags = EVENT_HEADER_FLAG_CLASSIC_HEADER;
|
||||
int eventPointerSize;
|
||||
if ((eventRecord->EventHeader.Flags & EVENT_HEADER_FLAG_64_BIT_HEADER) != 0)
|
||||
{
|
||||
eventPointerSize = 8;
|
||||
flags |= EVENT_HEADER_FLAG_64_BIT_HEADER;
|
||||
}
|
||||
else
|
||||
{
|
||||
eventPointerSize = 4;
|
||||
flags |= EVENT_HEADER_FLAG_32_BIT_HEADER;
|
||||
}
|
||||
|
||||
InjectImgIdEvent(relogger, eventRecord, originalFileName, header->SizeOfImage, ntHeader->FileHeader.TimeDateStamp, eventPointerSize, flags, processId);
|
||||
|
||||
ULONG numberOfDebugDirectories;
|
||||
auto debugDirectories = static_cast<PIMAGE_DEBUG_DIRECTORY>(ImageDirectoryEntryToData(imageBase, FALSE, IMAGE_DIRECTORY_ENTRY_DEBUG, &numberOfDebugDirectories));
|
||||
numberOfDebugDirectories /= sizeof(*debugDirectories);
|
||||
|
||||
PIMAGE_DEBUG_DIRECTORY current;
|
||||
for (current = debugDirectories; numberOfDebugDirectories != 0; --numberOfDebugDirectories, ++current)
|
||||
{
|
||||
if (current->Type == IMAGE_DEBUG_TYPE_CODEVIEW)
|
||||
{
|
||||
auto data = reinterpret_cast<PCV_INFO_PDB70>(imageBase + current->PointerToRawData);
|
||||
|
||||
if (data->CvSignature == 0x53445352) // RSDS in ASCII
|
||||
{
|
||||
InjectDbgIdEvent(relogger, eventRecord, data, eventPointerSize, flags, processId);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(mapFile);
|
||||
CloseHandle(file);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
wstring PEFileNameAndProcessIdFromEvent(PEVENT_RECORD eventRecord, DWORD *processId)
|
||||
{
|
||||
ImageLoad<T> imgLoadEvent(static_cast<PBYTE>(eventRecord->UserData));
|
||||
*processId = imgLoadEvent.ProcessId;
|
||||
wstring peFileName(imgLoadEvent.FileName.compare(0, length_of(SystemRoot), SystemRoot) == 0 ? windowsDirectory + imgLoadEvent.FileName.substr(length_of(SystemRoot)) : GlobalRoot + imgLoadEvent.FileName);
|
||||
return peFileName;
|
||||
}
|
||||
|
||||
__declspec(noinline) HRESULT InjectKernelTraceControlEvents(PEVENT_RECORD eventRecord, ITraceRelogger *relogger)
|
||||
{
|
||||
if (eventRecord->EventHeader.EventDescriptor.Opcode != DCStopOpCode)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
DWORD processId = 0;
|
||||
wstring peFileName((eventRecord->EventHeader.Flags & EVENT_HEADER_FLAG_64_BIT_HEADER) != 0 ? PEFileNameAndProcessIdFromEvent<uint64_t>(eventRecord, &processId) : PEFileNameAndProcessIdFromEvent<uint32_t>(eventRecord, &processId));
|
||||
auto peFileNamePtr = peFileName.c_str();
|
||||
|
||||
DWORD handle;
|
||||
DWORD versionSize = GetFileVersionInfoSize(peFileNamePtr, &handle);
|
||||
if (versionSize > 0)
|
||||
{
|
||||
auto buffer = malloc(versionSize);
|
||||
|
||||
if (GetFileVersionInfo(peFileNamePtr, handle, versionSize, buffer) != 0)
|
||||
{
|
||||
struct LANGANDCODEPAGE
|
||||
{
|
||||
WORD wLanguage;
|
||||
WORD wCodePage;
|
||||
} * lpTranslate;
|
||||
|
||||
UINT cbTranslate;
|
||||
|
||||
VerQueryValue(buffer, TEXT("\\VarFileInfo\\Translation"), reinterpret_cast<LPVOID *>(&lpTranslate), &cbTranslate);
|
||||
|
||||
for (unsigned i = 0; i < cbTranslate / sizeof(LANGANDCODEPAGE); ++i)
|
||||
{
|
||||
WCHAR dest[MAX_PATH + 1];
|
||||
PWCHAR lpBuffer;
|
||||
UINT dwBytes;
|
||||
|
||||
StringCchPrintf(dest, MAX_PATH, TEXT("\\StringFileInfo\\%04x%04x\\OriginalFilename"), lpTranslate[i].wLanguage, lpTranslate[i].wCodePage);
|
||||
VerQueryValue(buffer, dest, reinterpret_cast<LPVOID *>(&lpBuffer), &dwBytes);
|
||||
|
||||
if (dwBytes == 0)
|
||||
{
|
||||
StringCchPrintf(dest, MAX_PATH, TEXT("\\StringFileInfo\\%04x%04x\\InternalName"), lpTranslate[i].wLanguage, lpTranslate[i].wCodePage);
|
||||
VerQueryValue(buffer, dest, reinterpret_cast<LPVOID *>(&lpBuffer), &dwBytes);
|
||||
|
||||
if (dwBytes == 0)
|
||||
{
|
||||
lpBuffer = (PWCHAR)peFileNamePtr;
|
||||
}
|
||||
}
|
||||
|
||||
const LPWSTR lastDot = wcsrchr(lpBuffer, L'.');
|
||||
if (lastDot && !_wcsicmp(lastDot, L".MUI"))
|
||||
{
|
||||
*lastDot = UNICODE_NULL;
|
||||
}
|
||||
|
||||
InjectKernelTraceControlEventsInner(peFileNamePtr, lpBuffer, relogger, eventRecord, processId);
|
||||
}
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
InjectKernelTraceControlEventsInner(peFileNamePtr, peFileNamePtr, relogger, eventRecord, processId);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,436 @@
|
|||
#include <string>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <relogger.h>
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
#include "TraceEventCallback.h"
|
||||
#include <evntprov.h>
|
||||
|
||||
#define LOGFILE_PATH L""
|
||||
#define LOGSESSION_NAME L"BPerfProfiles"
|
||||
#define LOGFILE_NAME L"Relogger.etl"
|
||||
|
||||
typedef struct _STACK_TRACING_EVENT_ID
|
||||
{
|
||||
GUID EventGuid;
|
||||
UCHAR Type;
|
||||
UCHAR Reserved[7];
|
||||
} STACK_TRACING_EVENT_ID, *PSTACK_TRACING_EVENT_ID;
|
||||
|
||||
enum EVENT_TRACE_INFORMATION_CLASS
|
||||
{
|
||||
EventTraceTimeProfileInformation = 3,
|
||||
EventTraceStackCachingInformation = 16
|
||||
};
|
||||
|
||||
enum SYSTEM_INFORMATION_CLASS
|
||||
{
|
||||
SystemPerformanceTraceInformation = 31
|
||||
};
|
||||
|
||||
typedef struct _EVENT_TRACE_TIME_PROFILE_INFORMATION
|
||||
{
|
||||
EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass;
|
||||
ULONG ProfileInterval;
|
||||
} EVENT_TRACE_TIME_PROFILE_INFORMATION;
|
||||
|
||||
bool ctrlCTriggered = false;
|
||||
typedef int(__stdcall *NtSetSystemInformation)(int SystemInformationClass, void *SystemInformation, int SystemInformationLength);
|
||||
auto ntdll = LoadLibrary(L"ntdll.dll");
|
||||
auto addr = NtSetSystemInformation(GetProcAddress(ntdll, "NtSetSystemInformation"));
|
||||
|
||||
ULONG EnableTraceEx2Shim(_In_ TRACEHANDLE traceHandle, _In_ LPCGUID guid, _In_ BYTE level, _In_ ULONG matchAnyKeyword, _In_ ULONG matchAllKeyword, _In_ ULONG timeout, _In_opt_ LPCWSTR exeNamesFilter, _In_opt_ const USHORT *stackEventIdsFilter, _In_ USHORT stackEventIdsCount)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
EVENT_FILTER_DESCRIPTOR descriptor[2];
|
||||
ENABLE_TRACE_PARAMETERS enableParameters;
|
||||
ZeroMemory(&enableParameters, sizeof(ENABLE_TRACE_PARAMETERS));
|
||||
enableParameters.FilterDescCount = 0;
|
||||
enableParameters.Version = ENABLE_TRACE_PARAMETERS_VERSION_2;
|
||||
enableParameters.EnableFilterDesc = descriptor;
|
||||
|
||||
auto eventIdsBufferSize = ((stackEventIdsCount - 1) * 2) + sizeof(EVENT_FILTER_EVENT_ID);
|
||||
PEVENT_FILTER_EVENT_ID eventIdsBuffer;
|
||||
|
||||
eventIdsBuffer = (PEVENT_FILTER_EVENT_ID)malloc(eventIdsBufferSize);
|
||||
ZeroMemory(eventIdsBuffer, eventIdsBufferSize);
|
||||
|
||||
eventIdsBuffer->FilterIn = TRUE;
|
||||
eventIdsBuffer->Reserved = 0;
|
||||
eventIdsBuffer->Count = stackEventIdsCount;
|
||||
|
||||
auto eventIdsPtr = &eventIdsBuffer->Events[0];
|
||||
for (int i = 0; i < stackEventIdsCount; ++i)
|
||||
{
|
||||
*eventIdsPtr++ = stackEventIdsFilter[i];
|
||||
}
|
||||
|
||||
if (exeNamesFilter != nullptr)
|
||||
{
|
||||
descriptor[index].Ptr = (ULONGLONG)exeNamesFilter;
|
||||
descriptor[index].Size = static_cast<ULONG>((wcslen(exeNamesFilter) + 1) * sizeof(WCHAR));
|
||||
descriptor[index].Type = EVENT_FILTER_TYPE_EXECUTABLE_NAME;
|
||||
index++;
|
||||
}
|
||||
|
||||
if (stackEventIdsCount > 0)
|
||||
{
|
||||
descriptor[index].Ptr = (ULONGLONG)eventIdsBuffer;
|
||||
descriptor[index].Size = static_cast<ULONG>(eventIdsBufferSize);
|
||||
descriptor[index].Type = EVENT_FILTER_TYPE_STACKWALK;
|
||||
index++;
|
||||
|
||||
enableParameters.EnableProperty = EVENT_ENABLE_PROPERTY_STACK_TRACE;
|
||||
}
|
||||
|
||||
enableParameters.FilterDescCount = index; // set the right size
|
||||
|
||||
auto retVal = EnableTraceEx2(traceHandle, guid, EVENT_CONTROL_CODE_ENABLE_PROVIDER, level, matchAnyKeyword, matchAllKeyword, timeout, &enableParameters);
|
||||
|
||||
free(eventIdsBuffer);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void FillEventProperties(PEVENT_TRACE_PROPERTIES sessionProperties, ULONG bufferSize)
|
||||
{
|
||||
ZeroMemory(sessionProperties, bufferSize);
|
||||
|
||||
sessionProperties->Wnode.BufferSize = bufferSize;
|
||||
sessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
||||
sessionProperties->Wnode.ClientContext = 1;
|
||||
|
||||
sessionProperties->MinimumBuffers = max(64, std::thread::hardware_concurrency() * 2);
|
||||
sessionProperties->MaximumBuffers = sessionProperties->MinimumBuffers * 2;
|
||||
|
||||
sessionProperties->BufferSize = 1024;
|
||||
sessionProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_SYSTEM_LOGGER_MODE | EVENT_TRACE_INDEPENDENT_SESSION_MODE;
|
||||
sessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
|
||||
sessionProperties->LogFileNameOffset = 0;
|
||||
sessionProperties->FlushTimer = 1;
|
||||
|
||||
StringCbCopy(reinterpret_cast<LPWSTR>(reinterpret_cast<char *>(sessionProperties) + sessionProperties->LoggerNameOffset), (wcslen(LOGSESSION_NAME) + 1) * 2, LOGSESSION_NAME);
|
||||
}
|
||||
|
||||
HRESULT StartSession(PTRACEHANDLE sessionHandle, PEVENT_TRACE_PROPERTIES sessionProperties)
|
||||
{
|
||||
HRESULT hr = ERROR_SUCCESS;
|
||||
int retryCount = 1;
|
||||
while (retryCount < 10)
|
||||
{
|
||||
hr = StartTrace(static_cast<PTRACEHANDLE>(sessionHandle), LOGSESSION_NAME, sessionProperties);
|
||||
if (hr != ERROR_SUCCESS)
|
||||
{
|
||||
if (hr == ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
hr = ControlTrace(*sessionHandle, LOGSESSION_NAME, sessionProperties, EVENT_TRACE_CONTROL_STOP);
|
||||
if (hr == ERROR_WMI_INSTANCE_NOT_FOUND || hr == ERROR_SUCCESS)
|
||||
{
|
||||
Sleep(100 * retryCount); // sleep for 100-milliseconds to let it propogate.
|
||||
retryCount++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
bool AcquireProfilePrivileges()
|
||||
{
|
||||
bool privileged = false;
|
||||
|
||||
HANDLE tokenHandle;
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle))
|
||||
{
|
||||
TOKEN_PRIVILEGES privileges;
|
||||
|
||||
privileges.PrivilegeCount = 1;
|
||||
privileges.Privileges->Luid.LowPart = 11;
|
||||
privileges.Privileges->Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
if (AdjustTokenPrivileges(tokenHandle, false, &privileges, 0, nullptr, nullptr))
|
||||
{
|
||||
privileged = true;
|
||||
}
|
||||
|
||||
CloseHandle(tokenHandle);
|
||||
}
|
||||
|
||||
return privileged;
|
||||
}
|
||||
|
||||
HRESULT RelogTraceFile(TRACEHANDLE sessionHandle, PWSTR outputFileName, int rollOverTimeMSec, PWSTR jittedCodeSymbolicDataFile)
|
||||
{
|
||||
HRESULT hr;
|
||||
ITraceRelogger *relogger = nullptr;
|
||||
TraceEventCallback *callback = nullptr;
|
||||
|
||||
hr = CoCreateInstance(CLSID_TraceRelogger, nullptr, CLSCTX_INPROC_SERVER, __uuidof(ITraceRelogger), reinterpret_cast<LPVOID *>(&relogger));
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
wprintf(L"Failed to instantiate ITraceRelogger object: 0x%08x.\n", hr);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
relogger->SetOutputFilename(outputFileName);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
wprintf(L"Failed to set output log file: 0x%08x.\n", hr);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
TRACEHANDLE traceHandle;
|
||||
|
||||
hr = relogger->AddRealtimeTraceStream(LOGSESSION_NAME, nullptr, &traceHandle);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
wprintf(L"Failed to set log file input stream: 0x%08x.\n", hr);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
callback = new TraceEventCallback(sessionHandle, relogger, rollOverTimeMSec, jittedCodeSymbolicDataFile);
|
||||
|
||||
if (callback == nullptr)
|
||||
{
|
||||
wprintf(L"Failed to allocate callback: 0x%08x.\n", hr);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
hr = relogger->RegisterCallback(callback);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
wprintf(L"Failed to register callback: 0x%08x.\n", hr);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
DoStartRundown(sessionHandle);
|
||||
|
||||
hr = relogger->ProcessTrace();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
wprintf(L"Failed to process trace: 0x%08x.\n", hr);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
if (relogger != nullptr)
|
||||
{
|
||||
relogger->Release();
|
||||
}
|
||||
|
||||
if (callback != nullptr)
|
||||
{
|
||||
callback->Release();
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
wstring FormatProfileSting()
|
||||
{
|
||||
auto in_time_t = chrono::system_clock::to_time_t(chrono::system_clock::now());
|
||||
tm nt;
|
||||
localtime_s(&nt, &in_time_t);
|
||||
|
||||
wstringstream ss;
|
||||
ss << put_time(&nt, L"%Y-%m-%d-%H-%M-%S");
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
BOOL WINAPI ConsoleControlHandler(DWORD ctrlType)
|
||||
{
|
||||
switch (ctrlType)
|
||||
{
|
||||
case CTRL_C_EVENT:
|
||||
case CTRL_BREAK_EVENT:
|
||||
wprintf(L"Ctrl+C handled, stopping session.\n");
|
||||
ctrlCTriggered = true;
|
||||
EVENT_TRACE_PROPERTIES properties;
|
||||
ZeroMemory(&properties, sizeof(EVENT_TRACE_PROPERTIES));
|
||||
properties.Wnode.BufferSize = sizeof(EVENT_TRACE_PROPERTIES);
|
||||
ControlTrace(0, LOGSESSION_NAME, &properties, EVENT_TRACE_CONTROL_STOP);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int wmain(const int argc, PWSTR *argv)
|
||||
{
|
||||
if (argc < 4)
|
||||
{
|
||||
printf("Usage: BPerfCPUSamplesCollector CpuTimerInMilliSeconds FileRolloverPeriodSec ProfileLogsDirectory [JittedCodeSymbolicDataFile] [ProcessNameFilters]\n");
|
||||
printf("Example 1: BPerfCPUSamplesCollector 10 900 D:\\Data\\BPerfLogs D:\\Data\\BPerfSymbols\\Symbols.bin\n");
|
||||
printf("Example 2: BPerfCPUSamplesCollector 10 900 D:\\Data\\BPerfLogs D:\\Data\\BPerfSymbols\\Symbols.bin w3wp.exe\n");
|
||||
printf("Example 3: BPerfCPUSamplesCollector 10 900 D:\\Data\\BPerfLogs D:\\Data\\BPerfSymbols\\Symbols.bin w3wp.exe;sql.exe\n");
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
SetConsoleCtrlHandler(ConsoleControlHandler, true);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
wprintf(L"Failed to initialize COM: 0x%08x\n", hr);
|
||||
return HRESULT_CODE(hr);
|
||||
}
|
||||
|
||||
/* set working dir */
|
||||
SetCurrentDirectory(argv[3]);
|
||||
|
||||
auto cpuSampleMSec = _wtoi(argv[1]);
|
||||
auto rolloverTimeMSec = _wtoi(argv[2]) * 1000;
|
||||
|
||||
if (!AcquireProfilePrivileges())
|
||||
{
|
||||
wprintf(L"Failed to acquire profiling privileges. Are you running as admin?\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
int bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGFILE_PATH) + sizeof(LOGSESSION_NAME);
|
||||
auto sessionProperties = static_cast<PEVENT_TRACE_PROPERTIES>(malloc(bufferSize));
|
||||
|
||||
if (sessionProperties == nullptr)
|
||||
{
|
||||
wprintf(L"Unable to allocate %d bytes for properties structure.\n", bufferSize);
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
FillEventProperties(sessionProperties, bufferSize);
|
||||
|
||||
TRACEHANDLE sessionHandle;
|
||||
|
||||
hr = StartSession(&sessionHandle, sessionProperties);
|
||||
if (FAILED(HRESULT(hr)))
|
||||
{
|
||||
wprintf(L"StartTrace() failed with %lu\n", hr);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ULONG data = EVENT_TRACE_FLAG_PROCESS | EVENT_TRACE_FLAG_IMAGE_LOAD | EVENT_TRACE_FLAG_THREAD | EVENT_TRACE_FLAG_PROFILE;
|
||||
hr = TraceSetInformation(sessionHandle, TraceSystemTraceEnableFlagsInfo, &data, sizeof(ULONG));
|
||||
|
||||
if (hr != ERROR_SUCCESS)
|
||||
{
|
||||
wprintf(L"Failed to set trace information: 0x%08x.\n", hr);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
auto exeNamesFilter = argc == 6 ? argv[5] : nullptr;
|
||||
|
||||
EnableTraceEx2Shim(sessionHandle, &AspNetEventSource, TRACE_LEVEL_INFORMATION, 0, 0, 0, exeNamesFilter, nullptr, 0);
|
||||
EnableTraceEx2Shim(sessionHandle, &TplEventSource, TRACE_LEVEL_INFORMATION, 0x42, 0, 0, exeNamesFilter, nullptr, 0);
|
||||
EnableTraceEx2Shim(sessionHandle, &BPerfStampingGuid, TRACE_LEVEL_INFORMATION, 0, 0, 0, exeNamesFilter, nullptr, 0);
|
||||
EnableTraceEx2Shim(sessionHandle, &MonitoredScopeEventSource, TRACE_LEVEL_INFORMATION, 0, 0, 0, exeNamesFilter, nullptr, 0);
|
||||
|
||||
USHORT stacksForTcpIp[1] = {1013}; // Tcp Create Endpoint
|
||||
EnableTraceEx2Shim(sessionHandle, &TcpIpProviderGuid, TRACE_LEVEL_INFORMATION, 0x1, 0, 0, exeNamesFilter, stacksForTcpIp, 1);
|
||||
|
||||
USHORT stacksForClr[3] = {80, 250, 252}; // Exception Start, Catch, Finally
|
||||
EnableTraceEx2Shim(sessionHandle, &ClrGuid, TRACE_LEVEL_INFORMATION, 0x18011, 0, 0, exeNamesFilter, stacksForClr, 3);
|
||||
EnableTraceEx2Shim(sessionHandle, &CoreClrGuid, TRACE_LEVEL_INFORMATION, 0x18011, 0, 0, exeNamesFilter, stacksForClr, 3);
|
||||
|
||||
if (hr != ERROR_SUCCESS)
|
||||
{
|
||||
wprintf(L"Failed to enable bperf stamping guid: 0x%08x.\n", hr);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
STACK_TRACING_EVENT_ID stacks;
|
||||
stacks.EventGuid = {0xce1dbfb4, 0x137e, 0x4da6, {0x87, 0xb0, 0x3f, 0x59, 0xaa, 0x10, 0x2c, 0xbc}};
|
||||
stacks.Type = 0x2e;
|
||||
hr = TraceSetInformation(sessionHandle, TraceStackTracingInfo, &stacks, sizeof(STACK_TRACING_EVENT_ID));
|
||||
|
||||
if (hr != ERROR_SUCCESS)
|
||||
{
|
||||
wprintf(L"Failed to set stack trace information: 0x%08x.\n", hr);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
EVENT_TRACE_TIME_PROFILE_INFORMATION timeInfo;
|
||||
timeInfo.EventTraceInformationClass = EventTraceTimeProfileInformation;
|
||||
timeInfo.ProfileInterval = static_cast<int>(cpuSampleMSec * 10000.0 + .5);
|
||||
hr = addr(SystemPerformanceTraceInformation, &timeInfo, sizeof(EVENT_TRACE_TIME_PROFILE_INFORMATION)); // set interval to 10ms
|
||||
if (hr != ERROR_SUCCESS)
|
||||
{
|
||||
wprintf(L"Failed to set profile timer info\r\n");
|
||||
}
|
||||
|
||||
wprintf(L"\n BPerfCPUSamplesCollector is running ...\n");
|
||||
|
||||
wprintf(L" Profiling Timer Speed : %dms\n", cpuSampleMSec);
|
||||
wprintf(L" File Rotation Period : %ds\n", rolloverTimeMSec / 1000);
|
||||
wprintf(L" ETW Session Name: %s\n", LOGSESSION_NAME);
|
||||
wprintf(L" Profiles Directory Location: %s\n", argv[3]);
|
||||
|
||||
if (argc >= 5)
|
||||
{
|
||||
wprintf(L" Using Symbol Data File: %s\n", argv[4]);
|
||||
}
|
||||
|
||||
if (argc == 6)
|
||||
{
|
||||
wprintf(L" Process Name Filters: %s\n", argv[5]);
|
||||
}
|
||||
|
||||
wprintf(L"\n *** NOTE *** -> Profile file names start with ''BPerfProfiles'' followed by timestamp\n\n");
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (ctrlCTriggered)
|
||||
{
|
||||
wprintf(L"BPerf exiting in response to Ctrl+C.");
|
||||
break;
|
||||
}
|
||||
|
||||
auto begin = FormatProfileSting();
|
||||
|
||||
hr = RelogTraceFile(sessionHandle, LOGFILE_NAME, rolloverTimeMSec, argc >= 5 ? argv[4] : nullptr);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
wprintf(L"Exiting because Relogging failed.");
|
||||
break;
|
||||
}
|
||||
|
||||
auto end = FormatProfileSting();
|
||||
|
||||
auto destWString = (L"BPerfProfiles." + begin + L"_" + end + L".etl");
|
||||
auto dest = destWString.c_str();
|
||||
wprintf(L" Writing '%s' ...", dest);
|
||||
MoveFileW(LOGFILE_NAME, dest);
|
||||
wprintf(L" Done\n");
|
||||
}
|
||||
|
||||
timeInfo.ProfileInterval = static_cast<int>(10000.0 + .5); // set back to 1ms
|
||||
addr(SystemPerformanceTraceInformation, &timeInfo, sizeof(EVENT_TRACE_TIME_PROFILE_INFORMATION));
|
||||
|
||||
EVENT_TRACE_PROPERTIES properties;
|
||||
ZeroMemory(&properties, sizeof(EVENT_TRACE_PROPERTIES));
|
||||
properties.Wnode.BufferSize = sizeof(EVENT_TRACE_PROPERTIES);
|
||||
ControlTrace(0, LOGSESSION_NAME, &properties, EVENT_TRACE_CONTROL_STOP);
|
||||
|
||||
cleanup:
|
||||
if (sessionProperties != nullptr)
|
||||
{
|
||||
free(sessionProperties);
|
||||
}
|
||||
|
||||
CoUninitialize();
|
||||
return HRESULT_CODE(hr);
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
#include "TraceEventCallback.h"
|
||||
#include <unordered_set>
|
||||
|
||||
unordered_set<ULONG> threadingTrackingTable;
|
||||
unordered_set<ULONG> taskTrackingTable;
|
||||
|
||||
__declspec(noinline) void HandleRundowCompletion(PEVENT_RECORD eventRecord, ITraceRelogger *relogger, bool isCancelled)
|
||||
{
|
||||
if (eventRecord->EventHeader.EventDescriptor.Opcode == RundownComplete && isCancelled)
|
||||
{
|
||||
relogger->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(noinline) void HandleBPerfStampingEvents(PEVENT_RECORD eventRecord)
|
||||
{
|
||||
switch (eventRecord->EventHeader.EventDescriptor.Id)
|
||||
{
|
||||
case 1:
|
||||
threadingTrackingTable.insert(eventRecord->EventHeader.ThreadId);
|
||||
break;
|
||||
case 2:
|
||||
threadingTrackingTable.erase(eventRecord->EventHeader.ThreadId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(noinline) void HandleTplEvents(PEVENT_RECORD eventRecord)
|
||||
{
|
||||
switch (eventRecord->EventHeader.EventDescriptor.Id)
|
||||
{
|
||||
case TaskScheduled:
|
||||
{
|
||||
if (threadingTrackingTable.find(eventRecord->EventHeader.ThreadId) != threadingTrackingTable.end())
|
||||
{
|
||||
auto taskId = *reinterpret_cast<ULONG *>(static_cast<PBYTE>(eventRecord->UserData) + 8);
|
||||
taskTrackingTable.insert(taskId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TaskStart:
|
||||
{
|
||||
auto taskId = *reinterpret_cast<ULONG *>(static_cast<PBYTE>(eventRecord->UserData) + 8);
|
||||
if (taskTrackingTable.find(taskId) != taskTrackingTable.end())
|
||||
{
|
||||
// if yes, then we should track this thread also
|
||||
threadingTrackingTable.insert(eventRecord->EventHeader.ThreadId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TaskStop:
|
||||
{
|
||||
auto taskId = *reinterpret_cast<ULONG *>(static_cast<PBYTE>(eventRecord->UserData) + 8);
|
||||
taskTrackingTable.erase(taskId);
|
||||
threadingTrackingTable.erase(eventRecord->EventHeader.ThreadId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(noinline) void ResetThreadState(PEVENT_RECORD eventRecord)
|
||||
{
|
||||
if (eventRecord->EventHeader.ProviderId == ThreadGuid ||
|
||||
eventRecord->EventHeader.ProviderId == ClrGuid &&
|
||||
(eventRecord->EventHeader.EventDescriptor.Id == ThreadPoolWorkerThreadStartOpCode ||
|
||||
eventRecord->EventHeader.EventDescriptor.Id == ThreadPoolWorkerThreadStopOpCode))
|
||||
{
|
||||
threadingTrackingTable.erase(eventRecord->EventHeader.ThreadId);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE TraceEventCallback::OnEvent(ITraceEvent *traceEvent, ITraceRelogger *relogger)
|
||||
{
|
||||
PEVENT_RECORD eventRecord;
|
||||
traceEvent->GetEventRecord(&eventRecord);
|
||||
|
||||
if (eventRecord->EventHeader.ProviderId == ThreadGuid && eventRecord->EventHeader.EventDescriptor.Opcode == CSwitchOpCode)
|
||||
{
|
||||
auto oldThreadId = *reinterpret_cast<ULONG *>(static_cast<PBYTE>(eventRecord->UserData) + 4);
|
||||
if (threadingTrackingTable.find(oldThreadId) != threadingTrackingTable.end())
|
||||
{
|
||||
relogger->Inject(traceEvent);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (eventRecord->EventHeader.ProviderId == TplEventSource)
|
||||
{
|
||||
HandleTplEvents(eventRecord);
|
||||
}
|
||||
|
||||
if (eventRecord->EventHeader.ProviderId == BPerfStampingGuid)
|
||||
{
|
||||
HandleBPerfStampingEvents(eventRecord);
|
||||
}
|
||||
|
||||
if (eventRecord->EventHeader.ProviderId == ThreadGuid || eventRecord->EventHeader.ProviderId == ClrGuid)
|
||||
{
|
||||
ResetThreadState(eventRecord);
|
||||
}
|
||||
|
||||
if (eventRecord->EventHeader.ProviderId == ImageLoadGuid)
|
||||
{
|
||||
InjectKernelTraceControlEvents(eventRecord, relogger);
|
||||
}
|
||||
|
||||
if (eventRecord->EventHeader.ProviderId == RundownGuid)
|
||||
{
|
||||
HandleRundowCompletion(eventRecord, relogger, this->isCancelled);
|
||||
}
|
||||
|
||||
return relogger->Inject(traceEvent);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE TraceEventCallback::OnBeginProcessTrace(ITraceEvent *header, ITraceRelogger *relogger)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(relogger);
|
||||
UNREFERENCED_PARAMETER(header);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE TraceEventCallback::OnFinalizeProcessTrace(ITraceRelogger *relogger)
|
||||
{
|
||||
if (this->jittedCodeSymbolicDataFile != nullptr)
|
||||
{
|
||||
HANDLE hFile = CreateFileW(this->jittedCodeSymbolicDataFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
LogJittedCodeSymbolicData(hFile, relogger);
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <objbase.h>
|
||||
#include <relogger.h>
|
||||
#include <atomic>
|
||||
#include <agents.h>
|
||||
#include <ppltasks.h>
|
||||
#include <evntrace.h>
|
||||
#include <strsafe.h>
|
||||
#include <unordered_map>
|
||||
#include <evntcons.h>
|
||||
|
||||
constexpr GUID PerfInfoGuid = {0xce1dbfb4, 0x137e, 0x4da6, {0x87, 0xb0, 0x3f, 0x59, 0xaa, 0x10, 0x2c, 0xbc}};
|
||||
constexpr GUID BPerfStampingGuid = {0xD46E5565, 0xD624, 0x4C4D, {0x89, 0xCC, 0x7A, 0x82, 0x88, 0x7D, 0x36, 0x27}};
|
||||
constexpr GUID ThreadGuid = {0x3d6fa8d1, 0xfe05, 0x11d0, {0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c}};
|
||||
constexpr GUID ProcessGuid = {0x3d6fa8d0, 0xfe05, 0x11d0, {0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c}};
|
||||
constexpr GUID RundownGuid = {0x68fdd900, 0x4a3e, 0x11d1, {0x84, 0xf4, 0x00, 0x00, 0xf8, 0x04, 0x64, 0xe3}};
|
||||
constexpr GUID ImageLoadGuid = {0x2cb15d1d, 0x5fc1, 0x11d2, {0xab, 0xe1, 0x00, 0xa0, 0xc9, 0x11, 0xf5, 0x18}};
|
||||
constexpr GUID ClrGuid = {0xE13C0D23, 0xCCBC, 0x4E12, {0x93, 0x1B, 0xD9, 0xCC, 0x2E, 0xEE, 0x27, 0xE4}};
|
||||
constexpr GUID CoreClrGuid = {0x319DC449, 0xADA5, 0x50F7, {0x42, 0x8E, 0x95, 0x7D, 0xB6, 0x79, 0x16, 0x68}};
|
||||
constexpr GUID TplEventSource = {0x2e5dba47, 0xa3d2, 0x4d16, {0x8e, 0xe0, 0x66, 0x71, 0xff, 0xdc, 0xd7, 0xb5}};
|
||||
constexpr GUID AspNetEventSource = {0xee799f41, 0xcfa5, 0x550b, {0xbf, 0x2c, 0x34, 0x47, 0x47, 0xc1, 0xc6, 0x68}};
|
||||
constexpr GUID TcpIpProviderGuid = {0x2f07e2ee, 0x15db, 0x40f1, {0x90, 0xef, 0x9d, 0x7b, 0xa2, 0x82, 0x18, 0x8a}};
|
||||
constexpr GUID KernelProcessGuid = {0x22fb2cd6, 0x0e7b, 0x422b, {0xa0, 0xc7, 0x2f, 0xad, 0x1f, 0xd0, 0xe7, 0x16}};
|
||||
constexpr GUID MonitoredScopeEventSource = {0x2e185d98, 0x344d, 0x5ef7, {0x06, 0x30, 0xaf, 0xe3, 0x22, 0xf0, 0x1b, 0x98}};
|
||||
|
||||
constexpr UCHAR CSwitchOpCode = 36;
|
||||
constexpr UCHAR StackWalkOpCode = 32;
|
||||
constexpr UCHAR DCStopOpCode = 4;
|
||||
constexpr UCHAR ThreadPoolWorkerThreadStartOpCode = 50;
|
||||
constexpr UCHAR ThreadPoolWorkerThreadStopOpCode = 51;
|
||||
|
||||
constexpr USHORT TaskScheduled = 7;
|
||||
constexpr USHORT TaskStart = 8;
|
||||
constexpr USHORT TaskStop = 9;
|
||||
|
||||
constexpr USHORT RundownComplete = 8;
|
||||
|
||||
__declspec(noinline) HRESULT InjectKernelTraceControlEvents(PEVENT_RECORD eventRecord, ITraceRelogger *relogger);
|
||||
void LogJittedCodeSymbolicData(HANDLE hFile, ITraceRelogger *relogger);
|
||||
|
||||
using namespace concurrency;
|
||||
using namespace std;
|
||||
|
||||
inline void DoRundown(TRACEHANDLE tracehandle, ULONG type)
|
||||
{
|
||||
EVENT_FILTER_DESCRIPTOR descriptor[1];
|
||||
descriptor[0].Type = EVENT_FILTER_TYPE_TRACEHANDLE;
|
||||
descriptor[0].Ptr = reinterpret_cast<ULONGLONG>(&tracehandle);
|
||||
descriptor[0].Size = sizeof(TRACEHANDLE);
|
||||
|
||||
ENABLE_TRACE_PARAMETERS enableParameters;
|
||||
ZeroMemory(&enableParameters, sizeof(ENABLE_TRACE_PARAMETERS));
|
||||
enableParameters.Version = ENABLE_TRACE_PARAMETERS_VERSION_2;
|
||||
enableParameters.EnableFilterDesc = descriptor;
|
||||
enableParameters.FilterDescCount = 1;
|
||||
|
||||
EnableTraceEx2(tracehandle, &AspNetEventSource, EVENT_CONTROL_CODE_CAPTURE_STATE, 0, 0, 0, 0, &enableParameters);
|
||||
EnableTraceEx2(tracehandle, &TplEventSource, EVENT_CONTROL_CODE_CAPTURE_STATE, 0, 0, 0, 0, &enableParameters);
|
||||
EnableTraceEx2(tracehandle, &MonitoredScopeEventSource, EVENT_CONTROL_CODE_CAPTURE_STATE, 0, 0, 0, 0, &enableParameters);
|
||||
|
||||
GUID systemGuid = {0x9e814aad, 0x3204, 0x11d2, {0x9a, 0x82, 0x00, 0x60, 0x08, 0xa8, 0x69, 0x39}};
|
||||
EnableTraceEx2(tracehandle, &systemGuid, EVENT_CONTROL_CODE_CAPTURE_STATE, 0, type, 0, INFINITE, &enableParameters);
|
||||
}
|
||||
|
||||
inline void DoStartRundown(TRACEHANDLE tracehandle)
|
||||
{
|
||||
DoRundown(tracehandle, 1);
|
||||
}
|
||||
|
||||
inline void DoStopRundown(TRACEHANDLE tracehandle)
|
||||
{
|
||||
DoRundown(tracehandle, 2);
|
||||
}
|
||||
|
||||
// Creates a task that completes after the specified delay.
|
||||
inline task<void> complete_after(unsigned int timeout)
|
||||
{
|
||||
// A task completion event that is set when a timer fires.
|
||||
task_completion_event<void> tce;
|
||||
|
||||
// Create a non-repeating timer.
|
||||
auto fire_once = new timer<int>(timeout, 0, nullptr, false);
|
||||
// Create a call object that sets the completion event after the timer fires.
|
||||
auto callback = new call<int>([tce](int) {
|
||||
tce.set();
|
||||
});
|
||||
|
||||
// Connect the timer to the callback and start the timer.
|
||||
fire_once->link_target(callback);
|
||||
fire_once->start();
|
||||
|
||||
// Create a task that completes after the completion event is set.
|
||||
task<void> event_set(tce);
|
||||
|
||||
// Create a continuation task that cleans up resources and
|
||||
// and return that continuation task.
|
||||
return event_set.then([callback, fire_once]() {
|
||||
delete callback;
|
||||
delete fire_once;
|
||||
});
|
||||
}
|
||||
|
||||
class TraceEventCallback : public ITraceEventCallback
|
||||
{
|
||||
public:
|
||||
HRESULT STDMETHODCALLTYPE OnEvent(ITraceEvent *traceEvent, ITraceRelogger *relogger) override;
|
||||
HRESULT STDMETHODCALLTYPE OnBeginProcessTrace(ITraceEvent *header, ITraceRelogger *relogger) override;
|
||||
HRESULT STDMETHODCALLTYPE OnFinalizeProcessTrace(ITraceRelogger *relogger) override;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppObj) override
|
||||
{
|
||||
if (riid == __uuidof(IUnknown) || riid == __uuidof(ITraceEventCallback))
|
||||
{
|
||||
*ppObj = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ppObj = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
this->AddRef();
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef() override
|
||||
{
|
||||
return atomic_fetch_add(&this->refCount, 1) + 1;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release() override
|
||||
{
|
||||
int count = atomic_fetch_sub(&this->refCount, 1) - 1;
|
||||
if (count == 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
TraceEventCallback(TRACEHANDLE tracehandle, ITraceRelogger *relogger, int rollOverTimeMSec, PWSTR jittedCodeSymbolicDataFile)
|
||||
{
|
||||
this->isCancelled = false;
|
||||
this->refCount = 0;
|
||||
this->jittedCodeSymbolicDataFile = jittedCodeSymbolicDataFile;
|
||||
this->reloggerCancellationTask = complete_after(rollOverTimeMSec).then([this, relogger, tracehandle] {
|
||||
this->isCancelled = true;
|
||||
DoStopRundown(tracehandle);
|
||||
});
|
||||
}
|
||||
|
||||
virtual ~TraceEventCallback()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
bool isCancelled;
|
||||
atomic<int> refCount;
|
||||
task<void> reloggerCancellationTask;
|
||||
PWSTR jittedCodeSymbolicDataFile;
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BPerfCoreCLRProfiler", "src\BPerfCoreCLRProfiler.vcxproj", "{C45E1F19-79E0-447D-86DB-3E8F1D2B1311}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C45E1F19-79E0-447D-86DB-3E8F1D2B1311}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{C45E1F19-79E0-447D-86DB-3E8F1D2B1311}.Debug|x64.Build.0 = Debug|x64
|
||||
{C45E1F19-79E0-447D-86DB-3E8F1D2B1311}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{C45E1F19-79E0-447D-86DB-3E8F1D2B1311}.Debug|x86.Build.0 = Debug|Win32
|
||||
{C45E1F19-79E0-447D-86DB-3E8F1D2B1311}.Release|x64.ActiveCfg = Release|x64
|
||||
{C45E1F19-79E0-447D-86DB-3E8F1D2B1311}.Release|x64.Build.0 = Release|x64
|
||||
{C45E1F19-79E0-447D-86DB-3E8F1D2B1311}.Release|x86.ActiveCfg = Release|Win32
|
||||
{C45E1F19-79E0-447D-86DB-3E8F1D2B1311}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,102 @@
|
|||
# BPerf CoreCLR Profiler
|
||||
|
||||
This is the cross-platform managed-code user-mode profiler component of the BPerf Production Profiling system. It implements the [CLR Profiling APIs](https://msdn.microsoft.com/en-us/library/ms404386(v=vs.110).aspx) and collects data about the managed application and logs it to [SQLite](https://www.sqlite.org).
|
||||
|
||||
It is capable of profiling .NET 4.5 applications and .NET Core (a.k.a CoreCLR) applications on all supported platforms (Linux, macOS, Windows)
|
||||
|
||||
Consuming BPerf
|
||||
---------------
|
||||
|
||||
* Acquire BPerfCoreCLRProfiler(.dll|so) via our [NuGet Package](https://nuget.org/packages/BPerf) built for Windows/Ubuntu 14.04/16.04/16.10 or build from source.
|
||||
* Perform environment variable setup for your platform (documented below)
|
||||
* Setup additional BPerf options:
|
||||
|
||||
1. ``BPERF_LOG_PATH`` - Output location for the SQLite database (ensure your process has write permissions!)
|
||||
|
||||
2. ``BPERF_OVERWRITE_LOGS`` -- Whether the existing logs should be overwritten
|
||||
|
||||
3. ``BPERF_MONITOR_ALLOCATIONS`` - Monitors every object allocation and GC. _Caution_: Application most likely usable, but noticable impact on performance.
|
||||
|
||||
4. ``BPERF_MONITOR_ENTERLEAVE`` - Monitors every function entry/exit. _Caution_: Application most likely usable, but noticable impact on performance.
|
||||
|
||||
5. View data from your SQLite database
|
||||
|
||||
Building on Windows
|
||||
-------------------
|
||||
|
||||
### Environment
|
||||
|
||||
All instructions must be run on the ``VS 2015 x64 Native Tools Command Prompt``.
|
||||
|
||||
### Build
|
||||
On the ``VS 2015 x64 Native Tools Command Prompt``.
|
||||
|
||||
```batch
|
||||
msbuild BPerfCoreCLRProfiler.vcxproj
|
||||
```
|
||||
|
||||
* or open using Visual Studio 2015+ (from the aforementioned command prompt)
|
||||
|
||||
### Setup
|
||||
|
||||
Profiling for .NET (and .NET Core) applications on Windows requires the following environment variables set to indicate the ``Profiler CLSID`` (a unique GUID), a path to the unmanaged dll and whether profiling is enabled or not.
|
||||
|
||||
The following example setup is configured with the correct ``Profiler CLSID``, and an imaginary file location for the BPerf Profiler DLL.
|
||||
|
||||
```batch
|
||||
SET COR_PROFILER={D46E5565-D624-4C4D-89CC-7A82887D3626}
|
||||
SET COR_ENABLE_PROFILING=1
|
||||
SET COR_PROFILER_PATH=C:\filePath\to\BPerfCoreCLRProfiler.dll
|
||||
SET BPERF_LOG_PATH=C:\BPerfLogs.db
|
||||
YourProgram.exe OR (corerun YourProgram.exe for CoreCLR applications)
|
||||
```
|
||||
|
||||
Building on Linux, macOS
|
||||
------------------------
|
||||
|
||||
BPerf requires a CoreCLR repository built so that it can link against certain components in addition to consuming header files and other native artifacts.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
* CoreCLR Repository (built from source)
|
||||
* Clang 3.5+
|
||||
|
||||
### Environment
|
||||
|
||||
``build.sh`` expects the following environment variables to be setup; default values are shown below.
|
||||
|
||||
```bash
|
||||
export CORECLR_PATH=~/coreclr # default
|
||||
export BuildOS=Linux # Linux(default), MacOSX
|
||||
export BuildArch=x64 # x64 (default)
|
||||
export BuildType=Debug # Debug(default), Release
|
||||
export Output=CorProfiler.so # default
|
||||
```
|
||||
|
||||
``CORECLR_PATH`` is the path to your cloned and successfully built CoreCLR repository.
|
||||
|
||||
``BuildOS``, ``BuildArch`` and ``BuildType`` must match how you built the CoreCLR repository, so the header files and other artifacts needed for compilation are locatable by convention.
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
git clone http://github.com/Microsoft/BPerf
|
||||
cd CoreCLRProfiler
|
||||
./build.sh
|
||||
```
|
||||
|
||||
This produces a ``BPerfCoreCLRProfiler.so`` file you can then use in the setup when executing your .NET Core application.
|
||||
|
||||
### Setup
|
||||
|
||||
Profiling for .NET Core applications on Linux and macOS requires the following environment variables set to indicate the ``Profiler CLSID`` (a unique GUID), a path to the unmanaged shared library and whether profiling is enabled or not.
|
||||
|
||||
The following example setup is configured with the correct ``Profiler CLSID``, and an imaginary file location for the BPerf Profiler Shared Library.
|
||||
|
||||
```bash
|
||||
export CORECLR_PROFILER={D46E5565-D624-4C4D-89CC-7A82887D3626}
|
||||
export CORECLR_ENABLE_PROFILING=1
|
||||
export CORECLR_PROFILER_PATH=/filePath/to/CorProfiler.so
|
||||
export BPERF_LOG_PATH=/filePath/to/BPerfLog.db
|
||||
./corerun YourProgram.dll
|
||||
```
|
|
@ -0,0 +1,24 @@
|
|||
#!/bin/sh
|
||||
|
||||
[ -z "${CORECLR_PATH:-}" ] && CORECLR_PATH=~/coreclr
|
||||
[ -z "${BuildOS:-}" ] && BuildOS=Linux
|
||||
[ -z "${BuildArch:-}" ] && BuildArch=x64
|
||||
[ -z "${BuildType:-}" ] && BuildType=Debug
|
||||
[ -z "${Output:-}" ] && Output=BPerfCoreCLRProfiler.so
|
||||
|
||||
printf ' CORECLR_PATH : %s\n' "$CORECLR_PATH"
|
||||
printf ' BuildOS : %s\n' "$BuildOS"
|
||||
printf ' BuildArch : %s\n' "$BuildArch"
|
||||
printf ' BuildType : %s\n' "$BuildType"
|
||||
|
||||
printf ' Building %s ... ' "$Output"
|
||||
|
||||
CORECLR_BUILT_PATH=$CORECLR_PATH/bin/Product/$BuildOS.$BuildArch.$BuildType
|
||||
|
||||
CXX_FLAGS="$CXX_FLAGS --no-undefined -Wno-invalid-noreturn -fPIC -fms-extensions -DUNICODE -DBIT64 -DFEATURE_PAL -DPAL_STDCPP_COMPAT -DPLATFORM_UNIX -std=c++11"
|
||||
INCLUDES="-I $CORECLR_PATH/src/pal/inc/rt -I $CORECLR_PATH/src/pal/prebuilt/inc -I $CORECLR_PATH/src/pal/inc -I $CORECLR_PATH/src/inc -I $CORECLR_BUILT_PATH/inc"
|
||||
|
||||
clang -c -fPIC src/sqlite3.c
|
||||
clang++ -shared -ldl -luuid -lunwind-generic -lpthread -o $Output $CXX_FLAGS $INCLUDES src/*.cpp sqlite3.o $CORECLR_BUILT_PATH/lib/libcoreclrpal.a
|
||||
rm sqlite3.o
|
||||
printf 'Done.\n'
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "AppDomainLoadEvent.h"
|
||||
|
||||
AppDomainLoadEvent::AppDomainLoadEvent(int64_t appDomainId, int32_t appDomainFlags, portable_wide_string appDomainName, int32_t appDomainIndex, int16_t clrInstanceId) : EtwEvent(), appDomainId(appDomainId), appDomainFlags(appDomainFlags), appDomainName(std::move(appDomainName)), appDomainIndex(appDomainIndex), clrInstanceId(clrInstanceId)
|
||||
{
|
||||
}
|
||||
|
||||
void AppDomainLoadEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
|
||||
t.Write(this->appDomainId);
|
||||
t.Write(this->appDomainFlags);
|
||||
t.Write(this->appDomainName);
|
||||
t.Write(this->appDomainIndex);
|
||||
t.Write(this->clrInstanceId);
|
||||
}
|
||||
|
||||
int32_t AppDomainLoadEvent::GetEventId()
|
||||
{
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// 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 "EtwEvent.h"
|
||||
|
||||
class AppDomainLoadEvent : public EtwEvent
|
||||
{
|
||||
friend class EtwLogger;
|
||||
|
||||
public:
|
||||
AppDomainLoadEvent(int64_t appDomainId, int32_t appDomainFlags, portable_wide_string appDomainName, int32_t appDomainIndex, int16_t clrInstanceId);
|
||||
void Serialize(Serializer &t) override;
|
||||
int32_t GetEventId() override;
|
||||
|
||||
private:
|
||||
int64_t appDomainId;
|
||||
int32_t appDomainFlags;
|
||||
portable_wide_string appDomainName;
|
||||
int32_t appDomainIndex;
|
||||
int16_t clrInstanceId;
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "AppDomainUnloadEvent.h"
|
||||
|
||||
AppDomainUnloadEvent::AppDomainUnloadEvent(int64_t appDomainId, int32_t appDomainFlags, portable_wide_string appDomainName, int32_t appDomainIndex, int16_t clrInstanceId) : EtwEvent(), appDomainId(appDomainId), appDomainFlags(appDomainFlags), appDomainName(std::move(appDomainName)), appDomainIndex(appDomainIndex), clrInstanceId(clrInstanceId)
|
||||
{
|
||||
}
|
||||
|
||||
void AppDomainUnloadEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
|
||||
t.Write(this->appDomainId);
|
||||
t.Write(this->appDomainFlags);
|
||||
t.Write(this->appDomainName);
|
||||
t.Write(this->appDomainIndex);
|
||||
t.Write(this->clrInstanceId);
|
||||
}
|
||||
|
||||
int32_t AppDomainUnloadEvent::GetEventId()
|
||||
{
|
||||
return 2;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// 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 "EtwEvent.h"
|
||||
|
||||
class AppDomainUnloadEvent : public EtwEvent
|
||||
{
|
||||
public:
|
||||
AppDomainUnloadEvent(int64_t appDomainId, int32_t appDomainFlags, portable_wide_string appDomainName, int32_t appDomainIndex, int16_t clrInstanceId);
|
||||
void Serialize(Serializer &t) override;
|
||||
int32_t GetEventId() override;
|
||||
|
||||
private:
|
||||
int64_t appDomainId;
|
||||
int32_t appDomainFlags;
|
||||
portable_wide_string appDomainName;
|
||||
int32_t appDomainIndex;
|
||||
int16_t clrInstanceId;
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "AssemblyLoadEvent.h"
|
||||
|
||||
AssemblyLoadEvent::AssemblyLoadEvent(int64_t assemblyId, int64_t appDomainId, int64_t bindingId, int32_t assemblyFlags, portable_wide_string fullyQualifiedAssemblyName, int16_t clrInstanceId) : EtwEvent(), assemblyId(assemblyId), appDomainId(appDomainId), bindingId(bindingId), assemblyFlags(assemblyFlags), fullyQualifiedAssemblyName(std::move(fullyQualifiedAssemblyName)), clrInstanceId(clrInstanceId)
|
||||
{
|
||||
}
|
||||
|
||||
void AssemblyLoadEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
|
||||
t.Write(this->assemblyId);
|
||||
t.Write(this->appDomainId);
|
||||
t.Write(this->bindingId);
|
||||
t.Write(this->assemblyFlags);
|
||||
t.Write(this->fullyQualifiedAssemblyName);
|
||||
t.Write(this->clrInstanceId);
|
||||
}
|
||||
|
||||
int32_t AssemblyLoadEvent::GetEventId()
|
||||
{
|
||||
return 3;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// 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 "EtwEvent.h"
|
||||
|
||||
class AssemblyLoadEvent : public EtwEvent
|
||||
{
|
||||
friend class EtwLogger;
|
||||
|
||||
public:
|
||||
AssemblyLoadEvent(int64_t assemblyId, int64_t appDomainId, int64_t bindingId, int32_t assemblyFlags, portable_wide_string fullyQualifiedAssemblyName, int16_t clrInstanceId);
|
||||
void Serialize(Serializer &t) override;
|
||||
int32_t GetEventId() override;
|
||||
|
||||
private:
|
||||
int64_t assemblyId;
|
||||
int64_t appDomainId;
|
||||
int64_t bindingId;
|
||||
int32_t assemblyFlags;
|
||||
portable_wide_string fullyQualifiedAssemblyName;
|
||||
int16_t clrInstanceId;
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "AssemblyUnloadEvent.h"
|
||||
|
||||
AssemblyUnloadEvent::AssemblyUnloadEvent(int64_t assemblyId, int64_t appDomainId, int64_t bindingId, int32_t assemblyFlags, portable_wide_string fullyQualifiedAssemblyName, int16_t clrInstanceId) : EtwEvent(), assemblyId(assemblyId), appDomainId(appDomainId), bindingId(bindingId), assemblyFlags(assemblyFlags), fullyQualifiedAssemblyName(std::move(fullyQualifiedAssemblyName)), clrInstanceId(clrInstanceId)
|
||||
{
|
||||
}
|
||||
|
||||
void AssemblyUnloadEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
|
||||
t.Write(this->assemblyId);
|
||||
t.Write(this->appDomainId);
|
||||
t.Write(this->bindingId);
|
||||
t.Write(this->assemblyFlags);
|
||||
t.Write(this->fullyQualifiedAssemblyName);
|
||||
t.Write(this->clrInstanceId);
|
||||
}
|
||||
|
||||
int32_t AssemblyUnloadEvent::GetEventId()
|
||||
{
|
||||
return 4;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// 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 "EtwEvent.h"
|
||||
|
||||
class AssemblyUnloadEvent : public EtwEvent
|
||||
{
|
||||
public:
|
||||
AssemblyUnloadEvent(int64_t assemblyId, int64_t appDomainId, int64_t bindingId, int32_t assemblyFlags, portable_wide_string fullyQualifiedAssemblyName, int16_t clrInstanceId);
|
||||
void Serialize(Serializer &t) override;
|
||||
int32_t GetEventId() override;
|
||||
|
||||
private:
|
||||
int64_t assemblyId;
|
||||
int64_t appDomainId;
|
||||
int64_t bindingId;
|
||||
int32_t assemblyFlags;
|
||||
portable_wide_string fullyQualifiedAssemblyName;
|
||||
int16_t clrInstanceId;
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
LIBRARY "BPerfCoreCLRProfiler.Dll"
|
||||
|
||||
EXPORTS
|
||||
DllCanUnloadNow PRIVATE
|
||||
DllGetClassObject PRIVATE
|
|
@ -0,0 +1,200 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{C45E1F19-79E0-447D-86DB-3E8F1D2B1311}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>BPerfCoreCLRProfiler</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>$(CORECLR_PATH)\src\pal\prebuilt\inc;$(CORECLR_PATH)\src\inc;$(CORECLR_PATH)\bin\Product\$(BuildOS).$(BuildArch).$(BuildType)\inc;$(NETFXSDKDir)Include\um;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>$(CORECLR_PATH)\src\pal\prebuilt\inc;$(CORECLR_PATH)\src\inc;$(CORECLR_PATH)\bin\Product\$(BuildOS).$(BuildArch).$(BuildType)\inc;$(NETFXSDKDir)Include\um;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>$(CORECLR_PATH)\src\pal\prebuilt\inc;$(CORECLR_PATH)\src\inc;$(CORECLR_PATH)\bin\Product\$(BuildOS).$(BuildArch).$(BuildType)\inc;$(NETFXSDKDir)Include\um;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>$(CORECLR_PATH)\src\pal\prebuilt\inc;$(CORECLR_PATH)\src\inc;$(CORECLR_PATH)\bin\Product\$(BuildOS).$(BuildArch).$(BuildType)\inc;$(NETFXSDKDir)Include\um;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;BPERFCORECLRPROFILER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ModuleDefinitionFile>BPerfCoreCLRProfiler.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;BPERFCORECLRPROFILER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ModuleDefinitionFile>BPerfCoreCLRProfiler.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;BPERFCORECLRPROFILER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ModuleDefinitionFile>BPerfCoreCLRProfiler.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;BPERFCORECLRPROFILER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ModuleDefinitionFile>BPerfCoreCLRProfiler.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AppDomainLoadEvent.h" />
|
||||
<ClInclude Include="AppDomainUnloadEvent.h" />
|
||||
<ClInclude Include="AssemblyLoadEvent.h" />
|
||||
<ClInclude Include="AssemblyUnloadEvent.h" />
|
||||
<ClInclude Include="BPerfProfiler.h" />
|
||||
<ClInclude Include="CComPtr.h" />
|
||||
<ClInclude Include="ClassFactory.h" />
|
||||
<ClInclude Include="CQuickBytes.h" />
|
||||
<ClInclude Include="EtwEvent.h" />
|
||||
<ClInclude Include="EtwLogger.h" />
|
||||
<ClInclude Include="GuidDef.h" />
|
||||
<ClInclude Include="HeartBeatEvent.h" />
|
||||
<ClInclude Include="ILRewriter.h" />
|
||||
<ClInclude Include="JITCompilationFinishedEvent.h" />
|
||||
<ClInclude Include="JITCompilationStartedEvent.h" />
|
||||
<ClInclude Include="MethodEnterEvent.h" />
|
||||
<ClInclude Include="MethodILToNativeMapEvent.h" />
|
||||
<ClInclude Include="MethodUnloadEvent.h" />
|
||||
<ClInclude Include="ModuleLoadEvent.h" />
|
||||
<ClInclude Include="ModuleUnloadEvent.h" />
|
||||
<ClInclude Include="PortableString.h" />
|
||||
<ClInclude Include="profiler_pal.h" />
|
||||
<ClInclude Include="EtwLogger.Generated.h" />
|
||||
<ClInclude Include="RundownList.h" />
|
||||
<ClInclude Include="RundownListNode.h" />
|
||||
<ClInclude Include="RuntimeInformationEvent.h" />
|
||||
<ClInclude Include="Serializer.h" />
|
||||
<ClInclude Include="sha1.h" />
|
||||
<ClInclude Include="ShutdownEvent.h" />
|
||||
<ClInclude Include="sqlite3.h" />
|
||||
<ClInclude Include="SQLiteEventLogger.h" />
|
||||
<ClInclude Include="SQLiteSerializer.h" />
|
||||
<ClInclude Include="StartupEvent.h" />
|
||||
<ClInclude Include="ThreadSafeQueue.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AppDomainLoadEvent.cpp" />
|
||||
<ClCompile Include="AppDomainUnloadEvent.cpp" />
|
||||
<ClCompile Include="AssemblyLoadEvent.cpp" />
|
||||
<ClCompile Include="AssemblyUnloadEvent.cpp" />
|
||||
<ClCompile Include="BPerfProfiler.cpp" />
|
||||
<ClCompile Include="BPerfProfiler.PrivateHelpers.cpp" />
|
||||
<ClCompile Include="ClassFactory.cpp" />
|
||||
<ClCompile Include="CQuickBytes.cpp" />
|
||||
<ClCompile Include="dllmain.cpp" />
|
||||
<ClCompile Include="EtwEvent.cpp" />
|
||||
<ClCompile Include="HeartBeatEvent.cpp" />
|
||||
<ClCompile Include="ILRewriter.cpp" />
|
||||
<ClCompile Include="JITCompilationFinishedEvent.cpp" />
|
||||
<ClCompile Include="JITCompilationStartedEvent.cpp" />
|
||||
<ClCompile Include="MethodEnterEvent.cpp" />
|
||||
<ClCompile Include="MethodILToNativeMapEvent.cpp" />
|
||||
<ClCompile Include="MethodUnloadEvent.cpp" />
|
||||
<ClCompile Include="ModuleLoadEvent.cpp" />
|
||||
<ClCompile Include="ModuleUnloadEvent.cpp" />
|
||||
<ClCompile Include="PrettyPrintSig.cpp" />
|
||||
<ClCompile Include="RuntimeInformationEvent.cpp" />
|
||||
<ClCompile Include="sha1.cpp" />
|
||||
<ClCompile Include="ShutdownEvent.cpp" />
|
||||
<ClCompile Include="sqlite3.c" />
|
||||
<ClCompile Include="SQLiteEventLogger.cpp" />
|
||||
<ClCompile Include="SQLiteSerializer.cpp" />
|
||||
<ClCompile Include="StartupEvent.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,204 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="sqlite3.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="profiler_pal.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ClassFactory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BPerfProfiler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CComPtr.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CQuickBytes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="EtwEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ThreadSafeQueue.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="JITCompilationStartedEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AppDomainLoadEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SQLiteEventLogger.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SQLiteSerializer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AppDomainUnloadEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AssemblyLoadEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AssemblyUnloadEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="sha1.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ModuleLoadEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ModuleUnloadEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="JITCompilationFinishedEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MethodILToNativeMapEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MethodUnloadEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Serializer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RuntimeInformationEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ShutdownEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PortableString.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GuidDef.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HeartBeatEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StartupEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ILRewriter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RundownList.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RundownListNode.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="EtwLogger.Generated.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="EtwLogger.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MethodEnterEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="sqlite3.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ClassFactory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BPerfProfiler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PrettyPrintSig.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SQLiteSerializer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="sha1.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BPerfProfiler.PrivateHelpers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AssemblyUnloadEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AssemblyLoadEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AppDomainLoadEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AppDomainUnloadEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EtwEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="JITCompilationFinishedEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="JITCompilationStartedEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MethodILToNativeMapEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MethodUnloadEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ModuleLoadEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ModuleUnloadEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CQuickBytes.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RuntimeInformationEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ShutdownEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SQLiteEventLogger.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HeartBeatEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StartupEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ILRewriter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MethodEnterEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,392 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
#include "profiler_pal.h"
|
||||
#include "BPerfProfiler.h"
|
||||
#include "strsafe.h"
|
||||
#include "corhlpr.h"
|
||||
#include "CComPtr.h"
|
||||
#include "AssemblyLoadEvent.h"
|
||||
#include "AssemblyUnloadEvent.h"
|
||||
#include "ModuleLoadEvent.h"
|
||||
#include "ModuleUnloadEvent.h"
|
||||
#include "JITCompilationStartedEvent.h"
|
||||
#include "JITCompilationFinishedEvent.h"
|
||||
#include "MethodUnloadEvent.h"
|
||||
#include "CQuickBytes.h"
|
||||
#include "sha1.h"
|
||||
#include "RundownList.h"
|
||||
|
||||
HRESULT BPerfProfiler::AssemblyLoadUnloadData(AssemblyID assemblyId, bool load)
|
||||
{
|
||||
HRESULT hr;
|
||||
ULONG assemblyNameLength = 0;
|
||||
AppDomainID appDomainId;
|
||||
ModuleID moduleId;
|
||||
|
||||
IfFailRet(this->corProfilerInfo->GetAssemblyInfo(assemblyId, 0, &assemblyNameLength, nullptr, &appDomainId, &moduleId));
|
||||
portable_wide_string assemblyName(assemblyNameLength, ZEROSTRING);
|
||||
IfFailRet(this->corProfilerInfo->GetAssemblyInfo(assemblyId, assemblyNameLength, nullptr, addr(assemblyName), nullptr, nullptr));
|
||||
|
||||
CComPtr<IMetaDataAssemblyImport> metadataAssemblyImport;
|
||||
IfFailRet(this->corProfilerInfo->GetModuleMetaData(moduleId, ofRead, IID_IMetaDataAssemblyImport, reinterpret_cast<IUnknown **>(&metadataAssemblyImport)));
|
||||
|
||||
mdAssembly mda;
|
||||
metadataAssemblyImport->GetAssemblyFromScope(&mda);
|
||||
|
||||
ASSEMBLYMETADATA assemblyMetadata;
|
||||
DWORD assemblyFlags;
|
||||
const void *publicKey;
|
||||
ULONG publicKeySize;
|
||||
ULONG hashAlgorithm;
|
||||
|
||||
assemblyMetadata.cbLocale = 0;
|
||||
assemblyMetadata.ulProcessor = 0;
|
||||
assemblyMetadata.ulOS = 0;
|
||||
|
||||
IfFailRet(metadataAssemblyImport->GetAssemblyProps(mda, &publicKey, &publicKeySize, &hashAlgorithm, nullptr, 0, nullptr, &assemblyMetadata, &assemblyFlags));
|
||||
|
||||
portable_wide_char publicKeyToken[17];
|
||||
|
||||
if ((assemblyFlags & afPublicKey) != 0)
|
||||
{
|
||||
constexpr BYTE ecmaPublicKey[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
if (memcmp(publicKey, ecmaPublicKey, publicKeySize) == 0)
|
||||
{
|
||||
StringCchPrintf(publicKeyToken, 17, W("b77a5c561934e089"));
|
||||
}
|
||||
else
|
||||
{
|
||||
SHA1Hash sha1Hash;
|
||||
sha1Hash.AddData(PBYTE(publicKey), publicKeySize);
|
||||
PBYTE hash = sha1Hash.GetHash() + SHA1_HASH_SIZE - 8; // we only care about the last 8 bytes
|
||||
|
||||
StringCchPrintfW(publicKeyToken, 17, W("%02x%02x%02x%02x%02x%02x%02x%02x"), hash[7], hash[6], hash[5], hash[4], hash[3], hash[2], hash[1], hash[0]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
StringCchPrintfW(publicKeyToken, 5, W("null"));
|
||||
}
|
||||
|
||||
portable_wide_string culture(assemblyMetadata.cbLocale, ZEROSTRING);
|
||||
assemblyMetadata.szLocale = addr(culture);
|
||||
IfFailRet(metadataAssemblyImport->GetAssemblyProps(mda, &publicKey, &publicKeySize, &hashAlgorithm, nullptr, 0, nullptr, &assemblyMetadata, &assemblyFlags));
|
||||
|
||||
auto maxPossibleStringLength = assemblyName.length() + culture.length() + 120; // 120 is the length of formatstring + max possible version number lengths + publickeytoken + possible "neutral" culture
|
||||
portable_wide_string fullyQualifiedAssemblyName(maxPossibleStringLength, ZEROSTRING);
|
||||
StringCchPrintfW(addr(fullyQualifiedAssemblyName), maxPossibleStringLength, W("%s, Version=%d.%d.%d.%d, Culture=%s, PublicKeyToken=%s"), assemblyName.c_str(), assemblyMetadata.usMajorVersion, assemblyMetadata.usMinorVersion, assemblyMetadata.usBuildNumber, assemblyMetadata.usRevisionNumber, culture.length() == 0 ? W("neutral") : culture.c_str(), publicKeyToken);
|
||||
|
||||
size_t realLength;
|
||||
StringCchLengthW(fullyQualifiedAssemblyName.c_str(), maxPossibleStringLength, &realLength);
|
||||
fullyQualifiedAssemblyName.resize(realLength + 1); // shrink since we over-estimate the size, this is required for correctness
|
||||
|
||||
if (load)
|
||||
{
|
||||
auto temp = this->assemblyRundown.Add(LogEvent2<AssemblyLoadEvent>(this->eventQueue, assemblyId, appDomainId, 0, assemblyFlags, fullyQualifiedAssemblyName, this->clrInstanceId));
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->assemblyMutex);
|
||||
this->assemblyInfoMap[assemblyId] = temp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->eventQueue->push(LogEvent<AssemblyUnloadEvent>(assemblyId, appDomainId, 0, assemblyFlags, fullyQualifiedAssemblyName, this->clrInstanceId));
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->assemblyMutex);
|
||||
|
||||
auto iter = this->assemblyInfoMap.find(assemblyId);
|
||||
if (iter != this->assemblyInfoMap.end())
|
||||
{
|
||||
this->assemblyInfoMap.erase(iter);
|
||||
this->assemblyRundown.Remove(iter->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
PIMAGE_DEBUG_DIRECTORY GetDebugDirectories(const IMAGE_OPTIONAL_HEADER *header, LPCBYTE imageBase, ULONG *numberOfDebugDirectories)
|
||||
{
|
||||
PIMAGE_DEBUG_DIRECTORY debugDirectories = nullptr;
|
||||
auto optionalHeader = T(header);
|
||||
auto offset = optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
|
||||
|
||||
if (offset != 0)
|
||||
{
|
||||
debugDirectories = reinterpret_cast<PIMAGE_DEBUG_DIRECTORY>(reinterpret_cast<ULONG_PTR>(imageBase) + offset);
|
||||
*numberOfDebugDirectories = optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size / sizeof(*debugDirectories);
|
||||
}
|
||||
|
||||
return debugDirectories;
|
||||
}
|
||||
|
||||
using ImageHeaderType = std::conditional<sizeof(size_t) == 8, PIMAGE_OPTIONAL_HEADER64, PIMAGE_OPTIONAL_HEADER32>::type;
|
||||
|
||||
typedef struct _CV_INFO_PDB70
|
||||
{
|
||||
DWORD CvSignature;
|
||||
GUID Signature;
|
||||
DWORD Age;
|
||||
char PdbFileName[1];
|
||||
} CV_INFO_PDB70;
|
||||
|
||||
HRESULT BPerfProfiler::ModuleLoadUnloadData(ModuleID moduleId, bool load)
|
||||
{
|
||||
HRESULT hr;
|
||||
ULONG moduleILPathLength = 0;
|
||||
LPCBYTE imageBase;
|
||||
DWORD moduleFlags;
|
||||
AssemblyID assemblyId;
|
||||
|
||||
IfFailRet(this->corProfilerInfo->GetModuleInfo2(moduleId, &imageBase, 0, &moduleILPathLength, nullptr, &assemblyId, &moduleFlags));
|
||||
portable_wide_string moduleILPath(moduleILPathLength, ZEROSTRING);
|
||||
IfFailRet(this->corProfilerInfo->GetModuleInfo2(moduleId, nullptr, moduleILPathLength, nullptr, addr(moduleILPath), nullptr, nullptr));
|
||||
|
||||
DWORD nativePdbAge = 0, managedPdbAge = 0;
|
||||
GUID nativePdbSignature = IID_NULL, managedPdbSignature = IID_NULL;
|
||||
portable_wide_string nativePdbBuildPath, managedPdbBuildPath, moduleNativePath;
|
||||
|
||||
if (imageBase != nullptr)
|
||||
{
|
||||
auto dosHeader = reinterpret_cast<const IMAGE_DOS_HEADER *>(imageBase);
|
||||
auto ntHeader = reinterpret_cast<const IMAGE_NT_HEADERS *>(imageBase + dosHeader->e_lfanew);
|
||||
const IMAGE_OPTIONAL_HEADER *header = &ntHeader->OptionalHeader;
|
||||
|
||||
ULONG numberOfDebugDirectories = 0;
|
||||
auto debugDirectories = GetDebugDirectories<ImageHeaderType>(header, imageBase, &numberOfDebugDirectories);
|
||||
|
||||
// Last two debug entries are NGEN and IL respectively.
|
||||
while (numberOfDebugDirectories > 2)
|
||||
{
|
||||
++debugDirectories;
|
||||
--numberOfDebugDirectories;
|
||||
}
|
||||
|
||||
if (numberOfDebugDirectories == 2)
|
||||
{
|
||||
if (debugDirectories->Type == IMAGE_DEBUG_TYPE_CODEVIEW)
|
||||
{
|
||||
auto data = reinterpret_cast<const CV_INFO_PDB70 *>(imageBase + debugDirectories->AddressOfRawData);
|
||||
std::string temp(data->PdbFileName);
|
||||
nativePdbAge = data->Age;
|
||||
nativePdbSignature = data->Signature;
|
||||
nativePdbBuildPath = portable_wide_string(temp.begin(), temp.end());
|
||||
nativePdbBuildPath.resize(nativePdbBuildPath.length() + 1); // we want the null terminator when we serialize
|
||||
}
|
||||
|
||||
++debugDirectories;
|
||||
--numberOfDebugDirectories;
|
||||
}
|
||||
|
||||
if (numberOfDebugDirectories == 1)
|
||||
{
|
||||
if (debugDirectories->Type == IMAGE_DEBUG_TYPE_CODEVIEW)
|
||||
{
|
||||
auto data = reinterpret_cast<const CV_INFO_PDB70 *>(imageBase + debugDirectories->AddressOfRawData);
|
||||
std::string temp(data->PdbFileName);
|
||||
managedPdbAge = data->Age;
|
||||
managedPdbSignature = data->Signature;
|
||||
managedPdbBuildPath = portable_wide_string(temp.begin(), temp.end());
|
||||
managedPdbBuildPath.resize(managedPdbBuildPath.length() + 1); // // we want the null terminator when we serialize
|
||||
}
|
||||
}
|
||||
|
||||
// NGEN Image
|
||||
if ((moduleFlags & COR_PRF_MODULE_NGEN) != 0)
|
||||
{
|
||||
/*
|
||||
if (GetMappedFileName(GetCurrentProcess(), LPVOID(imageBase), addr(moduleNativePath), MAX_PATH))
|
||||
{
|
||||
TranslateDevicePathToDrivePath(moduleNativePath);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
if (load)
|
||||
{
|
||||
auto temp = this->moduleRundown.Add(LogEvent2<ModuleLoadEvent>(this->eventQueue, moduleId, assemblyId, moduleFlags, moduleILPath, moduleNativePath, this->clrInstanceId, managedPdbSignature, managedPdbAge, managedPdbBuildPath, nativePdbSignature, nativePdbAge, nativePdbBuildPath));
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->moduleMutex);
|
||||
this->moduleInfoMap[moduleId] = temp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->eventQueue->push(LogEvent<ModuleUnloadEvent>(moduleId, assemblyId, moduleFlags, moduleILPath, moduleNativePath, this->clrInstanceId, managedPdbSignature, managedPdbAge, managedPdbBuildPath, nativePdbSignature, nativePdbAge, nativePdbBuildPath));
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->moduleMutex);
|
||||
|
||||
auto iter = this->moduleInfoMap.find(moduleId);
|
||||
if (iter != this->moduleInfoMap.end())
|
||||
{
|
||||
this->moduleInfoMap.erase(iter);
|
||||
this->moduleRundown.Remove(iter->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
HRESULT BPerfProfiler::MethodJitStartFinishData(FunctionID functionId, bool finished, bool unload)
|
||||
{
|
||||
HRESULT hr;
|
||||
mdToken methodToken;
|
||||
ClassID classId;
|
||||
ModuleID moduleId;
|
||||
LPCBYTE methodHeader;
|
||||
ULONG methodILSize;
|
||||
|
||||
IfFailRet(this->corProfilerInfo->GetFunctionInfo(functionId, &classId, &moduleId, &methodToken));
|
||||
IfFailRet(this->corProfilerInfo->GetILFunctionBody(moduleId, methodToken, &methodHeader, &methodILSize));
|
||||
|
||||
CComPtr<IMetaDataImport> metaDataImport;
|
||||
IfFailRet(this->corProfilerInfo->GetTokenAndMetaDataFromFunction(functionId, IID_IMetaDataImport, reinterpret_cast<IUnknown **>(&metaDataImport), &methodToken));
|
||||
|
||||
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, addr(methodName), 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
|
||||
|
||||
if (!finished)
|
||||
{
|
||||
this->eventQueue->push(LogEvent<JITCompilationStartedEvent>(functionId, moduleId, methodToken, methodILSize, fullClassName, methodName, signatureString, this->clrInstanceId));
|
||||
}
|
||||
else
|
||||
{
|
||||
ULONG32 codeInfosCount;
|
||||
|
||||
IfFailRet(this->corProfilerInfo->GetCodeInfo2(functionId, 0, &codeInfosCount, nullptr));
|
||||
std::vector<COR_PRF_CODE_INFO> codeInfos(codeInfosCount);
|
||||
IfFailRet(this->corProfilerInfo->GetCodeInfo2(functionId, codeInfosCount, &codeInfosCount, codeInfos.data()));
|
||||
|
||||
int64_t methodStartAddress = 0;
|
||||
int32_t methodSize = 0;
|
||||
|
||||
if (codeInfosCount > 0)
|
||||
{
|
||||
methodStartAddress = codeInfos[0].startAddress;
|
||||
methodSize = static_cast<int32_t>(codeInfos[0].size);
|
||||
}
|
||||
|
||||
if (unload)
|
||||
{
|
||||
this->eventQueue->push(LogEvent<MethodUnloadEvent>(functionId, moduleId, methodStartAddress, methodSize, methodToken, 1, fullClassName, methodName, signatureString, this->clrInstanceId, 0));
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->methodMutex);
|
||||
|
||||
auto iter = this->methodInfoMap.find(functionId);
|
||||
if (iter != this->methodInfoMap.end())
|
||||
{
|
||||
this->methodInfoMap.erase(iter);
|
||||
this->methodRundown.Remove(iter->second);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->ilNativeMutex);
|
||||
|
||||
auto iter2 = this->ilNativeInfoMap.find(functionId);
|
||||
if (iter2 != this->ilNativeInfoMap.end())
|
||||
{
|
||||
this->ilNativeInfoMap.erase(iter2);
|
||||
this->ilNativeMapRundown.Remove(iter2->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto temp = this->methodRundown.Add(LogEvent2<JITCompilationFinishedEvent>(this->eventQueue, functionId, moduleId, methodStartAddress, methodSize, methodToken, 1, fullClassName, methodName, signatureString, this->clrInstanceId, 0));
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->methodMutex);
|
||||
this->methodInfoMap[functionId] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,743 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
/*
|
||||
/modules
|
||||
/assemblies
|
||||
/appdomains
|
||||
/jittedMethods
|
||||
/nativeMethods
|
||||
/threads
|
||||
|
||||
/rejit?moduleId=...&methodDefToken=...&il=...
|
||||
/gc
|
||||
|
||||
|
||||
/enableELTMonitoring
|
||||
/enableNewObjMonitoring
|
||||
/enableCPUSamplingUsingELTOrThreadSuspend
|
||||
|
||||
BPERF_ENABLE_CALL_COUNT
|
||||
|
||||
/showDisassembly/methodId
|
||||
/EIPToFunctionID
|
||||
/exceptions
|
||||
|
||||
ModuleId,ModuleName
|
||||
0x4334343,ModuleName
|
||||
*/
|
||||
|
||||
// TODO:
|
||||
// (1) Build a web service that can take an EIP and find FunctionID
|
||||
// (2) A Symbolization service /Eip+Time
|
||||
// (3) Source Line info too
|
||||
// (4) Exceptions
|
||||
// (5) Querying the Shadow Stack
|
||||
// (6) Supply capapbility to change web server url (and how much info we cache, etc).
|
||||
// (7) General debugging service (given functionID, show me name)
|
||||
// (8) Given some function names (Strings) tell me if they're hot how much time they're spending
|
||||
// (9) Object allocations (tell me if they allocated objects), etc.
|
||||
// (10) memory leaks
|
||||
// (11) Request REJIT from function strings
|
||||
// (12) Loaded Module List
|
||||
// (13) Enumerate JITTED functions ---> EnumJITedFunctions2
|
||||
// (14) EnumThreads
|
||||
// (15) GetReJITIDs
|
||||
// (16) ForceGC
|
||||
// (17) Flight Recorder Mode
|
||||
// (18) call count data
|
||||
|
||||
// Open Questions:
|
||||
// (1) Can GetFunctionFromIP2 be used to get FunctionIDs? Should be, how does one know from EIP if it's a rejitted function or not
|
||||
|
||||
#include "profiler_pal.h"
|
||||
#include "BPerfProfiler.h"
|
||||
#include "corhlpr.h"
|
||||
#include "JITCompilationStartedEvent.h"
|
||||
#include "AppDomainUnloadEvent.h"
|
||||
#include "RuntimeInformationEvent.h"
|
||||
#include "ShutdownEvent.h"
|
||||
#include "SQLiteEventLogger.h"
|
||||
#include "StartupEvent.h"
|
||||
#include "RundownList.h"
|
||||
#include "EtwLogger.h"
|
||||
#include "ILRewriter.h"
|
||||
#include "CComPtr.h"
|
||||
#include "MethodEnterEvent.h"
|
||||
|
||||
thread_local std::vector<FunctionID> ShadowStack;
|
||||
|
||||
static void STDMETHODCALLTYPE Enter(FunctionID functionId)
|
||||
{
|
||||
ShadowStack.push_back(functionId);
|
||||
ProfilerInstance->eventQueue->push(LogEvent<MethodEnterEvent>(functionId, &ShadowStack));
|
||||
}
|
||||
|
||||
static void STDMETHODCALLTYPE Leave(FunctionID functionId)
|
||||
{
|
||||
ShadowStack.pop_back();
|
||||
}
|
||||
|
||||
void(STDMETHODCALLTYPE *EnterMethodAddress)(FunctionID) = &Enter;
|
||||
void(STDMETHODCALLTYPE *LeaveMethodAddress)(FunctionID) = &Leave;
|
||||
COR_SIGNATURE enterLeaveMethodSignature[] = { IMAGE_CEE_CS_CALLCONV_STDCALL, 0x01, ELEMENT_TYPE_VOID, ELEMENT_TYPE_I };
|
||||
|
||||
BPerfProfiler::BPerfProfiler() : eventQueue(std::make_shared<ThreadSafeQueue<std::unique_ptr<EtwEvent>>>()), clrInstanceId(0), refCount(0), corProfilerInfo(nullptr), shouldMaintainShadowStack(false)
|
||||
{
|
||||
if (IsEnvironmentVariableEnabled("BPERF_DELAYED_STARTUP"))
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
|
||||
}
|
||||
|
||||
this->eventQueue->push(LogEvent<StartupEvent>(this->clrInstanceId));
|
||||
|
||||
this->eventLoggerThread = std::thread([this] {
|
||||
|
||||
const std::string bPerfLogsPath = [&]() {
|
||||
const char *ptr = std::getenv("BPERF_LOG_PATH");
|
||||
return ptr != nullptr ? ptr : "BPerfLog.db"; // default file name, in the current working directory
|
||||
}();
|
||||
|
||||
if (IsEnvironmentVariableEnabled("BPERF_OVERWRITE_LOGS"))
|
||||
{
|
||||
portable_wide_string wideString;
|
||||
int wlen = MultiByteToWideChar(CP_UTF8, 0, bPerfLogsPath.c_str(), -1, nullptr, 0);
|
||||
if (wlen > 0)
|
||||
{
|
||||
wideString.resize(wlen - 1);
|
||||
MultiByteToWideChar(CP_UTF8, 0, bPerfLogsPath.c_str(), -1, addr(wideString), wlen);
|
||||
DeleteFile(wideString.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
SQLiteEventLogger logger(this->db, this->eventQueue, bPerfLogsPath);
|
||||
logger.RunEventLoop();
|
||||
});
|
||||
}
|
||||
|
||||
BPerfProfiler::~BPerfProfiler()
|
||||
{
|
||||
if (this->corProfilerInfo != nullptr)
|
||||
{
|
||||
this->corProfilerInfo->Release();
|
||||
this->corProfilerInfo = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::Initialize(IUnknown *pICorProfilerInfoUnk)
|
||||
{
|
||||
HRESULT queryInterfaceResult = pICorProfilerInfoUnk->QueryInterface(__uuidof(ICorProfilerInfo7), reinterpret_cast<void **>(&this->corProfilerInfo));
|
||||
|
||||
if (FAILED(queryInterfaceResult))
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
DWORD eventMask = COR_PRF_DISABLE_TRANSPARENCY_CHECKS_UNDER_FULL_TRUST |
|
||||
COR_PRF_MONITOR_THREADS |
|
||||
COR_PRF_MONITOR_SUSPENDS |
|
||||
COR_PRF_MONITOR_MODULE_LOADS |
|
||||
COR_PRF_MONITOR_JIT_COMPILATION |
|
||||
COR_PRF_MONITOR_FUNCTION_UNLOADS |
|
||||
COR_PRF_MONITOR_EXCEPTIONS |
|
||||
COR_PRF_MONITOR_CLR_EXCEPTIONS |
|
||||
COR_PRF_MONITOR_CLASS_LOADS |
|
||||
COR_PRF_MONITOR_ASSEMBLY_LOADS |
|
||||
COR_PRF_MONITOR_APPDOMAIN_LOADS;
|
||||
|
||||
if (IsEnvironmentVariableEnabled("BPERF_MONITOR_ALLOCATIONS"))
|
||||
{
|
||||
eventMask |= COR_PRF_MONITOR_OBJECT_ALLOCATED;
|
||||
}
|
||||
|
||||
if (IsEnvironmentVariableEnabled("BPERF_MONITOR_ENTERLEAVE"))
|
||||
{
|
||||
this->shouldMaintainShadowStack = true;
|
||||
}
|
||||
|
||||
COR_PRF_RUNTIME_TYPE sku;
|
||||
USHORT majorVersion, minorVersion, buildNumber, qfeVersion;
|
||||
this->corProfilerInfo->GetRuntimeInformation(&this->clrInstanceId, &sku, &majorVersion, &minorVersion, &buildNumber, &qfeVersion, 0, nullptr, nullptr);
|
||||
this->eventQueue->push(LogEvent<RuntimeInformationEvent>(this->clrInstanceId, sku, majorVersion, minorVersion, buildNumber, qfeVersion));
|
||||
|
||||
ProfilerInstance = this;
|
||||
|
||||
#ifdef _WINDOWS
|
||||
EventRegisterMicrosoft_Windows_DotNETRuntimeRundown();
|
||||
#endif
|
||||
|
||||
return this->corProfilerInfo->SetEventMask(eventMask);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::Shutdown()
|
||||
{
|
||||
#ifdef _WINDOWS
|
||||
EventUnregisterMicrosoft_Windows_DotNETRuntimeRundown();
|
||||
#endif
|
||||
|
||||
std::lock_guard<std::mutex> lock(RundownMutex);
|
||||
|
||||
ProfilerInstance = nullptr;
|
||||
|
||||
this->eventQueue->push(LogEvent<ShutdownEvent>(this->clrInstanceId));
|
||||
this->eventLoggerThread.join();
|
||||
|
||||
if (this->corProfilerInfo != nullptr)
|
||||
{
|
||||
this->corProfilerInfo->Release();
|
||||
this->corProfilerInfo = nullptr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void STDMETHODCALLTYPE BPerfProfiler::Rundown()
|
||||
{
|
||||
#ifdef _WINDOWS
|
||||
this->appDomainRundown.Iterate([](auto data) { EtwLogger::WriteEvent(data); });
|
||||
this->assemblyRundown.Iterate([](auto data) { EtwLogger::WriteEvent(data); });
|
||||
this->moduleRundown.Iterate([](auto data) { EtwLogger::WriteEvent(data); });
|
||||
this->methodRundown.Iterate([](auto data) { EtwLogger::WriteEvent(data); });
|
||||
this->ilNativeMapRundown.Iterate([](auto data) { EtwLogger::WriteEvent(data); });
|
||||
#endif
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::AppDomainCreationStarted(AppDomainID appDomainId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::AppDomainCreationFinished(AppDomainID appDomainId, HRESULT hrStatus)
|
||||
{
|
||||
HRESULT hr;
|
||||
IfFailRet(hrStatus);
|
||||
|
||||
ULONG appDomainNameLength = 0;
|
||||
|
||||
this->corProfilerInfo->GetAppDomainInfo(appDomainId, 0, &appDomainNameLength, nullptr, nullptr);
|
||||
portable_wide_string appDomainName(appDomainNameLength, ZEROSTRING);
|
||||
IfFailRet(this->corProfilerInfo->GetAppDomainInfo(appDomainId, appDomainNameLength, &appDomainNameLength, addr(appDomainName), nullptr));
|
||||
|
||||
auto temp = this->appDomainRundown.Add(LogEvent2<AppDomainLoadEvent>(this->eventQueue, appDomainId, 0, appDomainName, 0, this->clrInstanceId));
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->appDomainMutex);
|
||||
this->appDomainInfoMap[appDomainId] = temp;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::AppDomainShutdownStarted(AppDomainID appDomainId)
|
||||
{
|
||||
HRESULT hr;
|
||||
ULONG appDomainNameLength = 0;
|
||||
|
||||
this->corProfilerInfo->GetAppDomainInfo(appDomainId, 0, &appDomainNameLength, nullptr, nullptr);
|
||||
|
||||
portable_wide_string appDomainName(appDomainNameLength, ZEROSTRING);
|
||||
IfFailRet(this->corProfilerInfo->GetAppDomainInfo(appDomainId, appDomainNameLength, &appDomainNameLength, addr(appDomainName), nullptr));
|
||||
|
||||
this->eventQueue->push(LogEvent<AppDomainUnloadEvent>(appDomainId, 0, appDomainName, 0, this->clrInstanceId));
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->appDomainMutex);
|
||||
|
||||
auto iter = this->appDomainInfoMap.find(appDomainId);
|
||||
if (iter != this->appDomainInfoMap.end())
|
||||
{
|
||||
this->appDomainInfoMap.erase(iter);
|
||||
this->appDomainRundown.Remove(iter->second);
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::AppDomainShutdownFinished(AppDomainID appDomainId, HRESULT hrStatus)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::AssemblyLoadStarted(AssemblyID assemblyId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::AssemblyLoadFinished(AssemblyID assemblyId, HRESULT hrStatus)
|
||||
{
|
||||
HRESULT hr;
|
||||
IfFailRet(hrStatus);
|
||||
|
||||
hr = this->AssemblyLoadUnloadData(assemblyId, true);
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::AssemblyUnloadStarted(AssemblyID assemblyId)
|
||||
{
|
||||
return this->AssemblyLoadUnloadData(assemblyId, false);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::AssemblyUnloadFinished(AssemblyID assemblyId, HRESULT hrStatus)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ModuleLoadStarted(ModuleID moduleId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ModuleLoadFinished(ModuleID moduleId, HRESULT hrStatus)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ModuleUnloadStarted(ModuleID moduleId)
|
||||
{
|
||||
return this->ModuleLoadUnloadData(moduleId, false);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ModuleUnloadFinished(ModuleID moduleId, HRESULT hrStatus)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ModuleAttachedToAssembly(ModuleID moduleId, AssemblyID assemblyId)
|
||||
{
|
||||
return this->ModuleLoadUnloadData(moduleId, true);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ClassLoadStarted(ClassID classId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ClassLoadFinished(ClassID classId, HRESULT hrStatus)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ClassUnloadStarted(ClassID classId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ClassUnloadFinished(ClassID classId, HRESULT hrStatus)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::FunctionUnloadStarted(FunctionID functionId)
|
||||
{
|
||||
return this->MethodJitStartFinishData(functionId, /* finished */ true, /* unload */ true);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::JITCompilationStarted(FunctionID functionId, BOOL fIsSafeToBlock)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (this->shouldMaintainShadowStack)
|
||||
{
|
||||
mdToken token;
|
||||
ClassID classId;
|
||||
ModuleID moduleId;
|
||||
|
||||
IfFailRet(this->corProfilerInfo->GetFunctionInfo(functionId, &classId, &moduleId, &token));
|
||||
|
||||
CComPtr<IMetaDataImport> metadataImport;
|
||||
IfFailRet(this->corProfilerInfo->GetModuleMetaData(moduleId, ofRead | ofWrite, IID_IMetaDataImport, reinterpret_cast<IUnknown **>(&metadataImport)));
|
||||
|
||||
CComPtr<IMetaDataEmit> metadataEmit;
|
||||
IfFailRet(metadataImport->QueryInterface(IID_IMetaDataEmit, reinterpret_cast<void **>(&metadataEmit)));
|
||||
|
||||
mdSignature enterLeaveMethodSignatureToken;
|
||||
metadataEmit->GetTokenFromSig(enterLeaveMethodSignature, sizeof(enterLeaveMethodSignature), &enterLeaveMethodSignatureToken);
|
||||
|
||||
IfFailRet(RewriteIL(this->corProfilerInfo, nullptr, moduleId, token, functionId, reinterpret_cast<ULONGLONG>(EnterMethodAddress), reinterpret_cast<ULONGLONG>(LeaveMethodAddress), enterLeaveMethodSignatureToken));
|
||||
}
|
||||
|
||||
return this->MethodJitStartFinishData(functionId, /* finished */ false, /* unload */ false);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::JITCompilationFinished(FunctionID functionId, HRESULT hrStatus, BOOL fIsSafeToBlock)
|
||||
{
|
||||
HRESULT hr;
|
||||
IfFailRet(hrStatus);
|
||||
|
||||
this->MethodJitStartFinishData(functionId, /* finished */ true, /* unload */ false);
|
||||
|
||||
ULONG32 nativeMapsCount;
|
||||
|
||||
IfFailRet(this->corProfilerInfo->GetILToNativeMapping(functionId, 0, &nativeMapsCount, nullptr));
|
||||
std::vector<COR_DEBUG_IL_TO_NATIVE_MAP> nativeMaps(nativeMapsCount);
|
||||
IfFailRet(this->corProfilerInfo->GetILToNativeMapping(functionId, nativeMapsCount, &nativeMapsCount, nativeMaps.data()));
|
||||
|
||||
std::vector<uint32_t> ilOffsets, nativeOffsets;
|
||||
ilOffsets.resize(nativeMapsCount);
|
||||
nativeOffsets.resize(nativeMapsCount);
|
||||
|
||||
for (auto const &e : nativeMaps)
|
||||
{
|
||||
ilOffsets.emplace_back(e.ilOffset);
|
||||
nativeOffsets.emplace_back(e.nativeStartOffset);
|
||||
}
|
||||
|
||||
auto temp = this->ilNativeMapRundown.Add(LogEvent2<MethodILToNativeMapEvent>(this->eventQueue, functionId, 0, 0, nativeMapsCount, ilOffsets, nativeOffsets, this->clrInstanceId));
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->ilNativeMutex);
|
||||
this->ilNativeInfoMap[functionId] = temp;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::JITCachedFunctionSearchStarted(FunctionID functionId, BOOL *pbUseCachedFunction)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::JITCachedFunctionSearchFinished(FunctionID functionId, COR_PRF_JIT_CACHE result)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::JITFunctionPitched(FunctionID functionId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::JITInlining(FunctionID callerId, FunctionID calleeId, BOOL *pfShouldInline)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ThreadCreated(ThreadID threadId)
|
||||
{
|
||||
ShadowStack.reserve(512); // reserve to accomodate 512 frames without allocation
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ThreadDestroyed(ThreadID threadId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ThreadAssignedToOSThread(ThreadID managedThreadId, DWORD osThreadId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RemotingClientInvocationStarted()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RemotingClientSendingMessage(GUID *pCookie, BOOL fIsAsync)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RemotingClientReceivingReply(GUID *pCookie, BOOL fIsAsync)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RemotingClientInvocationFinished()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RemotingServerReceivingMessage(GUID *pCookie, BOOL fIsAsync)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RemotingServerInvocationStarted()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RemotingServerInvocationReturned()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RemotingServerSendingReply(GUID *pCookie, BOOL fIsAsync)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::UnmanagedToManagedTransition(FunctionID functionId, COR_PRF_TRANSITION_REASON reason)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ManagedToUnmanagedTransition(FunctionID functionId, COR_PRF_TRANSITION_REASON reason)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RuntimeSuspendStarted(COR_PRF_SUSPEND_REASON suspendReason)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RuntimeSuspendFinished()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RuntimeSuspendAborted()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RuntimeResumeStarted()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RuntimeResumeFinished()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RuntimeThreadSuspended(ThreadID threadId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RuntimeThreadResumed(ThreadID threadId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::MovedReferences(ULONG cMovedObjectIDRanges, ObjectID oldObjectIDRangeStart[], ObjectID newObjectIDRangeStart[], ULONG cObjectIDRangeLength[])
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ObjectAllocated(ObjectID objectId, ClassID classId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ObjectsAllocatedByClass(ULONG cClassCount, ClassID classIds[], ULONG cObjects[])
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ObjectReferences(ObjectID objectId, ClassID classId, ULONG cObjectRefs, ObjectID objectRefIds[])
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RootReferences(ULONG cRootRefs, ObjectID rootRefIds[])
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionThrown(ObjectID thrownObjectId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionSearchFunctionEnter(FunctionID functionId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionSearchFunctionLeave()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionSearchFilterEnter(FunctionID functionId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionSearchFilterLeave()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionSearchCatcherFound(FunctionID functionId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionOSHandlerEnter(UINT_PTR __unused)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionOSHandlerLeave(UINT_PTR __unused)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionUnwindFunctionEnter(FunctionID functionId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionUnwindFunctionLeave()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionUnwindFinallyEnter(FunctionID functionId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionUnwindFinallyLeave()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionCatcherEnter(FunctionID functionId, ObjectID objectId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionCatcherLeave()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::COMClassicVTableCreated(ClassID wrappedClassId, REFGUID implementedIID, void *pVTable, ULONG cSlots)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::COMClassicVTableDestroyed(ClassID wrappedClassId, REFGUID implementedIID, void *pVTable)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionCLRCatcherFound()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ExceptionCLRCatcherExecute()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ThreadNameChanged(ThreadID threadId, ULONG cchName, WCHAR name[])
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::GarbageCollectionStarted(int cGenerations, BOOL generationCollected[], COR_PRF_GC_REASON reason)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::SurvivingReferences(ULONG cSurvivingObjectIDRanges, ObjectID objectIDRangeStart[], ULONG cObjectIDRangeLength[])
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::GarbageCollectionFinished()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::FinalizeableObjectQueued(DWORD finalizerFlags, ObjectID objectID)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::RootReferences2(ULONG cRootRefs, ObjectID rootRefIds[], COR_PRF_GC_ROOT_KIND rootKinds[], COR_PRF_GC_ROOT_FLAGS rootFlags[], UINT_PTR rootIds[])
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::HandleCreated(GCHandleID handleId, ObjectID initialObjectId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::HandleDestroyed(GCHandleID handleId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::InitializeForAttach(IUnknown *pCorProfilerInfoUnk, void *pvClientData, UINT cbClientData)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ProfilerAttachComplete()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ProfilerDetachSucceeded()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ReJITCompilationStarted(FunctionID functionId, ReJITID rejitId, BOOL fIsSafeToBlock)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::GetReJITParameters(ModuleID moduleId, mdMethodDef methodId, ICorProfilerFunctionControl *pFunctionControl)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ReJITCompilationFinished(FunctionID functionId, ReJITID rejitId, HRESULT hrStatus, BOOL fIsSafeToBlock)
|
||||
{
|
||||
HRESULT hr;
|
||||
ULONG32 codeInfosCount;
|
||||
|
||||
IfFailRet(this->corProfilerInfo->GetCodeInfo3(functionId, rejitId, 0, &codeInfosCount, nullptr));
|
||||
std::vector<COR_PRF_CODE_INFO> codeInfos(codeInfosCount);
|
||||
IfFailRet(this->corProfilerInfo->GetCodeInfo3(functionId, rejitId, codeInfosCount, nullptr, codeInfos.data()));
|
||||
|
||||
ULONG32 nativeMapsCount;
|
||||
|
||||
IfFailRet(this->corProfilerInfo->GetILToNativeMapping2(functionId, rejitId, 0, &nativeMapsCount, nullptr));
|
||||
std::vector<COR_DEBUG_IL_TO_NATIVE_MAP> nativeMaps(nativeMapsCount);
|
||||
IfFailRet(this->corProfilerInfo->GetILToNativeMapping2(functionId, rejitId, nativeMapsCount, nullptr, nativeMaps.data()));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ReJITError(ModuleID moduleId, mdMethodDef methodId, FunctionID functionId, HRESULT hrStatus)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::MovedReferences2(ULONG cMovedObjectIDRanges, ObjectID oldObjectIDRangeStart[], ObjectID newObjectIDRangeStart[], SIZE_T cObjectIDRangeLength[])
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::SurvivingReferences2(ULONG cSurvivingObjectIDRanges, ObjectID objectIDRangeStart[], SIZE_T cObjectIDRangeLength[])
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ConditionalWeakTableElementReferences(ULONG cRootRefs, ObjectID keyRefIds[], ObjectID valueRefIds[], GCHandleID rootIds[])
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::GetAssemblyReferences(const WCHAR *wszAssemblyPath, ICorProfilerAssemblyReferenceProvider *pAsmRefProvider)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE BPerfProfiler::ModuleInMemorySymbolsUpdated(ModuleID moduleId)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,257 @@
|
|||
// 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 <atomic>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include "cor.h"
|
||||
#include "corprof.h"
|
||||
#include "sqlite3.h"
|
||||
#include "ThreadSafeQueue.h"
|
||||
#include "EtwEvent.h"
|
||||
#include "RundownList.h"
|
||||
#include "AssemblyLoadEvent.h"
|
||||
#include "AppDomainLoadEvent.h"
|
||||
#include "ModuleLoadEvent.h"
|
||||
#include "MethodILToNativeMapEvent.h"
|
||||
#include "JITCompilationFinishedEvent.h"
|
||||
|
||||
class BPerfProfiler : public ICorProfilerCallback7
|
||||
{
|
||||
public:
|
||||
BPerfProfiler();
|
||||
virtual ~BPerfProfiler();
|
||||
HRESULT STDMETHODCALLTYPE Initialize(IUnknown *pICorProfilerInfoUnk) override;
|
||||
HRESULT STDMETHODCALLTYPE Shutdown() override;
|
||||
HRESULT STDMETHODCALLTYPE AppDomainCreationStarted(AppDomainID appDomainId) override;
|
||||
HRESULT STDMETHODCALLTYPE AppDomainCreationFinished(AppDomainID appDomainId, HRESULT hrStatus) override;
|
||||
HRESULT STDMETHODCALLTYPE AppDomainShutdownStarted(AppDomainID appDomainId) override;
|
||||
HRESULT STDMETHODCALLTYPE AppDomainShutdownFinished(AppDomainID appDomainId, HRESULT hrStatus) override;
|
||||
HRESULT STDMETHODCALLTYPE AssemblyLoadStarted(AssemblyID assemblyId) override;
|
||||
HRESULT STDMETHODCALLTYPE AssemblyLoadFinished(AssemblyID assemblyId, HRESULT hrStatus) override;
|
||||
HRESULT STDMETHODCALLTYPE AssemblyUnloadStarted(AssemblyID assemblyId) override;
|
||||
HRESULT STDMETHODCALLTYPE AssemblyUnloadFinished(AssemblyID assemblyId, HRESULT hrStatus) override;
|
||||
HRESULT STDMETHODCALLTYPE ModuleLoadStarted(ModuleID moduleId) override;
|
||||
HRESULT STDMETHODCALLTYPE ModuleLoadFinished(ModuleID moduleId, HRESULT hrStatus) override;
|
||||
HRESULT STDMETHODCALLTYPE ModuleUnloadStarted(ModuleID moduleId) override;
|
||||
HRESULT STDMETHODCALLTYPE ModuleUnloadFinished(ModuleID moduleId, HRESULT hrStatus) override;
|
||||
HRESULT STDMETHODCALLTYPE ModuleAttachedToAssembly(ModuleID moduleId, AssemblyID AssemblyId) override;
|
||||
HRESULT STDMETHODCALLTYPE ClassLoadStarted(ClassID classId) override;
|
||||
HRESULT STDMETHODCALLTYPE ClassLoadFinished(ClassID classId, HRESULT hrStatus) override;
|
||||
HRESULT STDMETHODCALLTYPE ClassUnloadStarted(ClassID classId) override;
|
||||
HRESULT STDMETHODCALLTYPE ClassUnloadFinished(ClassID classId, HRESULT hrStatus) override;
|
||||
HRESULT STDMETHODCALLTYPE FunctionUnloadStarted(FunctionID functionId) override;
|
||||
HRESULT STDMETHODCALLTYPE JITCompilationStarted(FunctionID functionId, BOOL fIsSafeToBlock) override;
|
||||
HRESULT STDMETHODCALLTYPE JITCompilationFinished(FunctionID functionId, HRESULT hrStatus, BOOL fIsSafeToBlock) override;
|
||||
HRESULT STDMETHODCALLTYPE JITCachedFunctionSearchStarted(FunctionID functionId, BOOL *pbUseCachedFunction) override;
|
||||
HRESULT STDMETHODCALLTYPE JITCachedFunctionSearchFinished(FunctionID functionId, COR_PRF_JIT_CACHE result) override;
|
||||
HRESULT STDMETHODCALLTYPE JITFunctionPitched(FunctionID functionId) override;
|
||||
HRESULT STDMETHODCALLTYPE JITInlining(FunctionID callerId, FunctionID calleeId, BOOL *pfShouldInline) override;
|
||||
HRESULT STDMETHODCALLTYPE ThreadCreated(ThreadID threadId) override;
|
||||
HRESULT STDMETHODCALLTYPE ThreadDestroyed(ThreadID threadId) override;
|
||||
HRESULT STDMETHODCALLTYPE ThreadAssignedToOSThread(ThreadID managedThreadId, DWORD osThreadId) override;
|
||||
HRESULT STDMETHODCALLTYPE RemotingClientInvocationStarted() override;
|
||||
HRESULT STDMETHODCALLTYPE RemotingClientSendingMessage(GUID *pCookie, BOOL fIsAsync) override;
|
||||
HRESULT STDMETHODCALLTYPE RemotingClientReceivingReply(GUID *pCookie, BOOL fIsAsync) override;
|
||||
HRESULT STDMETHODCALLTYPE RemotingClientInvocationFinished() override;
|
||||
HRESULT STDMETHODCALLTYPE RemotingServerReceivingMessage(GUID *pCookie, BOOL fIsAsync) override;
|
||||
HRESULT STDMETHODCALLTYPE RemotingServerInvocationStarted() override;
|
||||
HRESULT STDMETHODCALLTYPE RemotingServerInvocationReturned() override;
|
||||
HRESULT STDMETHODCALLTYPE RemotingServerSendingReply(GUID *pCookie, BOOL fIsAsync) override;
|
||||
HRESULT STDMETHODCALLTYPE UnmanagedToManagedTransition(FunctionID functionId, COR_PRF_TRANSITION_REASON reason) override;
|
||||
HRESULT STDMETHODCALLTYPE ManagedToUnmanagedTransition(FunctionID functionId, COR_PRF_TRANSITION_REASON reason) override;
|
||||
HRESULT STDMETHODCALLTYPE RuntimeSuspendStarted(COR_PRF_SUSPEND_REASON suspendReason) override;
|
||||
HRESULT STDMETHODCALLTYPE RuntimeSuspendFinished() override;
|
||||
HRESULT STDMETHODCALLTYPE RuntimeSuspendAborted() override;
|
||||
HRESULT STDMETHODCALLTYPE RuntimeResumeStarted() override;
|
||||
HRESULT STDMETHODCALLTYPE RuntimeResumeFinished() override;
|
||||
HRESULT STDMETHODCALLTYPE RuntimeThreadSuspended(ThreadID threadId) override;
|
||||
HRESULT STDMETHODCALLTYPE RuntimeThreadResumed(ThreadID threadId) override;
|
||||
HRESULT STDMETHODCALLTYPE MovedReferences(ULONG cMovedObjectIDRanges, ObjectID oldObjectIDRangeStart[], ObjectID newObjectIDRangeStart[], ULONG cObjectIDRangeLength[]) override;
|
||||
HRESULT STDMETHODCALLTYPE ObjectAllocated(ObjectID objectId, ClassID classId) override;
|
||||
HRESULT STDMETHODCALLTYPE ObjectsAllocatedByClass(ULONG cClassCount, ClassID classIds[], ULONG cObjects[]) override;
|
||||
HRESULT STDMETHODCALLTYPE ObjectReferences(ObjectID objectId, ClassID classId, ULONG cObjectRefs, ObjectID objectRefIds[]) override;
|
||||
HRESULT STDMETHODCALLTYPE RootReferences(ULONG cRootRefs, ObjectID rootRefIds[]) override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionThrown(ObjectID thrownObjectId) override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionSearchFunctionEnter(FunctionID functionId) override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionSearchFunctionLeave() override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionSearchFilterEnter(FunctionID functionId) override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionSearchFilterLeave() override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionSearchCatcherFound(FunctionID functionId) override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionOSHandlerEnter(UINT_PTR __unused) override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionOSHandlerLeave(UINT_PTR __unused) override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionUnwindFunctionEnter(FunctionID functionId) override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionUnwindFunctionLeave() override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionUnwindFinallyEnter(FunctionID functionId) override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionUnwindFinallyLeave() override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionCatcherEnter(FunctionID functionId, ObjectID objectId) override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionCatcherLeave() override;
|
||||
HRESULT STDMETHODCALLTYPE COMClassicVTableCreated(ClassID wrappedClassId, REFGUID implementedIID, void *pVTable, ULONG cSlots) override;
|
||||
HRESULT STDMETHODCALLTYPE COMClassicVTableDestroyed(ClassID wrappedClassId, REFGUID implementedIID, void *pVTable) override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionCLRCatcherFound() override;
|
||||
HRESULT STDMETHODCALLTYPE ExceptionCLRCatcherExecute() override;
|
||||
HRESULT STDMETHODCALLTYPE ThreadNameChanged(ThreadID threadId, ULONG cchName, WCHAR name[]) override;
|
||||
HRESULT STDMETHODCALLTYPE GarbageCollectionStarted(int cGenerations, BOOL generationCollected[], COR_PRF_GC_REASON reason) override;
|
||||
HRESULT STDMETHODCALLTYPE SurvivingReferences(ULONG cSurvivingObjectIDRanges, ObjectID objectIDRangeStart[], ULONG cObjectIDRangeLength[]) override;
|
||||
HRESULT STDMETHODCALLTYPE GarbageCollectionFinished() override;
|
||||
HRESULT STDMETHODCALLTYPE FinalizeableObjectQueued(DWORD finalizerFlags, ObjectID objectID) override;
|
||||
HRESULT STDMETHODCALLTYPE RootReferences2(ULONG cRootRefs, ObjectID rootRefIds[], COR_PRF_GC_ROOT_KIND rootKinds[], COR_PRF_GC_ROOT_FLAGS rootFlags[], UINT_PTR rootIds[]) override;
|
||||
HRESULT STDMETHODCALLTYPE HandleCreated(GCHandleID handleId, ObjectID initialObjectId) override;
|
||||
HRESULT STDMETHODCALLTYPE HandleDestroyed(GCHandleID handleId) override;
|
||||
HRESULT STDMETHODCALLTYPE InitializeForAttach(IUnknown *pCorProfilerInfoUnk, void *pvClientData, UINT cbClientData) override;
|
||||
HRESULT STDMETHODCALLTYPE ProfilerAttachComplete() override;
|
||||
HRESULT STDMETHODCALLTYPE ProfilerDetachSucceeded() override;
|
||||
HRESULT STDMETHODCALLTYPE ReJITCompilationStarted(FunctionID functionId, ReJITID rejitId, BOOL fIsSafeToBlock) override;
|
||||
HRESULT STDMETHODCALLTYPE GetReJITParameters(ModuleID moduleId, mdMethodDef methodId, ICorProfilerFunctionControl *pFunctionControl) override;
|
||||
HRESULT STDMETHODCALLTYPE ReJITCompilationFinished(FunctionID functionId, ReJITID rejitId, HRESULT hrStatus, BOOL fIsSafeToBlock) override;
|
||||
HRESULT STDMETHODCALLTYPE ReJITError(ModuleID moduleId, mdMethodDef methodId, FunctionID functionId, HRESULT hrStatus) override;
|
||||
HRESULT STDMETHODCALLTYPE MovedReferences2(ULONG cMovedObjectIDRanges, ObjectID oldObjectIDRangeStart[], ObjectID newObjectIDRangeStart[], SIZE_T cObjectIDRangeLength[]) override;
|
||||
HRESULT STDMETHODCALLTYPE SurvivingReferences2(ULONG cSurvivingObjectIDRanges, ObjectID objectIDRangeStart[], SIZE_T cObjectIDRangeLength[]) override;
|
||||
HRESULT STDMETHODCALLTYPE ConditionalWeakTableElementReferences(ULONG cRootRefs, ObjectID keyRefIds[], ObjectID valueRefIds[], GCHandleID rootIds[]) override;
|
||||
HRESULT STDMETHODCALLTYPE GetAssemblyReferences(const WCHAR *wszAssemblyPath, ICorProfilerAssemblyReferenceProvider *pAsmRefProvider) override;
|
||||
HRESULT STDMETHODCALLTYPE ModuleInMemorySymbolsUpdated(ModuleID moduleId) override;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override
|
||||
{
|
||||
if (riid == __uuidof(ICorProfilerCallback7) ||
|
||||
riid == __uuidof(ICorProfilerCallback6) ||
|
||||
riid == __uuidof(ICorProfilerCallback5) ||
|
||||
riid == __uuidof(ICorProfilerCallback4) ||
|
||||
riid == __uuidof(ICorProfilerCallback3) ||
|
||||
riid == __uuidof(ICorProfilerCallback2) ||
|
||||
riid == __uuidof(ICorProfilerCallback) ||
|
||||
riid == IID_IUnknown)
|
||||
{
|
||||
*ppvObject = this;
|
||||
this->AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef(void) override
|
||||
{
|
||||
return std::atomic_fetch_add(&this->refCount, 1) + 1;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release(void) override
|
||||
{
|
||||
int count = std::atomic_fetch_sub(&this->refCount, 1) - 1;
|
||||
|
||||
if (count <= 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void STDMETHODCALLTYPE Rundown();
|
||||
|
||||
std::shared_ptr<ThreadSafeQueue<std::unique_ptr<EtwEvent>>> eventQueue;
|
||||
|
||||
private:
|
||||
USHORT clrInstanceId;
|
||||
std::atomic<int> refCount;
|
||||
ICorProfilerInfo7 *corProfilerInfo;
|
||||
sqlite3 *db;
|
||||
std::thread eventLoggerThread;
|
||||
bool shouldMaintainShadowStack;
|
||||
|
||||
/* There is coupling between the rundown lists and their corresponding map */
|
||||
/* Essentially we are returning naked pointers (RundownListNodeId) to the maps */
|
||||
/* When rundown list is torn down these are invalid obviously, however the */
|
||||
/* lifetimes of the maps and rundown lists are equivalent so when the dtor runs */
|
||||
/* it shouldn't matter if the rundown list is freed first since maps can't access */
|
||||
/* their contents in the dtor anyway */
|
||||
|
||||
std::mutex assemblyMutex;
|
||||
RundownList<AssemblyLoadEvent> assemblyRundown;
|
||||
std::unordered_map<AssemblyID, RundownListNodeId<AssemblyLoadEvent>> assemblyInfoMap;
|
||||
|
||||
std::mutex appDomainMutex;
|
||||
RundownList<AppDomainLoadEvent> appDomainRundown;
|
||||
std::unordered_map<AppDomainID, RundownListNodeId<AppDomainLoadEvent>> appDomainInfoMap;
|
||||
|
||||
std::mutex moduleMutex;
|
||||
RundownList<ModuleLoadEvent> moduleRundown;
|
||||
std::unordered_map<ModuleID, RundownListNodeId<ModuleLoadEvent>> moduleInfoMap;
|
||||
|
||||
std::mutex methodMutex;
|
||||
RundownList<JITCompilationFinishedEvent> methodRundown;
|
||||
std::unordered_map<FunctionID, RundownListNodeId<JITCompilationFinishedEvent>> methodInfoMap;
|
||||
|
||||
std::mutex ilNativeMutex;
|
||||
RundownList<MethodILToNativeMapEvent> ilNativeMapRundown;
|
||||
std::unordered_map<FunctionID, RundownListNodeId<MethodILToNativeMapEvent>> ilNativeInfoMap;
|
||||
|
||||
HRESULT AssemblyLoadUnloadData(AssemblyID assemblId, bool load);
|
||||
HRESULT ModuleLoadUnloadData(ModuleID moduleId, bool load);
|
||||
HRESULT MethodJitStartFinishData(FunctionID functionId, bool finished, bool unload);
|
||||
};
|
||||
|
||||
static bool IsEnvironmentVariableEnabled(const char *envVarName)
|
||||
{
|
||||
char *result;
|
||||
|
||||
#ifdef WIN32
|
||||
size_t requiredSize;
|
||||
|
||||
getenv_s(&requiredSize, nullptr, 0, envVarName);
|
||||
if (requiredSize == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result = static_cast<char *>(malloc(requiredSize * sizeof(char)));
|
||||
if (result == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
getenv_s(&requiredSize, result, requiredSize, envVarName);
|
||||
#else
|
||||
result = std::getenv(envVarName);
|
||||
if (result == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string env(result);
|
||||
size_t size = env.size();
|
||||
if (size == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size == 1)
|
||||
{
|
||||
if (env[0] == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T, class... Types>
|
||||
std::unique_ptr<T> LogEvent2(const std::shared_ptr<ThreadSafeQueue<std::unique_ptr<EtwEvent>>> &eventQueue, Types &&... Args)
|
||||
{
|
||||
eventQueue->push(std::unique_ptr<T>(new T(std::forward<Types>(Args)...)));
|
||||
return std::unique_ptr<T>(new T(std::forward<Types>(Args)...));
|
||||
}
|
||||
|
||||
template <class T, class... Types>
|
||||
std::unique_ptr<T> LogEvent(Types &&... Args)
|
||||
{
|
||||
return std::unique_ptr<T>(new T(std::forward<Types>(Args)...));
|
||||
}
|
||||
|
||||
#define addr(x) &x[0]
|
|
@ -0,0 +1,62 @@
|
|||
// 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
|
||||
|
||||
template <class TInterface>
|
||||
class CComPtr
|
||||
{
|
||||
private:
|
||||
TInterface *pointer;
|
||||
|
||||
public:
|
||||
CComPtr(const CComPtr &) = delete; // Copy constructor
|
||||
CComPtr &operator=(const CComPtr &) = delete; // Copy assignment
|
||||
CComPtr(CComPtr &&) = delete; // Move constructor
|
||||
CComPtr &operator=(CComPtr &&) = delete; // Move assignment
|
||||
|
||||
void *operator new(std::size_t) = delete;
|
||||
void *operator new[](std::size_t) = delete;
|
||||
|
||||
void operator delete(void *ptr) = delete;
|
||||
void operator delete[](void *ptr) = delete;
|
||||
|
||||
CComPtr()
|
||||
{
|
||||
this->pointer = nullptr;
|
||||
}
|
||||
|
||||
~CComPtr()
|
||||
{
|
||||
if (this->pointer)
|
||||
{
|
||||
this->pointer->Release();
|
||||
this->pointer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
operator TInterface *()
|
||||
{
|
||||
return this->pointer;
|
||||
}
|
||||
|
||||
operator TInterface *() const = delete;
|
||||
|
||||
TInterface &operator*() = delete;
|
||||
|
||||
TInterface &operator*() const = delete;
|
||||
|
||||
TInterface **operator&()
|
||||
{
|
||||
return &this->pointer;
|
||||
}
|
||||
|
||||
TInterface **operator&() const = delete;
|
||||
|
||||
TInterface *operator->()
|
||||
{
|
||||
return this->pointer;
|
||||
}
|
||||
|
||||
TInterface *operator->() const = delete;
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include <cstdlib>
|
||||
#include "CQuickBytes.h"
|
||||
|
||||
CQuickBytes::CQuickBytes()
|
||||
{
|
||||
this->size = 0;
|
||||
this->availableBytes = 1024;
|
||||
this->bytes.resize(this->availableBytes);
|
||||
}
|
||||
size_t CQuickBytes::Size() const { return this->size; }
|
||||
void *CQuickBytes::Ptr() { return this->bytes.data(); }
|
||||
int CQuickBytes::ReSizeNoThrow(size_t iItems)
|
||||
{
|
||||
this->size = iItems;
|
||||
|
||||
if (iItems > this->availableBytes)
|
||||
{
|
||||
this->availableBytes *= 2;
|
||||
this->bytes.resize(this->availableBytes);
|
||||
}
|
||||
|
||||
return 0; // S_OK
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// 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 <vector>
|
||||
|
||||
class CQuickBytes
|
||||
{
|
||||
public:
|
||||
CQuickBytes();
|
||||
size_t Size() const;
|
||||
void *Ptr();
|
||||
int ReSizeNoThrow(size_t);
|
||||
|
||||
private:
|
||||
std::vector<char *> bytes;
|
||||
size_t size;
|
||||
size_t availableBytes;
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "ClassFactory.h"
|
||||
#include "BPerfProfiler.h"
|
||||
|
||||
ClassFactory::ClassFactory() : refCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
ClassFactory::~ClassFactory()
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE ClassFactory::QueryInterface(REFIID riid, void **ppvObject)
|
||||
{
|
||||
if (riid == IID_IUnknown || riid == IID_IClassFactory)
|
||||
{
|
||||
*ppvObject = this;
|
||||
this->AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE ClassFactory::AddRef()
|
||||
{
|
||||
return std::atomic_fetch_add(&this->refCount, 1) + 1;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE ClassFactory::Release()
|
||||
{
|
||||
int count = std::atomic_fetch_sub(&this->refCount, 1) - 1;
|
||||
if (count <= 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject)
|
||||
{
|
||||
if (pUnkOuter != nullptr)
|
||||
{
|
||||
*ppvObject = nullptr;
|
||||
return CLASS_E_NOAGGREGATION;
|
||||
}
|
||||
|
||||
auto profiler = new BPerfProfiler();
|
||||
if (profiler == nullptr)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
return profiler->QueryInterface(riid, ppvObject);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE ClassFactory::LockServer(BOOL fLock)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// 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 "unknwn.h"
|
||||
#include <atomic>
|
||||
|
||||
class ClassFactory : public IClassFactory
|
||||
{
|
||||
private:
|
||||
std::atomic<int> refCount;
|
||||
|
||||
public:
|
||||
ClassFactory();
|
||||
virtual ~ClassFactory();
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
|
||||
ULONG STDMETHODCALLTYPE AddRef(void) override;
|
||||
ULONG STDMETHODCALLTYPE Release(void) override;
|
||||
HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject) override;
|
||||
HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock) override;
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include <chrono>
|
||||
#include "EtwEvent.h"
|
||||
#include "profiler_pal.h"
|
||||
|
||||
EtwEvent::EtwEvent() : processId(GetCurrentProcessId()), threadId(GetCurrentThreadId()), timestamp(std::chrono::steady_clock::now().time_since_epoch().count())
|
||||
{
|
||||
}
|
||||
|
||||
void EtwEvent::Serialize(Serializer &t)
|
||||
{
|
||||
t.Write(this->timestamp);
|
||||
t.Write(this->processId);
|
||||
t.Write(this->threadId);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// 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 "Serializer.h"
|
||||
|
||||
class EtwLogger;
|
||||
|
||||
class EtwEvent
|
||||
{
|
||||
public:
|
||||
virtual ~EtwEvent() = default;
|
||||
EtwEvent();
|
||||
virtual int32_t GetEventId() = 0;
|
||||
virtual void Serialize(Serializer &t);
|
||||
|
||||
protected:
|
||||
int32_t processId;
|
||||
int32_t threadId;
|
||||
int64_t timestamp;
|
||||
};
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,44 @@
|
|||
// 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 "EtwLogger.Generated.h"
|
||||
|
||||
#ifdef _WINDOWS
|
||||
|
||||
class EtwLogger
|
||||
{
|
||||
public:
|
||||
static void WriteEvent(AppDomainLoadEvent * data);
|
||||
static void WriteEvent(AssemblyLoadEvent * data);
|
||||
static void WriteEvent(ModuleLoadEvent * data);
|
||||
static void WriteEvent(JITCompilationFinishedEvent * data);
|
||||
static void WriteEvent(MethodILToNativeMapEvent * data);
|
||||
};
|
||||
|
||||
void EtwLogger::WriteEvent(AppDomainLoadEvent *data)
|
||||
{
|
||||
EventWriteAppDomainDCEnd_V1(data->appDomainId, data->appDomainFlags, data->appDomainName.c_str(), data->appDomainIndex, data->clrInstanceId);
|
||||
}
|
||||
|
||||
void EtwLogger::WriteEvent(AssemblyLoadEvent *data)
|
||||
{
|
||||
EventWriteAssemblyDCEnd_V1(data->assemblyId, data->appDomainId, data->bindingId, data->assemblyFlags, data->fullyQualifiedAssemblyName.c_str(), data->clrInstanceId);
|
||||
}
|
||||
|
||||
void EtwLogger::WriteEvent(ModuleLoadEvent *data)
|
||||
{
|
||||
EventWriteModuleDCEnd_V2(data->moduleId, data->assemblyId, data->moduleFlags, 0, data->moduleILPath.c_str(), data->moduleNativePath.c_str(), data->clrInstanceId, &data->managedPdbSignature, data->managedPdbAge, data->managedPdbBuildPath.c_str(), &data->nativePdbSignature, data->nativePdbAge, data->nativePdbBuildPath.c_str());
|
||||
}
|
||||
|
||||
void EtwLogger::WriteEvent(JITCompilationFinishedEvent *data)
|
||||
{
|
||||
EventWriteMethodDCEndVerbose_V2(data->methodId, data->moduleId, data->methodStartAddress, data->methodSize, data->methodToken, data->methodFlags, data->methodNamespace.c_str(), data->methodName.c_str(), data->methodSignature.c_str(), data->clrInstanceId, data->rejitId);
|
||||
}
|
||||
|
||||
void EtwLogger::WriteEvent(MethodILToNativeMapEvent *data)
|
||||
{
|
||||
EventWriteMethodDCEndILToNativeMap(data->methodId, data->reJitId, data->methodExtent, data->countOfMapEntries, data->ilOffsets.data(), data->nativeOffsets.data(), data->clrInstanceId);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef GUID_DEFINED
|
||||
#define GUID_DEFINED
|
||||
#if defined(__midl)
|
||||
typedef struct
|
||||
{
|
||||
unsigned long Data1;
|
||||
unsigned short Data2;
|
||||
unsigned short Data3;
|
||||
byte Data4[8];
|
||||
} GUID;
|
||||
#else
|
||||
typedef struct _GUID
|
||||
{
|
||||
unsigned long Data1;
|
||||
unsigned short Data2;
|
||||
unsigned short Data3;
|
||||
unsigned char Data4[8];
|
||||
} GUID;
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "HeartBeatEvent.h"
|
||||
|
||||
HeartBeatEvent::HeartBeatEvent() : EtwEvent()
|
||||
{
|
||||
}
|
||||
|
||||
void HeartBeatEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
}
|
||||
|
||||
int32_t HeartBeatEvent::GetEventId()
|
||||
{
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// 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 "EtwEvent.h"
|
||||
|
||||
class HeartBeatEvent : public EtwEvent
|
||||
{
|
||||
public:
|
||||
HeartBeatEvent();
|
||||
void Serialize(Serializer &t) override;
|
||||
int32_t GetEventId() override;
|
||||
};
|
|
@ -0,0 +1,843 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "cor.h"
|
||||
#include "corprof.h"
|
||||
#include "ILRewriter.h"
|
||||
#include <corhlpr.cpp>
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
|
||||
#undef IfFailRet
|
||||
#define IfFailRet(EXPR) do { HRESULT hr = (EXPR); if(FAILED(hr)) { return (hr); } } while (0)
|
||||
|
||||
#undef IfNullRet
|
||||
#define IfNullRet(EXPR) do { if ((EXPR) == NULL) return E_OUTOFMEMORY; } while (0)
|
||||
|
||||
struct ILInstr
|
||||
{
|
||||
ILInstr * m_pNext;
|
||||
ILInstr * m_pPrev;
|
||||
|
||||
unsigned m_opcode;
|
||||
unsigned m_offset;
|
||||
|
||||
union
|
||||
{
|
||||
ILInstr * m_pTarget;
|
||||
INT8 m_Arg8;
|
||||
INT16 m_Arg16;
|
||||
INT32 m_Arg32;
|
||||
INT64 m_Arg64;
|
||||
};
|
||||
};
|
||||
|
||||
struct EHClause
|
||||
{
|
||||
CorExceptionFlag m_Flags;
|
||||
ILInstr * m_pTryBegin;
|
||||
ILInstr * m_pTryEnd;
|
||||
ILInstr * m_pHandlerBegin; // First instruction inside the handler
|
||||
ILInstr * m_pHandlerEnd; // Last instruction inside the handler
|
||||
union
|
||||
{
|
||||
DWORD m_ClassToken; // use for type-based exception handlers
|
||||
ILInstr * m_pFilter; // use for filter-based exception handlers (COR_ILEXCEPTION_CLAUSE_FILTER is set)
|
||||
};
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#define OPDEF(c,s,pop,push,args,type,l,s1,s2,ctrl) c,
|
||||
#include "opcode.def"
|
||||
#undef OPDEF
|
||||
CEE_COUNT,
|
||||
CEE_SWITCH_ARG, // special internal instructions
|
||||
} OPCODE;
|
||||
|
||||
#define OPCODEFLAGS_SizeMask 0x0F
|
||||
#define OPCODEFLAGS_BranchTarget 0x10
|
||||
#define OPCODEFLAGS_Switch 0x20
|
||||
|
||||
static const BYTE s_OpCodeFlags[] =
|
||||
{
|
||||
#define InlineNone 0
|
||||
#define ShortInlineVar 1
|
||||
#define InlineVar 2
|
||||
#define ShortInlineI 1
|
||||
#define InlineI 4
|
||||
#define InlineI8 8
|
||||
#define ShortInlineR 4
|
||||
#define InlineR 8
|
||||
#define ShortInlineBrTarget 1 | OPCODEFLAGS_BranchTarget
|
||||
#define InlineBrTarget 4 | OPCODEFLAGS_BranchTarget
|
||||
#define InlineMethod 4
|
||||
#define InlineField 4
|
||||
#define InlineType 4
|
||||
#define InlineString 4
|
||||
#define InlineSig 4
|
||||
#define InlineRVA 4
|
||||
#define InlineTok 4
|
||||
#define InlineSwitch 0 | OPCODEFLAGS_Switch
|
||||
|
||||
#define OPDEF(c,s,pop,push,args,type,l,s1,s2,flow) args,
|
||||
#include "opcode.def"
|
||||
#undef OPDEF
|
||||
|
||||
#undef InlineNone
|
||||
#undef ShortInlineVar
|
||||
#undef InlineVar
|
||||
#undef ShortInlineI
|
||||
#undef InlineI
|
||||
#undef InlineI8
|
||||
#undef ShortInlineR
|
||||
#undef InlineR
|
||||
#undef ShortInlineBrTarget
|
||||
#undef InlineBrTarget
|
||||
#undef InlineMethod
|
||||
#undef InlineField
|
||||
#undef InlineType
|
||||
#undef InlineString
|
||||
#undef InlineSig
|
||||
#undef InlineRVA
|
||||
#undef InlineTok
|
||||
#undef InlineSwitch
|
||||
0, // CEE_COUNT
|
||||
4 | OPCODEFLAGS_BranchTarget, // CEE_SWITCH_ARG
|
||||
};
|
||||
|
||||
static int k_rgnStackPushes[] = {
|
||||
|
||||
#define OPDEF(c,s,pop,push,args,type,l,s1,s2,ctrl) \
|
||||
push ,
|
||||
|
||||
#define Push0 0
|
||||
#define Push1 1
|
||||
#define PushI 1
|
||||
#define PushI4 1
|
||||
#define PushR4 1
|
||||
#define PushI8 1
|
||||
#define PushR8 1
|
||||
#define PushRef 1
|
||||
#define VarPush 1 // Test code doesn't call vararg fcns, so this should not be used
|
||||
|
||||
#include "opcode.def"
|
||||
|
||||
#undef Push0
|
||||
#undef Push1
|
||||
#undef PushI
|
||||
#undef PushI4
|
||||
#undef PushR4
|
||||
#undef PushI8
|
||||
#undef PushR8
|
||||
#undef PushRef
|
||||
#undef VarPush
|
||||
#undef OPDEF
|
||||
};
|
||||
|
||||
class ILRewriter
|
||||
{
|
||||
private:
|
||||
ICorProfilerInfo * m_pICorProfilerInfo;
|
||||
ICorProfilerFunctionControl * m_pICorProfilerFunctionControl;
|
||||
|
||||
ModuleID m_moduleId;
|
||||
mdToken m_tkMethod;
|
||||
|
||||
mdToken m_tkLocalVarSig;
|
||||
unsigned m_maxStack;
|
||||
unsigned m_flags;
|
||||
bool m_fGenerateTinyHeader;
|
||||
|
||||
ILInstr m_IL; // Double linked list of all il instructions
|
||||
|
||||
unsigned m_nEH;
|
||||
EHClause * m_pEH;
|
||||
|
||||
// Helper table for importing. Sparse array that maps BYTE offset of beginning of an
|
||||
// instruction to that instruction's ILInstr*. BYTE offsets that don't correspond
|
||||
// to the beginning of an instruction are mapped to NULL.
|
||||
ILInstr ** m_pOffsetToInstr;
|
||||
unsigned m_CodeSize;
|
||||
|
||||
unsigned m_nInstrs;
|
||||
|
||||
BYTE * m_pOutputBuffer;
|
||||
|
||||
IMethodMalloc * m_pIMethodMalloc;
|
||||
|
||||
public:
|
||||
ILRewriter(ICorProfilerInfo * pICorProfilerInfo, ICorProfilerFunctionControl * pICorProfilerFunctionControl, ModuleID moduleID, mdToken tkMethod)
|
||||
: m_pICorProfilerInfo(pICorProfilerInfo), m_pICorProfilerFunctionControl(pICorProfilerFunctionControl),
|
||||
m_moduleId(moduleID), m_tkMethod(tkMethod), m_fGenerateTinyHeader(false),
|
||||
m_pEH(nullptr), m_pOffsetToInstr(nullptr), m_pOutputBuffer(nullptr), m_pIMethodMalloc(nullptr)
|
||||
{
|
||||
m_IL.m_pNext = &m_IL;
|
||||
m_IL.m_pPrev = &m_IL;
|
||||
|
||||
m_nInstrs = 0;
|
||||
}
|
||||
|
||||
~ILRewriter()
|
||||
{
|
||||
ILInstr * p = m_IL.m_pNext;
|
||||
while (p != &m_IL)
|
||||
{
|
||||
ILInstr * t = p->m_pNext;
|
||||
delete p;
|
||||
p = t;
|
||||
}
|
||||
delete[] m_pEH;
|
||||
delete[] m_pOffsetToInstr;
|
||||
delete[] m_pOutputBuffer;
|
||||
|
||||
if (m_pIMethodMalloc)
|
||||
m_pIMethodMalloc->Release();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// I M P O R T
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
HRESULT Import()
|
||||
{
|
||||
LPCBYTE pMethodBytes;
|
||||
|
||||
IfFailRet(m_pICorProfilerInfo->GetILFunctionBody(
|
||||
m_moduleId, m_tkMethod, &pMethodBytes, NULL));
|
||||
|
||||
COR_ILMETHOD_DECODER decoder((COR_ILMETHOD*)pMethodBytes);
|
||||
|
||||
// Import the header flags
|
||||
m_tkLocalVarSig = decoder.GetLocalVarSigTok();
|
||||
m_maxStack = decoder.GetMaxStack();
|
||||
m_flags = (decoder.GetFlags() & CorILMethod_InitLocals);
|
||||
|
||||
m_CodeSize = decoder.GetCodeSize();
|
||||
|
||||
IfFailRet(ImportIL(decoder.Code));
|
||||
|
||||
IfFailRet(ImportEH(decoder.EH, decoder.EHCount()));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ImportIL(LPCBYTE pIL)
|
||||
{
|
||||
m_pOffsetToInstr = new ILInstr*[m_CodeSize + 1];
|
||||
IfNullRet(m_pOffsetToInstr);
|
||||
|
||||
ZeroMemory(m_pOffsetToInstr, m_CodeSize * sizeof(ILInstr*));
|
||||
|
||||
// Set the sentinel instruction
|
||||
m_pOffsetToInstr[m_CodeSize] = &m_IL;
|
||||
m_IL.m_opcode = -1;
|
||||
|
||||
bool fBranch = false;
|
||||
unsigned offset = 0;
|
||||
while (offset < m_CodeSize)
|
||||
{
|
||||
unsigned startOffset = offset;
|
||||
unsigned opcode = pIL[offset++];
|
||||
|
||||
if (opcode == CEE_PREFIX1)
|
||||
{
|
||||
if (offset >= m_CodeSize)
|
||||
{
|
||||
assert(false);
|
||||
return COR_E_INVALIDPROGRAM;
|
||||
}
|
||||
opcode = 0x100 + pIL[offset++];
|
||||
}
|
||||
|
||||
if ((CEE_PREFIX7 <= opcode) && (opcode <= CEE_PREFIX2))
|
||||
{
|
||||
// NOTE: CEE_PREFIX2-7 are currently not supported
|
||||
assert(false);
|
||||
return COR_E_INVALIDPROGRAM;
|
||||
}
|
||||
|
||||
if (opcode >= CEE_COUNT)
|
||||
{
|
||||
assert(false);
|
||||
return COR_E_INVALIDPROGRAM;
|
||||
}
|
||||
|
||||
BYTE flags = s_OpCodeFlags[opcode];
|
||||
|
||||
int size = (flags & OPCODEFLAGS_SizeMask);
|
||||
if (offset + size > m_CodeSize)
|
||||
{
|
||||
assert(false);
|
||||
return COR_E_INVALIDPROGRAM;
|
||||
}
|
||||
|
||||
ILInstr * pInstr = NewILInstr();
|
||||
IfNullRet(pInstr);
|
||||
|
||||
pInstr->m_opcode = opcode;
|
||||
|
||||
InsertBefore(&m_IL, pInstr);
|
||||
|
||||
m_pOffsetToInstr[startOffset] = pInstr;
|
||||
|
||||
switch (flags)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
pInstr->m_Arg8 = *(UNALIGNED INT8 *)&(pIL[offset]);
|
||||
break;
|
||||
case 2:
|
||||
pInstr->m_Arg16 = *(UNALIGNED INT16 *)&(pIL[offset]);
|
||||
break;
|
||||
case 4:
|
||||
pInstr->m_Arg32 = *(UNALIGNED INT32 *)&(pIL[offset]);
|
||||
break;
|
||||
case 8:
|
||||
pInstr->m_Arg64 = *(UNALIGNED INT64 *)&(pIL[offset]);
|
||||
break;
|
||||
case 1 | OPCODEFLAGS_BranchTarget:
|
||||
pInstr->m_Arg32 = offset + 1 + *(UNALIGNED INT8 *)&(pIL[offset]);
|
||||
fBranch = true;
|
||||
break;
|
||||
case 4 | OPCODEFLAGS_BranchTarget:
|
||||
pInstr->m_Arg32 = offset + 4 + *(UNALIGNED INT32 *)&(pIL[offset]);
|
||||
fBranch = true;
|
||||
break;
|
||||
case 0 | OPCODEFLAGS_Switch:
|
||||
{
|
||||
if (offset + sizeof(INT32) > m_CodeSize)
|
||||
{
|
||||
assert(false);
|
||||
return COR_E_INVALIDPROGRAM;
|
||||
}
|
||||
|
||||
unsigned nTargets = *(UNALIGNED INT32 *)&(pIL[offset]);
|
||||
pInstr->m_Arg32 = nTargets;
|
||||
offset += sizeof(INT32);
|
||||
|
||||
unsigned base = offset + nTargets * sizeof(INT32);
|
||||
|
||||
for (unsigned iTarget = 0; iTarget < nTargets; iTarget++)
|
||||
{
|
||||
if (offset + sizeof(INT32) > m_CodeSize)
|
||||
{
|
||||
assert(false);
|
||||
return COR_E_INVALIDPROGRAM;
|
||||
}
|
||||
|
||||
pInstr = NewILInstr();
|
||||
IfNullRet(pInstr);
|
||||
|
||||
pInstr->m_opcode = CEE_SWITCH_ARG;
|
||||
|
||||
pInstr->m_Arg32 = base + *(UNALIGNED INT32 *)&(pIL[offset]);
|
||||
offset += sizeof(INT32);
|
||||
|
||||
InsertBefore(&m_IL, pInstr);
|
||||
}
|
||||
fBranch = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
assert(offset == m_CodeSize);
|
||||
|
||||
if (fBranch)
|
||||
{
|
||||
// Go over all control flow instructions and resolve the targets
|
||||
for (ILInstr * pInstr = m_IL.m_pNext; pInstr != &m_IL; pInstr = pInstr->m_pNext)
|
||||
{
|
||||
if (s_OpCodeFlags[pInstr->m_opcode] & OPCODEFLAGS_BranchTarget)
|
||||
pInstr->m_pTarget = GetInstrFromOffset(pInstr->m_Arg32);
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ImportEH(const COR_ILMETHOD_SECT_EH* pILEH, unsigned nEH)
|
||||
{
|
||||
assert(m_pEH == NULL);
|
||||
|
||||
m_nEH = nEH;
|
||||
|
||||
if (nEH == 0)
|
||||
return S_OK;
|
||||
|
||||
IfNullRet(m_pEH = new EHClause[m_nEH]);
|
||||
for (unsigned iEH = 0; iEH < m_nEH; iEH++)
|
||||
{
|
||||
// If the EH clause is in tiny form, the call to pILEH->EHClause() below will
|
||||
// use this as a scratch buffer to expand the EH clause into its fat form.
|
||||
COR_ILMETHOD_SECT_EH_CLAUSE_FAT scratch;
|
||||
|
||||
const COR_ILMETHOD_SECT_EH_CLAUSE_FAT* ehInfo;
|
||||
ehInfo = (COR_ILMETHOD_SECT_EH_CLAUSE_FAT*)pILEH->EHClause(iEH, &scratch);
|
||||
|
||||
EHClause* clause = &(m_pEH[iEH]);
|
||||
clause->m_Flags = ehInfo->GetFlags();
|
||||
|
||||
clause->m_pTryBegin = GetInstrFromOffset(ehInfo->GetTryOffset());
|
||||
clause->m_pTryEnd = GetInstrFromOffset(ehInfo->GetTryOffset() + ehInfo->GetTryLength());
|
||||
clause->m_pHandlerBegin = GetInstrFromOffset(ehInfo->GetHandlerOffset());
|
||||
clause->m_pHandlerEnd = GetInstrFromOffset(ehInfo->GetHandlerOffset() + ehInfo->GetHandlerLength())->m_pPrev;
|
||||
if ((clause->m_Flags & COR_ILEXCEPTION_CLAUSE_FILTER) == 0)
|
||||
clause->m_ClassToken = ehInfo->GetClassToken();
|
||||
else
|
||||
clause->m_pFilter = GetInstrFromOffset(ehInfo->GetFilterOffset());
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ILInstr* NewILInstr()
|
||||
{
|
||||
m_nInstrs++;
|
||||
return new ILInstr();
|
||||
}
|
||||
|
||||
ILInstr* GetInstrFromOffset(unsigned offset)
|
||||
{
|
||||
ILInstr * pInstr = NULL;
|
||||
|
||||
if (offset <= m_CodeSize)
|
||||
pInstr = m_pOffsetToInstr[offset];
|
||||
|
||||
assert(pInstr != NULL);
|
||||
return pInstr;
|
||||
}
|
||||
|
||||
void InsertBefore(ILInstr * pWhere, ILInstr * pWhat)
|
||||
{
|
||||
pWhat->m_pNext = pWhere;
|
||||
pWhat->m_pPrev = pWhere->m_pPrev;
|
||||
|
||||
pWhat->m_pNext->m_pPrev = pWhat;
|
||||
pWhat->m_pPrev->m_pNext = pWhat;
|
||||
|
||||
AdjustState(pWhat);
|
||||
}
|
||||
|
||||
void InsertAfter(ILInstr * pWhere, ILInstr * pWhat)
|
||||
{
|
||||
pWhat->m_pNext = pWhere->m_pNext;
|
||||
pWhat->m_pPrev = pWhere;
|
||||
|
||||
pWhat->m_pNext->m_pPrev = pWhat;
|
||||
pWhat->m_pPrev->m_pNext = pWhat;
|
||||
|
||||
AdjustState(pWhat);
|
||||
}
|
||||
|
||||
void AdjustState(ILInstr * pNewInstr)
|
||||
{
|
||||
m_maxStack += k_rgnStackPushes[pNewInstr->m_opcode];
|
||||
}
|
||||
|
||||
|
||||
ILInstr * GetILList()
|
||||
{
|
||||
return &m_IL;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// E X P O R T
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
HRESULT Export()
|
||||
{
|
||||
// One instruction produces 2 + sizeof(native int) bytes in the worst case which can be 10 bytes for 64-bit.
|
||||
// For simplification we just use 10 here.
|
||||
unsigned maxSize = m_nInstrs * 10;
|
||||
|
||||
m_pOutputBuffer = new BYTE[maxSize];
|
||||
IfNullRet(m_pOutputBuffer);
|
||||
|
||||
again:
|
||||
BYTE * pIL = m_pOutputBuffer;
|
||||
|
||||
bool fBranch = false;
|
||||
unsigned offset = 0;
|
||||
|
||||
// Go over all instructions and produce code for them
|
||||
for (ILInstr * pInstr = m_IL.m_pNext; pInstr != &m_IL; pInstr = pInstr->m_pNext)
|
||||
{
|
||||
assert(offset < maxSize);
|
||||
pInstr->m_offset = offset;
|
||||
|
||||
unsigned opcode = pInstr->m_opcode;
|
||||
if (opcode < CEE_COUNT)
|
||||
{
|
||||
// CEE_PREFIX1 refers not to instruction prefixes (like tail.), but to
|
||||
// the lead byte of multi-byte opcodes. For now, the only lead byte
|
||||
// supported is CEE_PREFIX1 = 0xFE.
|
||||
if (opcode >= 0x100)
|
||||
m_pOutputBuffer[offset++] = CEE_PREFIX1;
|
||||
|
||||
// This appears to depend on an implicit conversion from
|
||||
// unsigned opcode down to BYTE, to deliberately lose data and have
|
||||
// opcode >= 0x100 wrap around to 0.
|
||||
m_pOutputBuffer[offset++] = (opcode & 0xFF);
|
||||
}
|
||||
|
||||
assert(pInstr->m_opcode < _countof(s_OpCodeFlags));
|
||||
BYTE flags = s_OpCodeFlags[pInstr->m_opcode];
|
||||
switch (flags)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
*(UNALIGNED INT8 *)&(pIL[offset]) = pInstr->m_Arg8;
|
||||
break;
|
||||
case 2:
|
||||
*(UNALIGNED INT16 *)&(pIL[offset]) = pInstr->m_Arg16;
|
||||
break;
|
||||
case 4:
|
||||
*(UNALIGNED INT32 *)&(pIL[offset]) = pInstr->m_Arg32;
|
||||
break;
|
||||
case 8:
|
||||
*(UNALIGNED INT64 *)&(pIL[offset]) = pInstr->m_Arg64;
|
||||
break;
|
||||
case 1 | OPCODEFLAGS_BranchTarget:
|
||||
fBranch = true;
|
||||
break;
|
||||
case 4 | OPCODEFLAGS_BranchTarget:
|
||||
fBranch = true;
|
||||
break;
|
||||
case 0 | OPCODEFLAGS_Switch:
|
||||
*(UNALIGNED INT32 *)&(pIL[offset]) = pInstr->m_Arg32;
|
||||
offset += sizeof(INT32);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
offset += (flags & OPCODEFLAGS_SizeMask);
|
||||
}
|
||||
m_IL.m_offset = offset;
|
||||
|
||||
if (fBranch)
|
||||
{
|
||||
bool fTryAgain = false;
|
||||
unsigned switchBase = 0;
|
||||
|
||||
// Go over all control flow instructions and resolve the targets
|
||||
for (ILInstr * pInstr = m_IL.m_pNext; pInstr != &m_IL; pInstr = pInstr->m_pNext)
|
||||
{
|
||||
unsigned opcode = pInstr->m_opcode;
|
||||
|
||||
if (pInstr->m_opcode == CEE_SWITCH)
|
||||
{
|
||||
switchBase = pInstr->m_offset + 1 + sizeof(INT32) * (pInstr->m_Arg32 + 1);
|
||||
continue;
|
||||
}
|
||||
if (opcode == CEE_SWITCH_ARG)
|
||||
{
|
||||
// Switch args are special
|
||||
*(UNALIGNED INT32 *)&(pIL[pInstr->m_offset]) = pInstr->m_pTarget->m_offset - switchBase;
|
||||
continue;
|
||||
}
|
||||
|
||||
BYTE flags = s_OpCodeFlags[pInstr->m_opcode];
|
||||
|
||||
if (flags & OPCODEFLAGS_BranchTarget)
|
||||
{
|
||||
int delta = pInstr->m_pTarget->m_offset - pInstr->m_pNext->m_offset;
|
||||
|
||||
switch (flags)
|
||||
{
|
||||
case 1 | OPCODEFLAGS_BranchTarget:
|
||||
// Check if delta is too big to fit into an INT8.
|
||||
//
|
||||
// (see #pragma at top of file)
|
||||
if ((INT8)delta != delta)
|
||||
{
|
||||
if (opcode == CEE_LEAVE_S)
|
||||
{
|
||||
pInstr->m_opcode = CEE_LEAVE;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(opcode >= CEE_BR_S && opcode <= CEE_BLT_UN_S);
|
||||
pInstr->m_opcode = opcode - CEE_BR_S + CEE_BR;
|
||||
assert(pInstr->m_opcode >= CEE_BR && pInstr->m_opcode <= CEE_BLT_UN);
|
||||
}
|
||||
fTryAgain = true;
|
||||
continue;
|
||||
}
|
||||
*(UNALIGNED INT8 *)&(pIL[pInstr->m_pNext->m_offset - sizeof(INT8)]) = delta;
|
||||
break;
|
||||
case 4 | OPCODEFLAGS_BranchTarget:
|
||||
*(UNALIGNED INT32 *)&(pIL[pInstr->m_pNext->m_offset - sizeof(INT32)]) = delta;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do the whole thing again if we changed the size of some branch targets
|
||||
if (fTryAgain)
|
||||
goto again;
|
||||
}
|
||||
|
||||
unsigned codeSize = offset;
|
||||
unsigned totalSize;
|
||||
LPBYTE pBody = NULL;
|
||||
if (m_fGenerateTinyHeader)
|
||||
{
|
||||
// Make sure we can fit in a tiny header
|
||||
if (codeSize >= 64)
|
||||
return E_FAIL;
|
||||
|
||||
totalSize = sizeof(IMAGE_COR_ILMETHOD_TINY) + codeSize;
|
||||
pBody = AllocateILMemory(totalSize);
|
||||
IfNullRet(pBody);
|
||||
|
||||
BYTE * pCurrent = pBody;
|
||||
|
||||
// Here's the tiny header
|
||||
*pCurrent = (BYTE)(CorILMethod_TinyFormat | (codeSize << 2));
|
||||
pCurrent += sizeof(IMAGE_COR_ILMETHOD_TINY);
|
||||
|
||||
// And the body
|
||||
CopyMemory(pCurrent, m_pOutputBuffer, codeSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use FAT header
|
||||
|
||||
unsigned alignedCodeSize = (offset + 3) & ~3;
|
||||
|
||||
totalSize = sizeof(IMAGE_COR_ILMETHOD_FAT) + alignedCodeSize +
|
||||
(m_nEH ? (sizeof(IMAGE_COR_ILMETHOD_SECT_FAT) + sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT) * m_nEH) : 0);
|
||||
|
||||
pBody = AllocateILMemory(totalSize);
|
||||
IfNullRet(pBody);
|
||||
|
||||
BYTE * pCurrent = pBody;
|
||||
|
||||
IMAGE_COR_ILMETHOD_FAT *pHeader = (IMAGE_COR_ILMETHOD_FAT *)pCurrent;
|
||||
pHeader->Flags = m_flags | (m_nEH ? CorILMethod_MoreSects : 0) | CorILMethod_FatFormat;
|
||||
pHeader->Size = sizeof(IMAGE_COR_ILMETHOD_FAT) / sizeof(DWORD);
|
||||
pHeader->MaxStack = m_maxStack;
|
||||
pHeader->CodeSize = offset;
|
||||
pHeader->LocalVarSigTok = m_tkLocalVarSig;
|
||||
|
||||
pCurrent = (BYTE*)(pHeader + 1);
|
||||
|
||||
CopyMemory(pCurrent, m_pOutputBuffer, codeSize);
|
||||
pCurrent += alignedCodeSize;
|
||||
|
||||
if (m_nEH != 0)
|
||||
{
|
||||
IMAGE_COR_ILMETHOD_SECT_FAT *pEH = (IMAGE_COR_ILMETHOD_SECT_FAT *)pCurrent;
|
||||
pEH->Kind = CorILMethod_Sect_EHTable | CorILMethod_Sect_FatFormat;
|
||||
pEH->DataSize = (unsigned)(sizeof(IMAGE_COR_ILMETHOD_SECT_FAT) + sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT) * m_nEH);
|
||||
|
||||
pCurrent = (BYTE*)(pEH + 1);
|
||||
|
||||
for (unsigned iEH = 0; iEH < m_nEH; iEH++)
|
||||
{
|
||||
EHClause *pSrc = &(m_pEH[iEH]);
|
||||
IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT * pDst = (IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT *)pCurrent;
|
||||
|
||||
pDst->Flags = pSrc->m_Flags;
|
||||
pDst->TryOffset = pSrc->m_pTryBegin->m_offset;
|
||||
pDst->TryLength = pSrc->m_pTryEnd->m_offset - pSrc->m_pTryBegin->m_offset;
|
||||
pDst->HandlerOffset = pSrc->m_pHandlerBegin->m_offset;
|
||||
pDst->HandlerLength = pSrc->m_pHandlerEnd->m_pNext->m_offset - pSrc->m_pHandlerBegin->m_offset;
|
||||
if ((pSrc->m_Flags & COR_ILEXCEPTION_CLAUSE_FILTER) == 0)
|
||||
pDst->ClassToken = pSrc->m_ClassToken;
|
||||
else
|
||||
pDst->FilterOffset = pSrc->m_pFilter->m_offset;
|
||||
|
||||
pCurrent = (BYTE*)(pDst + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IfFailRet(SetILFunctionBody(totalSize, pBody));
|
||||
DeallocateILMemory(pBody);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT SetILFunctionBody(unsigned size, LPBYTE pBody)
|
||||
{
|
||||
if (m_pICorProfilerFunctionControl != NULL)
|
||||
{
|
||||
// We're supplying IL for a rejit, so use the rejit mechanism
|
||||
IfFailRet(m_pICorProfilerFunctionControl->SetILFunctionBody(size, pBody));
|
||||
}
|
||||
else
|
||||
{
|
||||
// "classic-style" instrumentation on first JIT, so use old mechanism
|
||||
IfFailRet(m_pICorProfilerInfo->SetILFunctionBody(m_moduleId, m_tkMethod, pBody));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
LPBYTE AllocateILMemory(unsigned size)
|
||||
{
|
||||
if (m_pICorProfilerFunctionControl != NULL)
|
||||
{
|
||||
// We're supplying IL for a rejit, so we can just allocate from
|
||||
// the heap
|
||||
return new BYTE[size];
|
||||
}
|
||||
|
||||
// Else, this is "classic-style" instrumentation on first JIT, and
|
||||
// need to use the CLR's IL allocator
|
||||
|
||||
if (FAILED(m_pICorProfilerInfo->GetILFunctionBodyAllocator(m_moduleId, &m_pIMethodMalloc)))
|
||||
return NULL;
|
||||
|
||||
return (LPBYTE)m_pIMethodMalloc->Alloc(size);
|
||||
}
|
||||
|
||||
void DeallocateILMemory(LPBYTE pBody)
|
||||
{
|
||||
if (m_pICorProfilerFunctionControl == NULL)
|
||||
{
|
||||
// Old-style instrumentation does not provide a way to free up bytes
|
||||
return;
|
||||
}
|
||||
|
||||
delete[] pBody;
|
||||
}
|
||||
};
|
||||
|
||||
HRESULT AddProbe(
|
||||
ILRewriter * pilr,
|
||||
FunctionID functionId,
|
||||
UINT_PTR methodAddress,
|
||||
ULONG32 methodSignature,
|
||||
ILInstr * pInsertProbeBeforeThisInstr)
|
||||
{
|
||||
ILInstr * pNewInstr = nullptr;
|
||||
|
||||
constexpr auto CEE_LDC_I = sizeof(size_t) == 8 ? CEE_LDC_I8 : sizeof(size_t) == 4 ? CEE_LDC_I4 : throw std::logic_error("size_t must be defined as 8 or 4");
|
||||
|
||||
pNewInstr = pilr->NewILInstr();
|
||||
pNewInstr->m_opcode = CEE_LDC_I;
|
||||
pNewInstr->m_Arg64 = functionId;
|
||||
pilr->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
|
||||
|
||||
pNewInstr = pilr->NewILInstr();
|
||||
pNewInstr->m_opcode = CEE_LDC_I;
|
||||
pNewInstr->m_Arg64 = methodAddress;
|
||||
pilr->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
|
||||
|
||||
pNewInstr = pilr->NewILInstr();
|
||||
pNewInstr->m_opcode = CEE_CALLI;
|
||||
pNewInstr->m_Arg32 = methodSignature;
|
||||
pilr->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT AddEnterProbe(
|
||||
ILRewriter * pilr,
|
||||
FunctionID functionId,
|
||||
UINT_PTR methodAddress,
|
||||
ULONG32 methodSignature)
|
||||
{
|
||||
ILInstr * pFirstOriginalInstr = pilr->GetILList()->m_pNext;
|
||||
|
||||
return AddProbe(pilr, functionId, methodAddress, methodSignature, pFirstOriginalInstr);
|
||||
}
|
||||
|
||||
|
||||
HRESULT AddExitProbe(
|
||||
ILRewriter * pilr,
|
||||
FunctionID functionId,
|
||||
UINT_PTR methodAddress,
|
||||
ULONG32 methodSignature)
|
||||
{
|
||||
HRESULT hr;
|
||||
BOOL fAtLeastOneProbeAdded = FALSE;
|
||||
|
||||
// Find all RETs, and insert a call to the exit probe before each one.
|
||||
for (ILInstr * pInstr = pilr->GetILList()->m_pNext; pInstr != pilr->GetILList(); pInstr = pInstr->m_pNext)
|
||||
{
|
||||
switch (pInstr->m_opcode)
|
||||
{
|
||||
case CEE_RET:
|
||||
{
|
||||
// We want any branches or leaves that targeted the RET instruction to
|
||||
// actually target the epilog instructions we're adding. So turn the "RET"
|
||||
// into ["NOP", "RET"], and THEN add the epilog between the NOP & RET. That
|
||||
// ensures that any branches that went to the RET will now go to the NOP and
|
||||
// then execute our epilog.
|
||||
|
||||
// NOTE: The NOP is not strictly required, but is a simplification of the implementation.
|
||||
// RET->NOP
|
||||
pInstr->m_opcode = CEE_NOP;
|
||||
|
||||
// Add the new RET after
|
||||
ILInstr * pNewRet = pilr->NewILInstr();
|
||||
pNewRet->m_opcode = CEE_RET;
|
||||
pilr->InsertAfter(pInstr, pNewRet);
|
||||
|
||||
// Add now insert the epilog before the new RET
|
||||
hr = AddProbe(pilr, functionId, methodAddress, methodSignature, pNewRet);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
fAtLeastOneProbeAdded = TRUE;
|
||||
|
||||
// Advance pInstr after all this gunk so the for loop continues properly
|
||||
pInstr = pNewRet;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fAtLeastOneProbeAdded)
|
||||
return E_FAIL;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
// Uses the general-purpose ILRewriter class to import original
|
||||
// IL, rewrite it, and send the result to the CLR
|
||||
HRESULT RewriteIL(
|
||||
ICorProfilerInfo * pICorProfilerInfo,
|
||||
ICorProfilerFunctionControl * pICorProfilerFunctionControl,
|
||||
ModuleID moduleID,
|
||||
mdMethodDef methodDef,
|
||||
FunctionID functionId,
|
||||
UINT_PTR enterMethodAddress,
|
||||
UINT_PTR exitMethodAddress,
|
||||
|
||||
ULONG32 methodSignature)
|
||||
{
|
||||
ILRewriter rewriter(pICorProfilerInfo, pICorProfilerFunctionControl, moduleID, methodDef);
|
||||
|
||||
IfFailRet(rewriter.Import());
|
||||
{
|
||||
// Adds enter/exit probes
|
||||
IfFailRet(AddEnterProbe(&rewriter, functionId, enterMethodAddress, methodSignature));
|
||||
IfFailRet(AddExitProbe(&rewriter, functionId, exitMethodAddress, methodSignature));
|
||||
}
|
||||
IfFailRet(rewriter.Export());
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
HRESULT RewriteIL(
|
||||
ICorProfilerInfo *pICorProfilerInfo,
|
||||
ICorProfilerFunctionControl *pICorProfilerFunctionControl,
|
||||
ModuleID moduleID,
|
||||
mdMethodDef methodDef,
|
||||
FunctionID functionId,
|
||||
UINT_PTR enterMethodAddress,
|
||||
UINT_PTR exitMethodAddress,
|
||||
ULONG32 methodSignature);
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "JITCompilationFinishedEvent.h"
|
||||
|
||||
JITCompilationFinishedEvent::JITCompilationFinishedEvent(int64_t methodId, int64_t moduleId, int64_t methodStartAddress, int32_t methodSize, int32_t methodToken, int32_t methodFlags, portable_wide_string methodNamespace, portable_wide_string methodName, portable_wide_string methodSignature, int16_t clrInstanceId, int64_t rejitId) : EtwEvent(), methodId(methodId), moduleId(moduleId), methodStartAddress(methodStartAddress), methodSize(methodSize), methodToken(methodToken), methodFlags(methodFlags), methodNamespace(std::move(methodNamespace)), methodName(std::move(methodName)), methodSignature(std::move(methodSignature)), clrInstanceId(clrInstanceId), rejitId(rejitId)
|
||||
{
|
||||
}
|
||||
|
||||
void JITCompilationFinishedEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
|
||||
t.Write(this->methodId);
|
||||
t.Write(this->moduleId);
|
||||
t.Write(this->methodStartAddress);
|
||||
t.Write(this->methodSize);
|
||||
t.Write(this->methodToken);
|
||||
t.Write(this->methodFlags);
|
||||
t.Write(this->methodNamespace);
|
||||
t.Write(this->methodName);
|
||||
t.Write(this->methodSignature);
|
||||
t.Write(this->clrInstanceId);
|
||||
t.Write(this->rejitId);
|
||||
}
|
||||
|
||||
int32_t JITCompilationFinishedEvent::GetEventId()
|
||||
{
|
||||
return 5;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// 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 "EtwEvent.h"
|
||||
|
||||
class JITCompilationFinishedEvent : public EtwEvent
|
||||
{
|
||||
friend class EtwLogger;
|
||||
|
||||
public:
|
||||
JITCompilationFinishedEvent(int64_t methodId, int64_t moduleId, int64_t methodStartAddress, int32_t methodSize, int32_t methodToken, int32_t methodFlags, portable_wide_string methodNamespace, portable_wide_string methodName, portable_wide_string methodSignature, int16_t clrInstanceId, int64_t rejitId);
|
||||
void Serialize(Serializer &t) override;
|
||||
int32_t GetEventId() override;
|
||||
|
||||
private:
|
||||
int64_t methodId;
|
||||
int64_t moduleId;
|
||||
int64_t methodStartAddress;
|
||||
int32_t methodSize;
|
||||
int32_t methodToken;
|
||||
int32_t methodFlags;
|
||||
portable_wide_string methodNamespace;
|
||||
portable_wide_string methodName;
|
||||
portable_wide_string methodSignature;
|
||||
int16_t clrInstanceId;
|
||||
int64_t rejitId;
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "JITCompilationStartedEvent.h"
|
||||
|
||||
JITCompilationStartedEvent::JITCompilationStartedEvent(int64_t methodId, int64_t moduleId, int32_t methodToken, int32_t methodILSize, portable_wide_string methodNamespace, portable_wide_string methodName, portable_wide_string methodSignature, int16_t clrInstanceId) : EtwEvent(), methodId(methodId), moduleId(moduleId), methodToken(methodToken), methodILSize(methodILSize), methodNamespace(std::move(methodNamespace)), methodName(std::move(methodName)), methodSignature(std::move(methodSignature)), clrInstanceId(clrInstanceId)
|
||||
{
|
||||
}
|
||||
|
||||
void JITCompilationStartedEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
|
||||
t.Write(this->methodId);
|
||||
t.Write(this->moduleId);
|
||||
t.Write(this->methodToken);
|
||||
t.Write(this->methodILSize);
|
||||
t.Write(this->methodNamespace);
|
||||
t.Write(this->methodName);
|
||||
t.Write(this->methodSignature);
|
||||
t.Write(this->clrInstanceId);
|
||||
}
|
||||
|
||||
int32_t JITCompilationStartedEvent::GetEventId()
|
||||
{
|
||||
return 6;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// 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 "EtwEvent.h"
|
||||
|
||||
class JITCompilationStartedEvent : public EtwEvent
|
||||
{
|
||||
public:
|
||||
JITCompilationStartedEvent(int64_t methodId, int64_t moduleId, int32_t methodToken, int32_t methodILSize, portable_wide_string methodNamespace, portable_wide_string methodName, portable_wide_string methodSignature, int16_t clrInstanceId);
|
||||
void Serialize(Serializer &t) override;
|
||||
int32_t GetEventId() override;
|
||||
|
||||
private:
|
||||
int64_t methodId;
|
||||
int64_t moduleId;
|
||||
int32_t methodToken;
|
||||
int32_t methodILSize;
|
||||
portable_wide_string methodNamespace;
|
||||
portable_wide_string methodName;
|
||||
portable_wide_string methodSignature;
|
||||
int16_t clrInstanceId;
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
// 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 "MethodEnterEvent.h"
|
||||
|
||||
MethodEnterEvent::MethodEnterEvent(intptr_t functionId, std::vector<uintptr_t> *functionIds) : functionId(functionId), functionIds(functionIds)
|
||||
{
|
||||
}
|
||||
|
||||
void MethodEnterEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
t.Write(this->functionId);
|
||||
t.Write(this->functionIds->size());
|
||||
t.Write(*this->functionIds);
|
||||
}
|
||||
|
||||
int32_t MethodEnterEvent::GetEventId()
|
||||
{
|
||||
return 13;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// 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 "EtwEvent.h"
|
||||
|
||||
class MethodEnterEvent : public EtwEvent
|
||||
{
|
||||
public:
|
||||
MethodEnterEvent(intptr_t functionId, std::vector<uintptr_t> * functionIds);
|
||||
void Serialize(Serializer &t) override;
|
||||
int32_t GetEventId() override;
|
||||
|
||||
private:
|
||||
intptr_t functionId;
|
||||
std::vector<uintptr_t> *functionIds;
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "MethodILToNativeMapEvent.h"
|
||||
|
||||
MethodILToNativeMapEvent::MethodILToNativeMapEvent(int64_t methodId, int64_t reJitId, int8_t methodExtent, int16_t countOfMapEntries, std::vector<uint32_t> ilOffsets, std::vector<uint32_t> nativeOffsets, int16_t clrInstanceId) : EtwEvent(), methodId(methodId), reJitId(reJitId), methodExtent(methodExtent), countOfMapEntries(countOfMapEntries), ilOffsets(std::move(ilOffsets)), nativeOffsets(std::move(nativeOffsets)), clrInstanceId(clrInstanceId)
|
||||
{
|
||||
}
|
||||
|
||||
void MethodILToNativeMapEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
|
||||
t.Write(this->methodId);
|
||||
t.Write(this->reJitId);
|
||||
t.Write(this->methodExtent);
|
||||
t.Write(this->countOfMapEntries);
|
||||
t.Write(this->ilOffsets);
|
||||
t.Write(this->nativeOffsets);
|
||||
t.Write(this->clrInstanceId);
|
||||
}
|
||||
|
||||
int32_t MethodILToNativeMapEvent::GetEventId()
|
||||
{
|
||||
return 7;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// 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 "EtwEvent.h"
|
||||
|
||||
class MethodILToNativeMapEvent : public EtwEvent
|
||||
{
|
||||
friend class EtwLogger;
|
||||
|
||||
public:
|
||||
MethodILToNativeMapEvent(int64_t methodId, int64_t reJitId, int8_t methodExtent, int16_t countOfMapEntries, std::vector<uint32_t> ilOffsets, std::vector<uint32_t> nativeOffsets, int16_t clrInstanceId);
|
||||
void Serialize(Serializer &t) override;
|
||||
int32_t GetEventId() override;
|
||||
|
||||
private:
|
||||
int64_t methodId;
|
||||
int64_t reJitId;
|
||||
int8_t methodExtent;
|
||||
int16_t countOfMapEntries;
|
||||
std::vector<uint32_t> ilOffsets;
|
||||
std::vector<uint32_t> nativeOffsets;
|
||||
int16_t clrInstanceId;
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "MethodUnloadEvent.h"
|
||||
|
||||
MethodUnloadEvent::MethodUnloadEvent(int64_t methodId, int64_t moduleId, int64_t methodStartAddress, int32_t methodSize, int32_t methodToken, int32_t methodFlags, portable_wide_string methodNamespace, portable_wide_string methodName, portable_wide_string methodSignature, int16_t clrInstanceId, int64_t rejitId) : EtwEvent(), methodId(methodId), moduleId(moduleId), methodStartAddress(methodStartAddress), methodSize(methodSize), methodToken(methodToken), methodFlags(methodFlags), methodNamespace(std::move(methodNamespace)), methodName(std::move(methodName)), methodSignature(std::move(methodSignature)), clrInstanceId(clrInstanceId), rejitId(rejitId)
|
||||
{
|
||||
}
|
||||
|
||||
void MethodUnloadEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
|
||||
t.Write(this->methodId);
|
||||
t.Write(this->moduleId);
|
||||
t.Write(this->methodStartAddress);
|
||||
t.Write(this->methodSize);
|
||||
t.Write(this->methodToken);
|
||||
t.Write(this->methodFlags);
|
||||
t.Write(this->methodNamespace);
|
||||
t.Write(this->methodName);
|
||||
t.Write(this->methodSignature);
|
||||
t.Write(this->clrInstanceId);
|
||||
t.Write(this->rejitId);
|
||||
}
|
||||
|
||||
int32_t MethodUnloadEvent::GetEventId()
|
||||
{
|
||||
return 8;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// 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 "EtwEvent.h"
|
||||
|
||||
class MethodUnloadEvent : public EtwEvent
|
||||
{
|
||||
public:
|
||||
MethodUnloadEvent(int64_t methodId, int64_t moduleId, int64_t methodStartAddress, int32_t methodSize, int32_t methodToken, int32_t methodFlags, portable_wide_string methodNamespace, portable_wide_string methodName, portable_wide_string methodSignature, int16_t clrInstanceId, int64_t rejitId);
|
||||
void Serialize(Serializer &t) override;
|
||||
int32_t GetEventId() override;
|
||||
|
||||
private:
|
||||
int64_t methodId;
|
||||
int64_t moduleId;
|
||||
int64_t methodStartAddress;
|
||||
int32_t methodSize;
|
||||
int32_t methodToken;
|
||||
int32_t methodFlags;
|
||||
portable_wide_string methodNamespace;
|
||||
portable_wide_string methodName;
|
||||
portable_wide_string methodSignature;
|
||||
int16_t clrInstanceId;
|
||||
int64_t rejitId;
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "ModuleLoadEvent.h"
|
||||
|
||||
ModuleLoadEvent::ModuleLoadEvent(int64_t moduleId, int64_t assemblyId, int32_t moduleFlags, portable_wide_string moduleILPath, portable_wide_string moduleNativePath, int16_t clrInstanceId, GUID managedPdbSignature, int32_t managedPdbAge, portable_wide_string managedPdbBuildPath, GUID nativePdbSignature, int32_t nativePdbAge, portable_wide_string nativePdbBuildPath) : EtwEvent(), moduleId(moduleId), assemblyId(assemblyId), moduleFlags(moduleFlags), moduleILPath(std::move(moduleILPath)), moduleNativePath(std::move(moduleNativePath)), clrInstanceId(clrInstanceId), managedPdbSignature(managedPdbSignature), managedPdbAge(managedPdbAge), managedPdbBuildPath(std::move(managedPdbBuildPath)), nativePdbSignature(nativePdbSignature), nativePdbAge(nativePdbAge), nativePdbBuildPath(std::move(nativePdbBuildPath))
|
||||
{
|
||||
}
|
||||
|
||||
void ModuleLoadEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
|
||||
t.Write(this->moduleId);
|
||||
t.Write(this->assemblyId);
|
||||
t.Write(this->moduleFlags);
|
||||
t.Write(0); // Reserved1
|
||||
t.Write(this->moduleILPath);
|
||||
t.Write(this->moduleNativePath);
|
||||
t.Write(this->clrInstanceId);
|
||||
t.Write(this->managedPdbSignature);
|
||||
t.Write(this->managedPdbAge);
|
||||
t.Write(this->managedPdbBuildPath);
|
||||
t.Write(this->nativePdbSignature);
|
||||
t.Write(this->nativePdbAge);
|
||||
t.Write(this->nativePdbBuildPath);
|
||||
}
|
||||
|
||||
int32_t ModuleLoadEvent::GetEventId()
|
||||
{
|
||||
return 9;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// 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 "EtwEvent.h"
|
||||
|
||||
class ModuleLoadEvent : public EtwEvent
|
||||
{
|
||||
friend class EtwLogger;
|
||||
|
||||
public:
|
||||
ModuleLoadEvent(int64_t moduleId, int64_t assemblyId, int32_t moduleFlags, portable_wide_string moduleILPath, portable_wide_string moduleNativePath, int16_t clrInstanceId, GUID managedPdbSignature, int32_t managedPdbAge, portable_wide_string managedPdbBuildPath, GUID nativePdbSignature, int32_t nativePdbAge, portable_wide_string nativePdbBuildPath);
|
||||
void Serialize(Serializer &t) override;
|
||||
int32_t GetEventId() override;
|
||||
|
||||
private:
|
||||
int64_t moduleId;
|
||||
int64_t assemblyId;
|
||||
int32_t moduleFlags;
|
||||
portable_wide_string moduleILPath;
|
||||
portable_wide_string moduleNativePath;
|
||||
int16_t clrInstanceId;
|
||||
GUID managedPdbSignature;
|
||||
int32_t managedPdbAge;
|
||||
portable_wide_string managedPdbBuildPath;
|
||||
GUID nativePdbSignature;
|
||||
int32_t nativePdbAge;
|
||||
portable_wide_string nativePdbBuildPath;
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "ModuleUnloadEvent.h"
|
||||
|
||||
ModuleUnloadEvent::ModuleUnloadEvent(int64_t moduleId, int64_t assemblyId, int32_t moduleFlags, portable_wide_string moduleILPath, portable_wide_string moduleNativePath, int16_t clrInstanceId, GUID managedPdbSignature, int32_t managedPdbAge, portable_wide_string managedPdbBuildPath, GUID nativePdbSignature, int32_t nativePdbAge, portable_wide_string nativePdbBuildPath) : EtwEvent(), moduleId(moduleId), assemblyId(assemblyId), moduleFlags(moduleFlags), moduleILPath(std::move(moduleILPath)), moduleNativePath(std::move(moduleNativePath)), clrInstanceId(clrInstanceId), managedPdbSignature(managedPdbSignature), managedPdbAge(managedPdbAge), managedPdbBuildPath(std::move(managedPdbBuildPath)), nativePdbSignature(nativePdbSignature), nativePdbAge(nativePdbAge), nativePdbBuildPath(std::move(nativePdbBuildPath))
|
||||
{
|
||||
}
|
||||
|
||||
void ModuleUnloadEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
|
||||
t.Write(this->moduleId);
|
||||
t.Write(this->assemblyId);
|
||||
t.Write(this->moduleFlags);
|
||||
t.Write(0); // Reserved1
|
||||
t.Write(this->moduleILPath);
|
||||
t.Write(this->moduleNativePath);
|
||||
t.Write(this->clrInstanceId);
|
||||
t.Write(this->managedPdbSignature);
|
||||
t.Write(this->managedPdbAge);
|
||||
t.Write(this->managedPdbBuildPath);
|
||||
t.Write(this->nativePdbSignature);
|
||||
t.Write(this->nativePdbAge);
|
||||
t.Write(this->nativePdbBuildPath);
|
||||
}
|
||||
|
||||
int32_t ModuleUnloadEvent::GetEventId()
|
||||
{
|
||||
return 10;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// 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 "EtwEvent.h"
|
||||
|
||||
class ModuleUnloadEvent : public EtwEvent
|
||||
{
|
||||
public:
|
||||
ModuleUnloadEvent(int64_t moduleId, int64_t assemblyId, int32_t moduleFlags, portable_wide_string moduleILPath, portable_wide_string moduleNativePath, int16_t clrInstanceId, GUID managedPdbSignature, int32_t managedPdbAge, portable_wide_string managedPdbBuildPath, GUID nativePdbSignature, int32_t nativePdbAge, portable_wide_string nativePdbBuildPath);
|
||||
void Serialize(Serializer &t) override;
|
||||
int32_t GetEventId() override;
|
||||
|
||||
private:
|
||||
int64_t moduleId;
|
||||
int64_t assemblyId;
|
||||
int32_t moduleFlags;
|
||||
portable_wide_string moduleILPath;
|
||||
portable_wide_string moduleNativePath;
|
||||
int16_t clrInstanceId;
|
||||
GUID managedPdbSignature;
|
||||
int32_t managedPdbAge;
|
||||
portable_wide_string managedPdbBuildPath;
|
||||
GUID nativePdbSignature;
|
||||
int32_t nativePdbAge;
|
||||
portable_wide_string nativePdbBuildPath;
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
|
||||
#ifndef _WINDOWS
|
||||
using portable_wide_string = std::u16string;
|
||||
using portable_wide_char = char16_t;
|
||||
#define W(str) u##str
|
||||
#else
|
||||
using portable_wide_string = std::wstring;
|
||||
using portable_wide_char = wchar_t;
|
||||
#define W(str) L##str
|
||||
#endif
|
||||
|
||||
#define ZEROSTRING W('0')
|
||||
#define EMPTYSTRING W("")
|
||||
#define UNKNOWNSTRING W("Unknown")
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,123 @@
|
|||
// 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 <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include "RundownListNode.h"
|
||||
|
||||
template <class T>
|
||||
class RundownList
|
||||
{
|
||||
public:
|
||||
RundownList() : head(new RundownListNode<T>()), isInGCMode(false)
|
||||
{
|
||||
}
|
||||
|
||||
~RundownList()
|
||||
{
|
||||
while (this->head != nullptr)
|
||||
{
|
||||
this->Remove(this->head);
|
||||
}
|
||||
}
|
||||
|
||||
// Thread Safety Concerns : (1) modifying shared instance variable (this->head), protected by this-headNodeMutex
|
||||
RundownListNodeId<T> Add(std::unique_ptr<T> data)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->headNodeMutex); // guarantees only one adder
|
||||
this->head->prev = new RundownListNode<T>(std::move(data), this->head);
|
||||
this->head = this->head->prev;
|
||||
return this->head;
|
||||
}
|
||||
|
||||
// Thread Safety Concerns : (1) multiple ::Remove calls are serialized via this->removeMutex
|
||||
// (2) modifying shared instance variable (this->head), protected by this-headNodeMutex
|
||||
// (3) deleting incoming parameter (node), protected by nullptr check inside a lock (this->removeMutex)
|
||||
// (4) modifying shared instance variable (this->collectables), **NOT** protected, however atomic variable this->isInGCMode arbitrates alternating exclusive access
|
||||
void Remove(RundownListNodeId<T> node)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->removeMutex); // guarantees only one remover
|
||||
|
||||
if (node == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->headNodeMutex.lock();
|
||||
if (node->prev == nullptr)
|
||||
{
|
||||
this->head = node->next;
|
||||
|
||||
if (this->head != nullptr)
|
||||
{
|
||||
this->head->prev = nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the unlock could technically be here since we're not modifying this->head anymore, but ...
|
||||
node->prev->next = node->next;
|
||||
if (node->next != nullptr)
|
||||
{
|
||||
node->next->prev = node->prev;
|
||||
}
|
||||
}
|
||||
this->headNodeMutex.unlock();
|
||||
|
||||
// If someone started iterating, we need to make sure that this node isn't deleted just yet
|
||||
// Instead we put it in a GC list of future collectables, and it's the job of the iterator
|
||||
// to do the so-called "collection".
|
||||
if (this->isInGCMode.load())
|
||||
{
|
||||
this->collectables.push_back(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete node;
|
||||
}
|
||||
}
|
||||
|
||||
void Iterate(void(callback)(T *))
|
||||
{
|
||||
// Well-known state: There are precisely 0 or 1 iterations happening right now.
|
||||
|
||||
std::lock_guard<std::mutex> lock(this->iterationMutex); // guarantees only one iterator
|
||||
|
||||
// Well-known state: There are precisely 0 iterations happening right now. This has the following properties:
|
||||
// (1) this->collectables.length() is equal to 0.
|
||||
// (2) this->isIncGCMode is equal to false, consequently any calls to ::Remove immediately delete the node and free up memory.
|
||||
// (3) ::Add is happily adding items to the list, there is a lock (headNodeMutex), but it is a constant-time lock (i.e. fixed instructions in between the lock) with no contention UNLESS ::Remove is also removing the HEAD node at the same time.
|
||||
|
||||
this->isInGCMode.store(true); // any ::Remove() calls now add to a GC list instead of being immediately deleted
|
||||
|
||||
this->headNodeMutex.lock(); // acquire a conistent start point
|
||||
auto start = this->head;
|
||||
this->headNodeMutex.unlock();
|
||||
|
||||
// lock-free iteration
|
||||
while (start != nullptr && start->next != nullptr)
|
||||
{
|
||||
callback(start->data.get());
|
||||
start = start->next;
|
||||
}
|
||||
|
||||
this->isInGCMode.store(false); // any ::Remove() calls now delete nodes immediately, and GC list is not being modified anymore
|
||||
|
||||
// lock-free iteration to empty out the GC list
|
||||
for (auto &e : this->collectables)
|
||||
{
|
||||
delete e;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<RundownListNode<T> *> collectables;
|
||||
RundownListNode<T> *head;
|
||||
std::atomic_bool isInGCMode;
|
||||
std::mutex headNodeMutex;
|
||||
std::mutex removeMutex;
|
||||
std::mutex iterationMutex;
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
// 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
|
||||
|
||||
template <class T>
|
||||
class RundownList;
|
||||
|
||||
template <class T>
|
||||
class RundownListNode
|
||||
{
|
||||
template <typename>
|
||||
friend class RundownList;
|
||||
RundownListNode(std::unique_ptr<T> data, RundownListNode<T> *next) : data(std::move(data)), next(next), prev(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
RundownListNode() : next(nullptr), prev(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<T> data;
|
||||
RundownListNode<T> *next;
|
||||
RundownListNode<T> *prev;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using RundownListNodeId = RundownListNode<T> *;
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "RuntimeInformationEvent.h"
|
||||
|
||||
RuntimeInformationEvent::RuntimeInformationEvent(int16_t clrInstanceId, int16_t sku, int16_t vmMajorVersion, int16_t vmMinorVersion, int16_t vmBuildNumber, int16_t vmQfeNumber) : EtwEvent(), clrInstanceId(clrInstanceId), sku(sku), vmMajorVersion(vmMajorVersion), vmMinorVersion(vmMinorVersion), vmBuildNumber(vmBuildNumber), vmQfeNumber(vmQfeNumber)
|
||||
{
|
||||
}
|
||||
|
||||
void RuntimeInformationEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
|
||||
t.Write(this->clrInstanceId);
|
||||
t.Write(this->sku);
|
||||
t.Write(4);
|
||||
t.Write(0);
|
||||
t.Write(0);
|
||||
t.Write(0);
|
||||
t.Write(this->vmMajorVersion);
|
||||
t.Write(this->vmMinorVersion);
|
||||
t.Write(this->vmBuildNumber);
|
||||
t.Write(this->vmQfeNumber);
|
||||
t.WriteNull();
|
||||
t.WriteNull();
|
||||
t.WriteNull();
|
||||
t.WriteNull();
|
||||
t.WriteNull();
|
||||
}
|
||||
|
||||
int32_t RuntimeInformationEvent::GetEventId()
|
||||
{
|
||||
return 11;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// 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 "EtwEvent.h"
|
||||
|
||||
class RuntimeInformationEvent : public EtwEvent
|
||||
{
|
||||
public:
|
||||
RuntimeInformationEvent(int16_t clrInstanceId, int16_t sku, int16_t vmMajorVersion, int16_t vmMinorVersion, int16_t vmBuildNumber, int16_t vmQfeNumber);
|
||||
void Serialize(Serializer &t) override;
|
||||
int32_t GetEventId() override;
|
||||
|
||||
private:
|
||||
int16_t clrInstanceId;
|
||||
int16_t sku;
|
||||
int16_t vmMajorVersion;
|
||||
int16_t vmMinorVersion;
|
||||
int16_t vmBuildNumber;
|
||||
int16_t vmQfeNumber;
|
||||
};
|
|
@ -0,0 +1,127 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "SQLiteEventLogger.h"
|
||||
#include "SQLiteSerializer.h"
|
||||
#include "HeartBeatEvent.h"
|
||||
|
||||
template <class T, class... Types>
|
||||
std::unique_ptr<T> LogEvent(Types &&... Args)
|
||||
{
|
||||
return std::unique_ptr<T>(new T(std::forward<Types>(Args)...));
|
||||
}
|
||||
|
||||
template <int32_t I>
|
||||
void PrepareSql(sqlite3 *db, const char *sql, std::unordered_map<int32_t, sqlite3_stmt *> &map)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
sqlite3_prepare(db, sql, -1, &stmt, nullptr);
|
||||
map[I] = stmt;
|
||||
}
|
||||
|
||||
void CreateSql(sqlite3 *db, const char *sql)
|
||||
{
|
||||
sqlite3_exec(db, sql, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
SQLiteEventLogger::SQLiteEventLogger(sqlite3 *db, std::shared_ptr<ThreadSafeQueue<std::unique_ptr<EtwEvent>>> eventQueue, const std::string &bPerfLogsPath) : db(db), eventQueue(std::move(eventQueue))
|
||||
{
|
||||
auto err = sqlite3_open(bPerfLogsPath.c_str(), &this->db);
|
||||
if (err != SQLITE_OK)
|
||||
{
|
||||
printf("Encountered SQLite error when opening %s file (check write permissions/directory path exists): %s", bPerfLogsPath.c_str(), sqlite3_errmsg(this->db));
|
||||
}
|
||||
|
||||
CreateSql(this->db, "CREATE TABLE `HeartBeatEvent` (`Timestamp` BIGINT, `ProcessId` INT, `ThreadId` INT);");
|
||||
CreateSql(this->db, "CREATE TABLE `ShutdownEvent` (`Timestamp` BIGINT, `ProcessId` INT, `ThreadId` INT, `ClrInstanceID` SMALLINT);");
|
||||
CreateSql(this->db, "CREATE TABLE `AppDomainLoadEvent` (`Timestamp` BIGINT, `ProcessId` INT, `ThreadId` INT, `AppDomainID` BIGINT, `AppDomainFlags` INT, `AppDomainName` TEXT, `AppDomainIndex` INT, `ClrInstanceID` SMALLINT);");
|
||||
CreateSql(this->db, "CREATE TABLE `AppDomainUnloadEvent` (`Timestamp` BIGINT, `ProcessId` INT, `ThreadId` INT, `AppDomainID` BIGINT, `AppDomainFlags` INT, `AppDomainName` TEXT, `AppDomainIndex` INT, `ClrInstanceID` SMALLINT);");
|
||||
CreateSql(this->db, "CREATE TABLE `AssemblyLoadEvent` (`Timestamp` BIGINT, `ProcessId` INT, `ThreadId` INT, `AssemblyID` BIGINT, `AppDomainID` BIGINT, `BindingID` BIGINT, `AssemblyFlags` INT, `FullyQualifiedAssemblyName` TEXT, `ClrInstanceID` SMALLINT);");
|
||||
CreateSql(this->db, "CREATE TABLE `AssemblyUnloadEvent` (`Timestamp` BIGINT, `ProcessId` INT, `ThreadId` INT, `AssemblyID` BIGINT, `AppDomainID` BIGINT, `BindingID` BIGINT, `AssemblyFlags` INT, `FullyQualifiedAssemblyName` TEXT, `ClrInstanceID` SMALLINT);");
|
||||
CreateSql(this->db, "CREATE TABLE `JITCompilationFinishedEvent` (`Timestamp` BIGINT, `ProcessId` INT, `ThreadId` INT, `MethodID` BIGINT, `ModuleID` BIGINT, `MethodStartAddress` BIGINT, `MethodSize` INT, `MethodToken` INT, `MethodFlags` INT, `MethodNamespace` TEXT, `MethodName` TEXT, `MethodSignature` TEXT, `ClrInstanceID` SMALLINT, `ReJITID` BIGINT);");
|
||||
CreateSql(this->db, "CREATE TABLE `JITCompilationStartedEvent` (`Timestamp` BIGINT, `ProcessId` INT, `ThreadId` INT, `MethodID` BIGINT, `ModuleID` BIGINT, `MethodToken` INT, `MethodILSize` INT, `MethodNamespace` TEXT, `MethodName` TEXT, `MethodSignature` TEXT, `ClrInstanceID` SMALLINT);");
|
||||
CreateSql(this->db, "CREATE TABLE `MethodILToNativeMapEvent` (`Timestamp` BIGINT, `ProcessId` INT, `ThreadId` INT, `MethodID` BIGINT, `ReJITID` BIGINT, `MethodExtent` TINYINT, `CountOfMapEntries` SMALLINT, `ILOffsets` BLOB, `NativeOffsets` BLOB, `ClrInstanceID` SMALLINT);");
|
||||
CreateSql(this->db, "CREATE TABLE `MethodUnloadEvent` (`Timestamp` BIGINT, `ProcessId` INT, `ThreadId` INT, `MethodID` BIGINT, `ModuleID` BIGINT, `MethodStartAddress` BIGINT, `MethodSize` INT, `MethodToken` INT, `MethodFlags` INT, `MethodNamespace` TEXT, `MethodName` TEXT, `MethodSignature` TEXT, `ClrInstanceID` SMALLINT, `ReJITID` BIGINT);");
|
||||
CreateSql(this->db, "CREATE TABLE `ModuleLoadEvent` (`Timestamp` BIGINT, `ProcessId` INT, `ThreadId` INT, `ModuleID` BIGINT, `AssemblyID` BIGINT, `ModuleFlags` INT, `Reserved1` INT, `ModuleILPath` TEXT, `ModuleNativePath` TEXT, `ClrInstanceID` SMALLINT, `ManagedPdbSignature` BLOB, `ManagedPdbAge` INT, `ManagedPdbBuildPath` TEXT, `NativePdbSignature` BLOB, `NativePdbAge` INT, `NativePdbBuildPath` TEXT);");
|
||||
CreateSql(this->db, "CREATE TABLE `ModuleUnloadEvent` (`Timestamp` BIGINT, `ProcessId` INT, `ThreadId` INT, `ModuleID` BIGINT, `AssemblyID` BIGINT, `ModuleFlags` INT, `Reserved1` INT, `ModuleILPath` TEXT, `ModuleNativePath` TEXT, `ClrInstanceID` SMALLINT, `ManagedPdbSignature` BLOB, `ManagedPdbAge` INT, `ManagedPdbBuildPath` TEXT, `NativePdbSignature` BLOB, `NativePdbAge` INT, `NativePdbBuildPath` TEXT);");
|
||||
CreateSql(this->db, "CREATE TABLE `RuntimeInformationEvent` (`Timestamp` BIGINT, `ProcessId` INT, `ThreadId` INT, `ClrInstanceID` SMALLINT, `Sku` SMALLINT, `BclMajorVersion` SMALLINT, `BclMinorVersion` SMALLINT, `BclBuildNumber` SMALLINT, `BclQfeNumber` SMALLINT, `VMMajorVersion` SMALLINT, `VMMinorVersion` SMALLINT, `VMBuildNumber` SMALLINT, `VMQfeNumber` SMALLINT, `StartupFlags` INT, `StartupMode` TINYINT, `CommandLine` TEXT, `ComObjectGuid` BLOB, `RuntimeDllPath` TEXT);");
|
||||
CreateSql(this->db, "CREATE TABLE `StartupEvent` (`Timestamp` BIGINT, `ProcessId` INT, `ThreadId` INT, `ClrInstanceID` SMALLINT, `SteadyClockTime` BIGINT, `SystemClockTime` BIGINT);");
|
||||
CreateSql(this->db, "CREATE TABLE `MethodEnterEvent` (`Timestamp` BIGINT, `ProcessId` INT, `ThreadId` INT, `FunctionID` BIGINT, `FrameCount` INT, `Frames` BLOB);");
|
||||
|
||||
PrepareSql<-1>(this->db, "INSERT INTO `HeartBeatEvent` (`Timestamp`, `ProcessId`, `ThreadId`) VALUES (@1, @2, @3);", this->statementMap);
|
||||
PrepareSql<0>(this->db, "INSERT INTO `ShutdownEvent` (`Timestamp`, `ProcessId`, `ThreadId`, `ClrInstanceID`) VALUES (@1, @2, @3, @4);", this->statementMap);
|
||||
PrepareSql<1>(this->db, "INSERT INTO `AppDomainLoadEvent` (`Timestamp`, `ProcessId`, `ThreadId`, `AppDomainID`, `AppDomainFlags`, `AppDomainName`, `AppDomainIndex`, `ClrInstanceID`) VALUES (@1, @2, @3, @4, @5, @6, @7, @8);", this->statementMap);
|
||||
PrepareSql<2>(this->db, "INSERT INTO `AppDomainUnloadEvent` (`Timestamp`, `ProcessId`, `ThreadId`, `AppDomainID`, `AppDomainFlags`, `AppDomainName`, `AppDomainIndex`, `ClrInstanceID`) VALUES (@1, @2, @3, @4, @5, @6, @7, @8);", this->statementMap);
|
||||
PrepareSql<3>(this->db, "INSERT INTO `AssemblyLoadEvent` (`Timestamp`, `ProcessId`, `ThreadId`, `AssemblyID`, `AppDomainID`, `BindingID`, `AssemblyFlags`, `FullyQualifiedAssemblyName`, `ClrInstanceID`) VALUES (@1, @2, @3, @4, @5, @6, @7, @8, @9);", this->statementMap);
|
||||
PrepareSql<4>(this->db, "INSERT INTO `AssemblyUnloadEvent` (`Timestamp`, `ProcessId`, `ThreadId`, `AssemblyID`, `AppDomainID`, `BindingID`, `AssemblyFlags`, `FullyQualifiedAssemblyName`, `ClrInstanceID`) VALUES (@1, @2, @3, @4, @5, @6, @7, @8, @9);", this->statementMap);
|
||||
PrepareSql<5>(this->db, "INSERT INTO `JITCompilationFinishedEvent` (`Timestamp`, `ProcessId`, `ThreadId`, `MethodID`, `ModuleID`, `MethodStartAddress`, `MethodSize`, `MethodToken`, `MethodFlags`, `MethodNamespace`, `MethodName`, `MethodSignature`, `ClrInstanceID`, `ReJITID`) VALUES (@1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14);", this->statementMap);
|
||||
PrepareSql<6>(this->db, "INSERT INTO `JITCompilationStartedEvent` (`Timestamp`, `ProcessId`, `ThreadId`, `MethodID`, `ModuleID`, `MethodToken`, `MethodILSize`, `MethodNamespace`, `MethodName`, `MethodSignature`, `ClrInstanceID`) VALUES (@1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11); ", this->statementMap);
|
||||
PrepareSql<7>(this->db, "INSERT INTO `MethodILToNativeMapEvent` (`Timestamp`, `ProcessId`, `ThreadId`, `MethodID`, `ReJITID`, `MethodExtent`, `CountOfMapEntries`, `ILOffsets`, `NativeOffsets`, `ClrInstanceID`) VALUES (@1, @2, @3, @4, @5, @6, @7, @8, @9, @10);", this->statementMap);
|
||||
PrepareSql<8>(this->db, "INSERT INTO `MethodUnloadEvent` (`Timestamp`, `ProcessId`, `ThreadId`, `MethodID`, `ModuleID`, `MethodStartAddress`, `MethodSize`, `MethodToken`, `MethodFlags`, `MethodNamespace`, `MethodName`, `MethodSignature`, `ClrInstanceID`, `ReJITID`) VALUES (@1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14);", this->statementMap);
|
||||
PrepareSql<9>(this->db, "INSERT INTO `ModuleLoadEvent` (`Timestamp`, `ProcessId`, `ThreadId`, `ModuleID`, `AssemblyID`, `ModuleFlags`, `Reserved1`, `ModuleILPath`, `ModuleNativePath`, `ClrInstanceID`, `ManagedPdbSignature`, `ManagedPdbAge`, `ManagedPdbBuildPath`, `NativePdbSignature`, `NativePdbAge`, `NativePdbBuildPath`) VALUES (@1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16);", this->statementMap);
|
||||
PrepareSql<10>(this->db, "INSERT INTO `ModuleUnloadEvent` (`Timestamp`, `ProcessId`, `ThreadId`, `ModuleID`, `AssemblyID`, `ModuleFlags`, `Reserved1`, `ModuleILPath`, `ModuleNativePath`, `ClrInstanceID`, `ManagedPdbSignature`, `ManagedPdbAge`, `ManagedPdbBuildPath`, `NativePdbSignature`, `NativePdbAge`, `NativePdbBuildPath`) VALUES (@1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16);", this->statementMap);
|
||||
PrepareSql<11>(this->db, "INSERT INTO `RuntimeInformationEvent` (`Timestamp`, `ProcessId`, `ThreadId`, `ClrInstanceID`, `Sku`, `BclMajorVersion`, `BclMinorVersion`, `BclBuildNumber`, `BclQfeNumber`, `VMMajorVersion`, `VMMinorVersion`, `VMBuildNumber`, `VMQfeNumber`, `StartupFlags`, `StartupMode`, `CommandLine`, `ComObjectGuid`, `RuntimeDllPath`) VALUES (@1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18);", this->statementMap);
|
||||
PrepareSql<12>(this->db, "INSERT INTO `StartupEvent` (`Timestamp`, `ProcessId`, `ThreadId`, `ClrInstanceID`, `SteadyClockTime`, `SystemClockTime`) VALUES (@1, @2, @3, @4, @5, @6);", this->statementMap);
|
||||
PrepareSql<13>(this->db, "INSERT INTO `MethodEnterEvent` (`Timestamp`, `ProcessId`, `ThreadId`, `FunctionID`, `FrameCount`, `FunctionIDs`) VALUES (@1, @2, @3, @4, @5, @6);", this->statementMap);
|
||||
|
||||
this->heartBeatThread = std::thread([this] {
|
||||
while (true)
|
||||
{
|
||||
this->eventQueue->push(LogEvent<HeartBeatEvent>());
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SQLiteEventLogger::BeginTransaction()
|
||||
{
|
||||
sqlite3_exec(this->db, "BEGIN TRANSACTION", nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void SQLiteEventLogger::EndTransaction()
|
||||
{
|
||||
sqlite3_exec(this->db, "END TRANSACTION", nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void SQLiteEventLogger::FinalizeStatements()
|
||||
{
|
||||
for (auto &&e : this->statementMap)
|
||||
{
|
||||
sqlite3_finalize(e.second);
|
||||
}
|
||||
}
|
||||
|
||||
void SQLiteEventLogger::RunEventLoop()
|
||||
{
|
||||
this->BeginTransaction();
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto event = this->eventQueue->pop();
|
||||
auto eventId = event->GetEventId();
|
||||
SQLiteSerializer serializer(this->statementMap[eventId]);
|
||||
event->Serialize(serializer);
|
||||
serializer.Commit();
|
||||
|
||||
if (eventId <= 0)
|
||||
{
|
||||
this->EndTransaction();
|
||||
if (eventId == 0)
|
||||
{
|
||||
this->FinalizeStatements();
|
||||
break;
|
||||
}
|
||||
this->BeginTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SQLiteEventLogger::~SQLiteEventLogger()
|
||||
{
|
||||
if (this->db != nullptr)
|
||||
{
|
||||
sqlite3_close(this->db);
|
||||
}
|
||||
|
||||
this->heartBeatThread.detach(); // not harmful since in a very short while the profiler will be torn down
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// 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 <unordered_map>
|
||||
#include <thread>
|
||||
#include "ThreadSafeQueue.h"
|
||||
#include "EtwEvent.h"
|
||||
#include "sqlite3.h"
|
||||
|
||||
class SQLiteEventLogger
|
||||
{
|
||||
public:
|
||||
SQLiteEventLogger(sqlite3 *db, std::shared_ptr<ThreadSafeQueue<std::unique_ptr<EtwEvent>>> eventQueue, const std::string &bPerfLogsPath);
|
||||
~SQLiteEventLogger();
|
||||
void BeginTransaction();
|
||||
void EndTransaction();
|
||||
void FinalizeStatements();
|
||||
void RunEventLoop();
|
||||
|
||||
private:
|
||||
sqlite3 *db;
|
||||
std::shared_ptr<ThreadSafeQueue<std::unique_ptr<EtwEvent>>> eventQueue;
|
||||
std::unordered_map<int32_t, sqlite3_stmt *> statementMap;
|
||||
std::thread heartBeatThread;
|
||||
};
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "EtwEvent.h"
|
||||
#include "sqlite3.h"
|
||||
#include "SQLiteSerializer.h"
|
||||
|
||||
SQLiteSerializer::SQLiteSerializer(sqlite3_stmt *stmt) : stmt(stmt), i(1)
|
||||
{
|
||||
}
|
||||
|
||||
void SQLiteSerializer::Write(int8_t value)
|
||||
{
|
||||
sqlite3_bind_int(this->stmt, i++, value);
|
||||
}
|
||||
|
||||
void SQLiteSerializer::Write(int16_t value)
|
||||
{
|
||||
sqlite3_bind_int(this->stmt, i++, value);
|
||||
}
|
||||
|
||||
void SQLiteSerializer::Write(int32_t value)
|
||||
{
|
||||
sqlite3_bind_int(this->stmt, i++, value);
|
||||
}
|
||||
|
||||
void SQLiteSerializer::Write(uint32_t value)
|
||||
{
|
||||
sqlite3_bind_int(this->stmt, i++, value);
|
||||
}
|
||||
|
||||
void SQLiteSerializer::Write(int64_t value)
|
||||
{
|
||||
sqlite3_bind_int64(this->stmt, i++, value);
|
||||
}
|
||||
|
||||
void SQLiteSerializer::Write(uint64_t value)
|
||||
{
|
||||
sqlite3_bind_int64(this->stmt, i++, value);
|
||||
}
|
||||
|
||||
void SQLiteSerializer::Write(const portable_wide_string &value)
|
||||
{
|
||||
sqlite3_bind_blob(this->stmt, i++, value.c_str(), static_cast<int32_t>(value.length() * 2), nullptr);
|
||||
}
|
||||
|
||||
void SQLiteSerializer::Write(const GUID &value)
|
||||
{
|
||||
sqlite3_bind_blob(this->stmt, i++, &value, 16, nullptr);
|
||||
}
|
||||
|
||||
void SQLiteSerializer::Write(const std::vector<uint32_t> &value)
|
||||
{
|
||||
sqlite3_bind_blob(this->stmt, i++, value.data(), static_cast<int32_t>(value.size() * sizeof(uint32_t)), nullptr);
|
||||
}
|
||||
|
||||
void SQLiteSerializer::Write(const std::vector<uint64_t> &value)
|
||||
{
|
||||
sqlite3_bind_blob(this->stmt, i++, value.data(), static_cast<int32_t>(value.size() * sizeof(uint64_t)), nullptr);
|
||||
}
|
||||
|
||||
void SQLiteSerializer::WriteNull()
|
||||
{
|
||||
sqlite3_bind_null(this->stmt, i++);
|
||||
}
|
||||
|
||||
void SQLiteSerializer::Commit()
|
||||
{
|
||||
sqlite3_step(this->stmt);
|
||||
sqlite3_reset(this->stmt);
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// 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
|
||||
|
||||
class SQLiteSerializer : public Serializer
|
||||
{
|
||||
public:
|
||||
SQLiteSerializer(sqlite3_stmt *stmt);
|
||||
void Write(int8_t value) override;
|
||||
void Write(int16_t value) override;
|
||||
void Write(int32_t value) override;
|
||||
void Write(uint32_t value) override;
|
||||
void Write(int64_t value) override;
|
||||
void Write(uint64_t value) override;
|
||||
void Write(const portable_wide_string &value) override;
|
||||
void Write(const GUID &value) override;
|
||||
void Write(const std::vector<uint32_t> &value) override;
|
||||
void Write(const std::vector<uint64_t> &value) override;
|
||||
void WriteNull() override;
|
||||
void Commit() override;
|
||||
|
||||
private:
|
||||
sqlite3_stmt *stmt;
|
||||
int i;
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
// 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 <cstdint>
|
||||
#include <vector>
|
||||
#include "PortableString.h"
|
||||
#include "GuidDef.h"
|
||||
|
||||
class Serializer
|
||||
{
|
||||
public:
|
||||
virtual ~Serializer() = default;
|
||||
virtual void Write(int8_t value) = 0;
|
||||
virtual void Write(int16_t value) = 0;
|
||||
virtual void Write(int32_t value) = 0;
|
||||
virtual void Write(uint32_t value) = 0;
|
||||
virtual void Write(int64_t value) = 0;
|
||||
virtual void Write(uint64_t value) = 0;
|
||||
virtual void Write(const portable_wide_string &value) = 0;
|
||||
virtual void Write(const GUID &value) = 0;
|
||||
virtual void Write(const std::vector<uint32_t> &value) = 0;
|
||||
virtual void Write(const std::vector<uint64_t> &value) = 0;
|
||||
virtual void WriteNull() = 0;
|
||||
virtual void Commit() = 0;
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "ShutdownEvent.h"
|
||||
|
||||
ShutdownEvent::ShutdownEvent(int16_t clrInstanceId) : EtwEvent(), clrInstanceId(clrInstanceId)
|
||||
{
|
||||
}
|
||||
|
||||
void ShutdownEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
|
||||
t.Write(this->clrInstanceId);
|
||||
}
|
||||
|
||||
int32_t ShutdownEvent::GetEventId()
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// 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 "EtwEvent.h"
|
||||
|
||||
class ShutdownEvent : public EtwEvent
|
||||
{
|
||||
public:
|
||||
ShutdownEvent(int16_t clrInstanceId);
|
||||
void Serialize(Serializer &t) override;
|
||||
int32_t GetEventId() override;
|
||||
|
||||
private:
|
||||
int16_t clrInstanceId;
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
// 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 "StackEvent.h"
|
||||
|
||||
StackEvent::StackEvent(const std::vector<uintptr_t> &functionIds) : functionIds(functionIds)
|
||||
{
|
||||
}
|
||||
|
||||
void StackEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
t.Write(static_cast<int32_t>(this->functionIds.size()));
|
||||
t.Write(this->functionIds);
|
||||
}
|
||||
|
||||
int32_t StackEvent::GetEventId()
|
||||
{
|
||||
return 13;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "StartupEvent.h"
|
||||
#include <chrono>
|
||||
|
||||
StartupEvent::StartupEvent(int16_t clrInstanceId) : EtwEvent(), clrInstanceId(clrInstanceId), steadyClockTime(std::chrono::steady_clock::now().time_since_epoch().count()), systemClockTime(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()))
|
||||
{
|
||||
}
|
||||
|
||||
void StartupEvent::Serialize(Serializer &t)
|
||||
{
|
||||
EtwEvent::Serialize(t);
|
||||
|
||||
t.Write(this->clrInstanceId);
|
||||
t.Write(this->steadyClockTime);
|
||||
t.Write(this->systemClockTime);
|
||||
}
|
||||
|
||||
int32_t StartupEvent::GetEventId()
|
||||
{
|
||||
return 12;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// 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 "EtwEvent.h"
|
||||
|
||||
class StartupEvent : public EtwEvent
|
||||
{
|
||||
public:
|
||||
StartupEvent(int16_t clrInstanceId);
|
||||
void Serialize(Serializer &t) override;
|
||||
int32_t GetEventId() override;
|
||||
|
||||
private:
|
||||
int16_t clrInstanceId;
|
||||
int64_t steadyClockTime;
|
||||
int64_t systemClockTime;
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "ClassFactory.h"
|
||||
|
||||
const IID IID_NULL = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
const IID IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
|
||||
|
||||
const IID IID_IClassFactory = {0x00000001, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
|
||||
|
||||
BOOL STDMETHODCALLTYPE DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
extern "C" HRESULT STDMETHODCALLTYPE DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
|
||||
{
|
||||
// {D46E5565-D624-4C4D-89CC-7A82887D3626}
|
||||
const GUID CLSID_CorProfiler = {0xD46E5565, 0xD624, 0x4C4D, {0x89, 0xCC, 0x7A, 0x82, 0x88, 0x7D, 0x36, 0x26}};
|
||||
|
||||
if (ppv == nullptr || rclsid != CLSID_CorProfiler)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
auto factory = new ClassFactory;
|
||||
if (factory == nullptr)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
return factory->QueryInterface(riid, ppv);
|
||||
}
|
||||
|
||||
extern "C" HRESULT STDMETHODCALLTYPE DllCanUnloadNow()
|
||||
{
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef _WINDOWS
|
||||
#include <cstdlib>
|
||||
#include "pal_mstypes.h"
|
||||
#include "pal.h"
|
||||
#include "ntimage.h"
|
||||
#include "corhdr.h"
|
||||
#define CoTaskMemAlloc(cb) malloc(cb)
|
||||
#define CoTaskMemFree(cb) free(cb)
|
||||
#else
|
||||
#include <Windows.h>
|
||||
#define PAL_wcslen wcslen
|
||||
#endif
|
|
@ -0,0 +1,435 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
//
|
||||
//
|
||||
//
|
||||
// ===========================================================================
|
||||
// File: sha1.cpp
|
||||
//
|
||||
// ===========================================================================
|
||||
/*++
|
||||
|
||||
Abstract:
|
||||
|
||||
SHA-1 implementation
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
/*
|
||||
File sha1.cpp <STRIP>Version 03 August 2000.</STRIP>
|
||||
|
||||
|
||||
This implements the SHA-1 hash function.
|
||||
For algorithmic background see (for example)
|
||||
|
||||
|
||||
Alfred J. Menezes et al
|
||||
Handbook of Applied Cryptography
|
||||
The CRC Press Series on Discrete Mathematics
|
||||
and its Applications
|
||||
CRC Press LLC, 1997
|
||||
ISBN 0-8495-8523-7
|
||||
QA76.9A25M643
|
||||
|
||||
Also see FIPS 180-1 - Secure Hash Standard,
|
||||
1993 May 11 and 1995 April 17, by the U.S.
|
||||
National Institute of Standards and Technology (NIST).
|
||||
|
||||
*/
|
||||
|
||||
#define MODE_ANY
|
||||
#define SO_TOLERANT
|
||||
#define GC_NOTRIGGER
|
||||
#define CONTRACTL
|
||||
#define CONTRACTL_END
|
||||
#define NOTHROW
|
||||
#include "profiler_pal.h"
|
||||
#include "sha1.h"
|
||||
#include "PortableString.h"
|
||||
#include <cassert>
|
||||
#ifndef min
|
||||
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
typedef const DWORD DWORDC;
|
||||
#define ROTATE32L(x, n) _rotl(x, n)
|
||||
#define SHAVE32(x) (DWORD)(x)
|
||||
|
||||
static void SHA1_block(SHA1_CTX *ctx)
|
||||
/*
|
||||
Update the SHA-1 hash from a fresh 64 bytes of data.
|
||||
*/
|
||||
{
|
||||
static DWORDC sha1_round1 = 0x5A827999u;
|
||||
static DWORDC sha1_round2 = 0x6ED9EBA1u;
|
||||
static DWORDC sha1_round3 = 0x8F1BBCDCu;
|
||||
static DWORDC sha1_round4 = 0xCA62C1D6u;
|
||||
|
||||
DWORD a = ctx->partial_hash[0], b = ctx->partial_hash[1];
|
||||
DWORD c = ctx->partial_hash[2], d = ctx->partial_hash[3];
|
||||
DWORD e = ctx->partial_hash[4];
|
||||
DWORD msg80[80];
|
||||
int i;
|
||||
BOOL OK = TRUE;
|
||||
|
||||
// OACR note:
|
||||
// Loop conditions are using (i <= limit - increment) instead of (i < limit) to satisfy OACR. When the increment is greater
|
||||
// than 1, OACR incorrectly thinks that the max value of 'i' is (limit - 1).
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{ // Copy to local array, zero original
|
||||
// Extend length to 80
|
||||
DWORDC datval = ctx->awaiting_data[i];
|
||||
ctx->awaiting_data[i] = 0;
|
||||
msg80[i] = datval;
|
||||
}
|
||||
|
||||
for (i = 16; i <= 80 - 2; i += 2)
|
||||
{
|
||||
DWORDC temp1 = msg80[i - 3] ^ msg80[i - 8] ^ msg80[i - 14] ^ msg80[i - 16];
|
||||
DWORDC temp2 = msg80[i - 2] ^ msg80[i - 7] ^ msg80[i - 13] ^ msg80[i - 15];
|
||||
msg80[i] = ROTATE32L(temp1, 1);
|
||||
msg80[i + 1] = ROTATE32L(temp2, 1);
|
||||
}
|
||||
|
||||
#define ROUND1(B, C, D) ((D ^ (B & (C ^ D))) + sha1_round1)
|
||||
// Equivalent to (B & C) | (~B & D).
|
||||
// (check cases B = 0 and B = 1)
|
||||
#define ROUND2(B, C, D) ((B ^ C ^ D) + sha1_round2)
|
||||
|
||||
#define ROUND3(B, C, D) ((C & (B | D) | (B & D)) + sha1_round3)
|
||||
|
||||
#define ROUND4(B, C, D) ((B ^ C ^ D) + sha1_round4)
|
||||
|
||||
// Round 1
|
||||
for (i = 0; i <= 20 - 5; i += 5)
|
||||
{
|
||||
e += ROTATE32L(a, 5) + ROUND1(b, c, d) + msg80[i];
|
||||
b = ROTATE32L(b, 30);
|
||||
|
||||
d += ROTATE32L(e, 5) + ROUND1(a, b, c) + msg80[i + 1];
|
||||
a = ROTATE32L(a, 30);
|
||||
|
||||
c += ROTATE32L(d, 5) + ROUND1(e, a, b) + msg80[i + 2];
|
||||
e = ROTATE32L(e, 30);
|
||||
|
||||
b += ROTATE32L(c, 5) + ROUND1(d, e, a) + msg80[i + 3];
|
||||
d = ROTATE32L(d, 30);
|
||||
|
||||
a += ROTATE32L(b, 5) + ROUND1(c, d, e) + msg80[i + 4];
|
||||
c = ROTATE32L(c, 30);
|
||||
#if 0
|
||||
printf("i = %ld %08lx %08lx %08lx %08lx %08lx\n",
|
||||
i, a, b, c, d, e);
|
||||
#endif
|
||||
} // for i
|
||||
|
||||
// Round 2
|
||||
for (i = 20; i <= 40 - 5; i += 5)
|
||||
{
|
||||
e += ROTATE32L(a, 5) + ROUND2(b, c, d) + msg80[i];
|
||||
b = ROTATE32L(b, 30);
|
||||
|
||||
d += ROTATE32L(e, 5) + ROUND2(a, b, c) + msg80[i + 1];
|
||||
a = ROTATE32L(a, 30);
|
||||
|
||||
c += ROTATE32L(d, 5) + ROUND2(e, a, b) + msg80[i + 2];
|
||||
e = ROTATE32L(e, 30);
|
||||
|
||||
b += ROTATE32L(c, 5) + ROUND2(d, e, a) + msg80[i + 3];
|
||||
d = ROTATE32L(d, 30);
|
||||
|
||||
a += ROTATE32L(b, 5) + ROUND2(c, d, e) + msg80[i + 4];
|
||||
c = ROTATE32L(c, 30);
|
||||
} // for i
|
||||
|
||||
// Round 3
|
||||
for (i = 40; i <= 60 - 5; i += 5)
|
||||
{
|
||||
e += ROTATE32L(a, 5) + ROUND3(b, c, d) + msg80[i];
|
||||
b = ROTATE32L(b, 30);
|
||||
|
||||
d += ROTATE32L(e, 5) + ROUND3(a, b, c) + msg80[i + 1];
|
||||
a = ROTATE32L(a, 30);
|
||||
|
||||
c += ROTATE32L(d, 5) + ROUND3(e, a, b) + msg80[i + 2];
|
||||
e = ROTATE32L(e, 30);
|
||||
|
||||
b += ROTATE32L(c, 5) + ROUND3(d, e, a) + msg80[i + 3];
|
||||
d = ROTATE32L(d, 30);
|
||||
|
||||
a += ROTATE32L(b, 5) + ROUND3(c, d, e) + msg80[i + 4];
|
||||
c = ROTATE32L(c, 30);
|
||||
} // for i
|
||||
|
||||
// Round 4
|
||||
for (i = 60; i <= 80 - 5; i += 5)
|
||||
{
|
||||
e += ROTATE32L(a, 5) + ROUND4(b, c, d) + msg80[i];
|
||||
b = ROTATE32L(b, 30);
|
||||
|
||||
d += ROTATE32L(e, 5) + ROUND4(a, b, c) + msg80[i + 1];
|
||||
a = ROTATE32L(a, 30);
|
||||
|
||||
c += ROTATE32L(d, 5) + ROUND4(e, a, b) + msg80[i + 2];
|
||||
e = ROTATE32L(e, 30);
|
||||
|
||||
b += ROTATE32L(c, 5) + ROUND4(d, e, a) + msg80[i + 3];
|
||||
d = ROTATE32L(d, 30);
|
||||
|
||||
a += ROTATE32L(b, 5) + ROUND4(c, d, e) + msg80[i + 4];
|
||||
c = ROTATE32L(c, 30);
|
||||
} // for i
|
||||
|
||||
#undef ROUND1
|
||||
#undef ROUND2
|
||||
#undef ROUND3
|
||||
#undef ROUND4
|
||||
|
||||
ctx->partial_hash[0] += a;
|
||||
ctx->partial_hash[1] += b;
|
||||
ctx->partial_hash[2] += c;
|
||||
ctx->partial_hash[3] += d;
|
||||
ctx->partial_hash[4] += e;
|
||||
#if 0
|
||||
for (i = 0; i < 16; i++) {
|
||||
printf("%8lx ", msg16[i]);
|
||||
if ((i & 7) == 7) printf("\n");
|
||||
}
|
||||
printf("a, b, c, d, e = %08lx %08lx %08lx %08lx %08lx\n",
|
||||
a, b, c, d, e);
|
||||
printf("Partial hash = %08lx %08lx %08lx %08lx %08lx\n",
|
||||
(long)ctx->partial_hash[0], (long)ctx->partial_hash[1],
|
||||
(long)ctx->partial_hash[2], (long)ctx->partial_hash[3],
|
||||
(long)ctx->partial_hash[4]);
|
||||
#endif
|
||||
} // end SHA1_block
|
||||
|
||||
void SHA1Hash::SHA1Init(SHA1_CTX *ctx)
|
||||
{
|
||||
CONTRACTL
|
||||
{
|
||||
NOTHROW;
|
||||
GC_NOTRIGGER;
|
||||
MODE_ANY;
|
||||
SO_TOLERANT;
|
||||
}
|
||||
CONTRACTL_END;
|
||||
|
||||
ctx->nbit_total[0] = ctx->nbit_total[1] = 0;
|
||||
|
||||
for (DWORD i = 0; i != 16; i++)
|
||||
{
|
||||
ctx->awaiting_data[i] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Initialize hash variables.
|
||||
|
||||
*/
|
||||
|
||||
ctx->partial_hash[0] = 0x67452301u;
|
||||
ctx->partial_hash[1] = 0xefcdab89u;
|
||||
ctx->partial_hash[2] = ~ctx->partial_hash[0];
|
||||
ctx->partial_hash[3] = ~ctx->partial_hash[1];
|
||||
ctx->partial_hash[4] = 0xc3d2e1f0u;
|
||||
}
|
||||
|
||||
void SHA1Hash::SHA1Update(
|
||||
SHA1_CTX *ctx, // IN/OUT
|
||||
const BYTE *msg, // IN
|
||||
DWORD nbyte) // IN
|
||||
/*
|
||||
Append data to a partially hashed SHA-1 message.
|
||||
*/
|
||||
{
|
||||
CONTRACTL
|
||||
{
|
||||
NOTHROW;
|
||||
GC_NOTRIGGER;
|
||||
MODE_ANY;
|
||||
SO_TOLERANT;
|
||||
}
|
||||
CONTRACTL_END;
|
||||
|
||||
const BYTE *fresh_data = msg;
|
||||
DWORD nbyte_left = nbyte;
|
||||
DWORD nbit_occupied = ctx->nbit_total[0] & 511;
|
||||
DWORD *awaiting_data;
|
||||
DWORDC nbitnew_low = SHAVE32(8 * nbyte);
|
||||
|
||||
_ASSERTE((nbit_occupied & 7) == 0); // Partial bytes not implemented
|
||||
|
||||
ctx->nbit_total[0] += nbitnew_low;
|
||||
ctx->nbit_total[1] += (nbyte >> 29) + (SHAVE32(ctx->nbit_total[0]) < nbitnew_low);
|
||||
|
||||
/* Advance to word boundary in waiting_data */
|
||||
|
||||
if ((nbit_occupied & 31) != 0)
|
||||
{
|
||||
awaiting_data = ctx->awaiting_data + nbit_occupied / 32;
|
||||
|
||||
while ((nbit_occupied & 31) != 0 && nbyte_left != 0)
|
||||
{
|
||||
nbit_occupied += 8;
|
||||
*awaiting_data |= (DWORD)*fresh_data++
|
||||
<< ((-(int)nbit_occupied) & 31);
|
||||
nbyte_left--; // Start at most significant byte
|
||||
}
|
||||
} // if nbit_occupied
|
||||
|
||||
/* Transfer 4 bytes at a time */
|
||||
|
||||
do
|
||||
{
|
||||
DWORDC nword_occupied = nbit_occupied / 32;
|
||||
DWORD nwcopy = min(nbyte_left / 4, 16 - nword_occupied);
|
||||
_ASSERTE(nbit_occupied <= 512);
|
||||
_ASSERTE((nbit_occupied & 31) == 0 || nbyte_left == 0);
|
||||
awaiting_data = ctx->awaiting_data + nword_occupied;
|
||||
nbyte_left -= 4 * nwcopy;
|
||||
nbit_occupied += 32 * nwcopy;
|
||||
|
||||
while (nwcopy != 0)
|
||||
{
|
||||
DWORDC byte0 = (DWORD)fresh_data[0];
|
||||
DWORDC byte1 = (DWORD)fresh_data[1];
|
||||
DWORDC byte2 = (DWORD)fresh_data[2];
|
||||
DWORDC byte3 = (DWORD)fresh_data[3];
|
||||
*awaiting_data++ = byte3 | (byte2 << 8) | (byte1 << 16) | (byte0 << 24);
|
||||
/* Big endian */
|
||||
fresh_data += 4;
|
||||
nwcopy--;
|
||||
}
|
||||
|
||||
if (nbit_occupied == 512)
|
||||
{
|
||||
SHA1_block(ctx);
|
||||
nbit_occupied = 0;
|
||||
awaiting_data -= 16;
|
||||
_ASSERTE(awaiting_data == ctx->awaiting_data);
|
||||
}
|
||||
} while (nbyte_left >= 4);
|
||||
|
||||
_ASSERTE(ctx->awaiting_data + nbit_occupied / 32 == awaiting_data);
|
||||
|
||||
while (nbyte_left != 0)
|
||||
{
|
||||
DWORDC new_byte = (DWORD)*fresh_data++;
|
||||
|
||||
_ASSERTE((nbit_occupied & 31) <= 16);
|
||||
nbit_occupied += 8;
|
||||
*awaiting_data |= new_byte << ((-(int)nbit_occupied) & 31);
|
||||
nbyte_left--;
|
||||
}
|
||||
|
||||
_ASSERTE(nbit_occupied == (ctx->nbit_total[0] & 511));
|
||||
} // end SHA1Update
|
||||
|
||||
void SHA1Hash::SHA1Final(
|
||||
SHA1_CTX *ctx, // IN/OUT
|
||||
BYTE *digest) // OUT
|
||||
/*
|
||||
Finish a SHA-1 hash.
|
||||
*/
|
||||
{
|
||||
CONTRACTL
|
||||
{
|
||||
NOTHROW;
|
||||
GC_NOTRIGGER;
|
||||
MODE_ANY;
|
||||
SO_TOLERANT;
|
||||
}
|
||||
CONTRACTL_END;
|
||||
|
||||
DWORDC nbit0 = ctx->nbit_total[0];
|
||||
DWORDC nbit1 = ctx->nbit_total[1];
|
||||
DWORD nbit_occupied = nbit0 & 511;
|
||||
DWORD i;
|
||||
|
||||
_ASSERTE((nbit_occupied & 7) == 0);
|
||||
|
||||
ctx->awaiting_data[nbit_occupied / 32] |= (DWORD)0x80 << ((-8 - nbit_occupied) & 31);
|
||||
// Append a 1 bit
|
||||
nbit_occupied += 8;
|
||||
|
||||
// Append zero bits until length (in bits) is 448 mod 512.
|
||||
// Then append the length, in bits.
|
||||
// Here we assume the buffer was zeroed earlier.
|
||||
|
||||
if (nbit_occupied > 448)
|
||||
{ // If fewer than 64 bits left
|
||||
SHA1_block(ctx);
|
||||
nbit_occupied = 0;
|
||||
}
|
||||
ctx->awaiting_data[14] = nbit1;
|
||||
ctx->awaiting_data[15] = nbit0;
|
||||
SHA1_block(ctx);
|
||||
|
||||
/* Copy final digest to user-supplied byte array */
|
||||
|
||||
for (i = 0; i != 5; i++)
|
||||
{
|
||||
DWORDC dwi = ctx->partial_hash[i];
|
||||
digest[4 * i + 0] = (BYTE)((dwi >> 24) & 255);
|
||||
digest[4 * i + 1] = (BYTE)((dwi >> 16) & 255);
|
||||
digest[4 * i + 2] = (BYTE)((dwi >> 8) & 255);
|
||||
digest[4 * i + 3] = (BYTE)(dwi & 255); // Big-endian
|
||||
}
|
||||
} // end SHA1Final
|
||||
|
||||
SHA1Hash::SHA1Hash()
|
||||
{
|
||||
CONTRACTL
|
||||
{
|
||||
NOTHROW;
|
||||
GC_NOTRIGGER;
|
||||
MODE_ANY;
|
||||
SO_TOLERANT;
|
||||
}
|
||||
CONTRACTL_END;
|
||||
|
||||
m_fFinalized = FALSE;
|
||||
SHA1Init(&m_Context);
|
||||
}
|
||||
|
||||
void SHA1Hash::AddData(BYTE *pbData, DWORD cbData)
|
||||
{
|
||||
CONTRACTL
|
||||
{
|
||||
NOTHROW;
|
||||
GC_NOTRIGGER;
|
||||
MODE_ANY;
|
||||
SO_TOLERANT;
|
||||
}
|
||||
CONTRACTL_END;
|
||||
|
||||
if (m_fFinalized)
|
||||
return;
|
||||
|
||||
SHA1Update(&m_Context, pbData, cbData);
|
||||
}
|
||||
|
||||
// Retrieve a pointer to the final hash.
|
||||
BYTE *SHA1Hash::GetHash()
|
||||
{
|
||||
CONTRACTL
|
||||
{
|
||||
NOTHROW;
|
||||
GC_NOTRIGGER;
|
||||
MODE_ANY;
|
||||
SO_TOLERANT;
|
||||
}
|
||||
CONTRACTL_END;
|
||||
|
||||
if (m_fFinalized)
|
||||
return m_Value;
|
||||
|
||||
SHA1Final(&m_Context, m_Value);
|
||||
|
||||
m_fFinalized = TRUE;
|
||||
|
||||
return m_Value;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
//
|
||||
|
||||
// Hasher class, performs no allocation and therefore does not throw or return
|
||||
// errors. Usage is as follows:
|
||||
// Create an instance (this initializes the hash).
|
||||
// Add one or more blocks of input data using AddData().
|
||||
// Retrieve the hash using GetHash(). This can be done as many times as desired
|
||||
// until the object is destructed. Once a hash is asked for, further AddData
|
||||
// calls will be ignored. There is no way to reset object state (simply
|
||||
// destroy the object and create another instead).
|
||||
|
||||
#define SHA1_HASH_SIZE 20 // Number of bytes output by SHA-1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DWORD magic_sha1; // Magic value for A_SHA_CTX
|
||||
DWORD awaiting_data[16];
|
||||
// Data awaiting full 512-bit block.
|
||||
// Length (nbit_total[0] % 512) bits.
|
||||
// Unused part of buffer (at end) is zero
|
||||
DWORD partial_hash[5];
|
||||
// Hash through last full block
|
||||
DWORD nbit_total[2];
|
||||
// Total length of message so far
|
||||
// (bits, mod 2^64)
|
||||
} SHA1_CTX;
|
||||
|
||||
class SHA1Hash
|
||||
{
|
||||
private:
|
||||
SHA1_CTX m_Context;
|
||||
BYTE m_Value[SHA1_HASH_SIZE];
|
||||
BOOL m_fFinalized;
|
||||
|
||||
void SHA1Init(SHA1_CTX *);
|
||||
void SHA1Update(SHA1_CTX *, const BYTE *, const DWORD);
|
||||
void SHA1Final(SHA1_CTX *, BYTE *digest);
|
||||
|
||||
public:
|
||||
SHA1Hash();
|
||||
void AddData(BYTE *pbData, DWORD cbData);
|
||||
BYTE *GetHash();
|
||||
};
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2016 Microsoft
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,6 @@
|
|||
# BPerf
|
||||
BPerf is a Cloud Profiling system used by Bing.com based on Event Tracing for Windows.
|
||||
|
||||
## Microsoft Open Source Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"directory": "wwwroot/lib"
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp1.0</TargetFramework>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="**\*.cs" Exclude="$(GlobalExclude)" />
|
||||
<EmbeddedResource Include="**\*.resx" Exclude="$(GlobalExclude)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.App">
|
||||
<Version>1.0.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Sdk.Web">
|
||||
<Version>1.0.0-alpha-20161104-2-112</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics">
|
||||
<Version>1.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc">
|
||||
<Version>1.0.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Tools">
|
||||
<Version>1.0.0-preview2-final</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Routing">
|
||||
<Version>1.0.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration">
|
||||
<Version>1.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel">
|
||||
<Version>1.0.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles">
|
||||
<Version>1.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables">
|
||||
<Version>1.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json">
|
||||
<Version>1.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging">
|
||||
<Version>1.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console">
|
||||
<Version>1.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug">
|
||||
<Version>1.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions">
|
||||
<Version>1.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink.Loader">
|
||||
<Version>14.0.0</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<DotNetCliToolReference Include="BundlerMinifier.Core">
|
||||
<Version>2.2.301</Version>
|
||||
</DotNetCliToolReference>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
||||
<!--
|
||||
<Target Name="BeforePublish">
|
||||
<Exec Command="bower install" />
|
||||
<Exec Command="dotnet bundle" />
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,9 @@
|
|||
namespace TraceEventAPIServer
|
||||
{
|
||||
using System;
|
||||
|
||||
public sealed class CacheExpirationTimeProvider : ICacheExpirationTimeProvider
|
||||
{
|
||||
public DateTimeOffset Expiration => DateTimeOffset.UtcNow + TimeSpan.FromHours(1); // TODO: make this config driven
|
||||
}
|
||||
}
|
|
@ -0,0 +1,456 @@
|
|||
namespace TraceEventAPIServer
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using Diagnostics.Tracing.StackSources;
|
||||
using Microsoft.Diagnostics.Tracing.Etlx;
|
||||
using Microsoft.Diagnostics.Tracing.Stacks;
|
||||
using Microsoft.Diagnostics.Symbols;
|
||||
using Models;
|
||||
|
||||
public sealed class CallTreeDataProvider : ICallTreeDataProvider
|
||||
{
|
||||
private readonly CallTree callTree;
|
||||
|
||||
private readonly SymbolReader reader;
|
||||
|
||||
private readonly StackSource stacksource;
|
||||
|
||||
private List<TreeNode> summary;
|
||||
|
||||
private readonly Func<CallTreeNodeBase, bool> summaryPredicate;
|
||||
|
||||
private readonly Dictionary<string, CallTreeNodeBase> nodeNameCache = new Dictionary<string, CallTreeNodeBase>();
|
||||
|
||||
private readonly Dictionary<CallTreeNodeBase, TreeNode> callerTreeCache = new Dictionary<CallTreeNodeBase, TreeNode>();
|
||||
|
||||
private readonly Dictionary<CallTreeNodeBase, TreeNode> calleeTreeCache = new Dictionary<CallTreeNodeBase, TreeNode>();
|
||||
|
||||
private readonly object lockobj = new object();
|
||||
|
||||
public CallTreeDataProvider(TraceLog log, FilterParams filterParams, SymbolReader reader, ITraceDataPlugin plugin)
|
||||
{
|
||||
if (log == null)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullException(nameof(log));
|
||||
}
|
||||
|
||||
if (filterParams == null)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullException(nameof(filterParams));
|
||||
}
|
||||
|
||||
if (reader == null)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullException(nameof(reader));
|
||||
}
|
||||
|
||||
if (plugin == null)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullException(nameof(plugin));
|
||||
}
|
||||
|
||||
this.reader = reader;
|
||||
TraceEvents events = log.Events;
|
||||
this.stacksource = plugin.GetStackSource(events);
|
||||
this.summaryPredicate = plugin.SummaryPredicate;
|
||||
CallTree.DisableParallelism = true; // important
|
||||
this.stacksource = new FilterStackSource(filterParams, this.stacksource, ScalingPolicyKind.TimeMetric);
|
||||
|
||||
this.callTree = new CallTree(ScalingPolicyKind.TimeMetric) { StackSource = this.stacksource };
|
||||
float minIncusiveTimePercent;
|
||||
if (float.TryParse(filterParams.MinInclusiveTimePercent, out minIncusiveTimePercent) && minIncusiveTimePercent > 0)
|
||||
{
|
||||
this.callTree.FoldNodesUnder(minIncusiveTimePercent * this.callTree.Root.InclusiveMetric / 100, true);
|
||||
}
|
||||
}
|
||||
|
||||
public TreeNode GetNode(string name)
|
||||
{
|
||||
lock (this.lockobj)
|
||||
{
|
||||
if (this.nodeNameCache.ContainsKey(name))
|
||||
{
|
||||
CallTreeDataProviderEventSource.Log.NodeCacheHit(name);
|
||||
return new TreeNode(this.nodeNameCache[name]);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var node in this.callTree.ByID)
|
||||
{
|
||||
if (node.Name == name)
|
||||
{
|
||||
this.nodeNameCache.Add(name, node);
|
||||
CallTreeDataProviderEventSource.Log.NodeCacheMisss(name);
|
||||
return new TreeNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
CallTreeDataProviderEventSource.Log.NodeCacheNotFound(name);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TreeNode GetCallerTreeNode(string name, string path = "")
|
||||
{
|
||||
lock (this.lockobj)
|
||||
{
|
||||
CallTreeNodeBase node = this.GetNode(name).BackingNode;
|
||||
TreeNode callerTreeNode;
|
||||
|
||||
if (this.callerTreeCache.ContainsKey(node))
|
||||
{
|
||||
callerTreeNode = this.callerTreeCache[node];
|
||||
}
|
||||
else
|
||||
{
|
||||
callerTreeNode = new TreeNode(AggregateCallTreeNode.CallerTree(node));
|
||||
this.callerTreeCache.Add(node, callerTreeNode);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return callerTreeNode;
|
||||
}
|
||||
|
||||
var pathArr = path.Split('/');
|
||||
var pathNodeRoot = callerTreeNode.Children[int.Parse(pathArr[0])];
|
||||
|
||||
for (int i = 1; i < pathArr.Length; ++i)
|
||||
{
|
||||
pathNodeRoot = pathNodeRoot.Children[int.Parse(pathArr[i])];
|
||||
}
|
||||
|
||||
return pathNodeRoot;
|
||||
}
|
||||
}
|
||||
|
||||
public TreeNode GetCalleeTreeNode(string name, string path = "")
|
||||
{
|
||||
lock (this.lockobj)
|
||||
{
|
||||
CallTreeNodeBase node = this.GetNode(name).BackingNode;
|
||||
TreeNode calleeTreeNode;
|
||||
|
||||
if (this.calleeTreeCache.ContainsKey(node))
|
||||
{
|
||||
calleeTreeNode = this.calleeTreeCache[node];
|
||||
}
|
||||
else
|
||||
{
|
||||
calleeTreeNode = new TreeNode(AggregateCallTreeNode.CalleeTree(node));
|
||||
this.calleeTreeCache.Add(node, calleeTreeNode);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return calleeTreeNode;
|
||||
}
|
||||
|
||||
var pathArr = path.Split('/');
|
||||
var pathNodeRoot = calleeTreeNode.Children[int.Parse(pathArr[0])];
|
||||
|
||||
for (int i = 1; i < pathArr.Length; ++i)
|
||||
{
|
||||
pathNodeRoot = pathNodeRoot.Children[int.Parse(pathArr[i])];
|
||||
}
|
||||
|
||||
return pathNodeRoot;
|
||||
}
|
||||
}
|
||||
|
||||
public TreeNode[] GetCallerTree(string name)
|
||||
{
|
||||
return this.GetCallerTreeNode(name).Children;
|
||||
}
|
||||
|
||||
public TreeNode[] GetCallerTree(string name, string path)
|
||||
{
|
||||
return this.GetCallerTreeNode(name, path).Children;
|
||||
}
|
||||
|
||||
public TreeNode[] GetCalleeTree(string name)
|
||||
{
|
||||
return this.GetCalleeTreeNode(name).Children;
|
||||
}
|
||||
|
||||
public TreeNode[] GetCalleeTree(string name, string path)
|
||||
{
|
||||
return this.GetCalleeTreeNode(name, path).Children;
|
||||
}
|
||||
|
||||
public List<TreeNode> GetSummaryTree(int numNodes)
|
||||
{
|
||||
lock (this.lockobj)
|
||||
{
|
||||
if (this.summary == null)
|
||||
{
|
||||
var nodes = this.callTree.ByIDSortedExclusiveMetric().Where(this.summaryPredicate).Take(numNodes);
|
||||
|
||||
this.summary = new List<TreeNode>();
|
||||
foreach (CallTreeNodeBase node in nodes)
|
||||
{
|
||||
this.summary.Add(new TreeNode(node));
|
||||
}
|
||||
}
|
||||
|
||||
return this.summary;
|
||||
}
|
||||
}
|
||||
|
||||
public SourceInformation Source(TreeNode node)
|
||||
{
|
||||
SortedDictionary<int, float> metricOnLine;
|
||||
var sourceLocation = this.GetSourceLocation(node.BackingNode, node.BackingNode.Name, out metricOnLine);
|
||||
|
||||
if (sourceLocation != null)
|
||||
{
|
||||
var sourceFile = sourceLocation.SourceFile;
|
||||
FieldInfo fi = typeof(SourceFile).GetField("m_symbolModule", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (fi != null)
|
||||
{
|
||||
PropertyInfo pi = typeof(SymbolModule).GetProperty("PdbForSourceServer", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (pi != null)
|
||||
{
|
||||
MethodInfo mi = typeof(SymbolModule).GetMethod("GetSrcSrvStream", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (mi != null)
|
||||
{
|
||||
string srcStream = mi.Invoke(pi.GetValue(fi.GetValue(sourceFile)), null) as string;
|
||||
if (srcStream != null)
|
||||
{
|
||||
string[] lines = srcStream.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
// TFS DevDiv support
|
||||
if (line.StartsWith(sourceFile.BuildTimeFilePath))
|
||||
{
|
||||
var split = line.Split('*');
|
||||
|
||||
var sourceInfo = new SourceInformation
|
||||
{
|
||||
Url = "http://vstfdevdiv:8080/DevDiv2/DevDiv/_versionControl/changeset/" + split[3].Trim() + "#path=" + split[2].Trim() + "&_a=contents",
|
||||
Type = "Url",
|
||||
Summary = metricOnLine.Select(m => new LineInformation { Metric = m.Value, LineNumber = m.Key + 1, Line = string.Empty }).ToList()
|
||||
};
|
||||
|
||||
return sourceInfo;
|
||||
}
|
||||
}
|
||||
|
||||
// support for source depot?
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Source Database Format
|
||||
{
|
||||
var sdbFiles = Directory.GetFiles(this.reader.SourcePath, "*.sdb", SearchOption.AllDirectories);
|
||||
SourceInformation sourceInfo = null;
|
||||
|
||||
foreach (var sdbFile in sdbFiles)
|
||||
{
|
||||
using (ZipArchive archive = ZipFile.OpenRead(sdbFile))
|
||||
{
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
{
|
||||
if (sourceFile.BuildTimeFilePath.EndsWith(entry.FullName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
var summaryLineMetrics = new List<LineInformation>();
|
||||
var linemetrics = new List<LineInformation>();
|
||||
sourceInfo = new SourceInformation();
|
||||
sourceInfo.Lines = linemetrics;
|
||||
sourceInfo.Summary = summaryLineMetrics;
|
||||
sourceInfo.Type = "Lines";
|
||||
|
||||
using (var sr = new StreamReader(entry.Open()))
|
||||
{
|
||||
string line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
i++;
|
||||
float value = 0;
|
||||
if (metricOnLine.TryGetValue(i, out value))
|
||||
{
|
||||
var lineInfo = new LineInformation { LineNumber = i, Metric = value, Line = line };
|
||||
summaryLineMetrics.Add(lineInfo);
|
||||
linemetrics.Add(lineInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
linemetrics.Add(new LineInformation { LineNumber = i, Metric = value, Line = line });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sourceInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private SourceLocation GetSourceLocation(CallTreeNodeBase asCallTreeNodeBase, string cellText, out SortedDictionary<int, float> metricOnLine)
|
||||
{
|
||||
metricOnLine = null;
|
||||
var m = Regex.Match(cellText, "<<(.*!.*)>>");
|
||||
|
||||
if (m.Success)
|
||||
{
|
||||
cellText = m.Groups[1].Value;
|
||||
}
|
||||
|
||||
var frameIndexCounts = new Dictionary<StackSourceFrameIndex, float>();
|
||||
asCallTreeNodeBase.GetSamples(false, delegate (StackSourceSampleIndex sampleIdx)
|
||||
{
|
||||
var matchingFrameIndex = StackSourceFrameIndex.Invalid;
|
||||
var sample = this.stacksource.GetSampleByIndex(sampleIdx);
|
||||
var callStackIdx = sample.StackIndex;
|
||||
while (callStackIdx != StackSourceCallStackIndex.Invalid)
|
||||
{
|
||||
var frameIndex = this.stacksource.GetFrameIndex(callStackIdx);
|
||||
var frameName = this.stacksource.GetFrameName(frameIndex, false);
|
||||
if (frameName == cellText)
|
||||
matchingFrameIndex = frameIndex; // We keep overwriting it, so we get the entry closest to the root.
|
||||
callStackIdx = this.stacksource.GetCallerIndex(callStackIdx);
|
||||
}
|
||||
if (matchingFrameIndex != StackSourceFrameIndex.Invalid)
|
||||
{
|
||||
float count = 0;
|
||||
frameIndexCounts.TryGetValue(matchingFrameIndex, out count);
|
||||
frameIndexCounts[matchingFrameIndex] = count + sample.Metric;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
var maxFrameIdx = StackSourceFrameIndex.Invalid;
|
||||
float maxFrameIdxCount = -1;
|
||||
foreach (var keyValue in frameIndexCounts)
|
||||
{
|
||||
if (keyValue.Value >= maxFrameIdxCount)
|
||||
{
|
||||
maxFrameIdxCount = keyValue.Value;
|
||||
maxFrameIdx = keyValue.Key;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxFrameIdx == StackSourceFrameIndex.Invalid)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the most primitive TraceEventStackSource
|
||||
TraceEventStackSource asTraceEventStackSource = GetTraceEventStackSource(this.stacksource);
|
||||
if (asTraceEventStackSource == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var frameToLine = new Dictionary<StackSourceFrameIndex, int>();
|
||||
|
||||
var sourceLocation = asTraceEventStackSource.GetSourceLine(maxFrameIdx, this.reader);
|
||||
if (sourceLocation != null)
|
||||
{
|
||||
var filePathForMax = sourceLocation.SourceFile.BuildTimeFilePath;
|
||||
metricOnLine = new SortedDictionary<int, float>();
|
||||
|
||||
// Accumulate the counts on a line basis
|
||||
foreach (StackSourceFrameIndex frameIdx in frameIndexCounts.Keys)
|
||||
{
|
||||
var loc = asTraceEventStackSource.GetSourceLine(frameIdx, this.reader);
|
||||
if (loc != null && loc.SourceFile.BuildTimeFilePath == filePathForMax)
|
||||
{
|
||||
frameToLine[frameIdx] = loc.LineNumber;
|
||||
float metric;
|
||||
metricOnLine.TryGetValue(loc.LineNumber, out metric);
|
||||
metric += frameIndexCounts[frameIdx];
|
||||
metricOnLine[loc.LineNumber] = metric;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool commonMethodIdxSet = false;
|
||||
MethodIndex commonMethodIdx = MethodIndex.Invalid;
|
||||
|
||||
var nativeAddressFreq = new SortedDictionary<ulong, Tuple<int, float>>();
|
||||
foreach (var keyValue in frameIndexCounts)
|
||||
{
|
||||
var codeAddr = asTraceEventStackSource.GetFrameCodeAddress(keyValue.Key);
|
||||
if (codeAddr != CodeAddressIndex.Invalid)
|
||||
{
|
||||
var methodIdx = asTraceEventStackSource.TraceLog.CodeAddresses.MethodIndex(codeAddr);
|
||||
if (methodIdx != MethodIndex.Invalid)
|
||||
{
|
||||
if (!commonMethodIdxSet)
|
||||
commonMethodIdx = methodIdx; // First time, set it as the common method.
|
||||
else if (methodIdx != commonMethodIdx)
|
||||
methodIdx = MethodIndex.Invalid; // More than one method, give up.
|
||||
commonMethodIdxSet = true;
|
||||
}
|
||||
|
||||
var nativeAddr = asTraceEventStackSource.TraceLog.CodeAddresses.Address(codeAddr);
|
||||
var lineNum = 0;
|
||||
frameToLine.TryGetValue(keyValue.Key, out lineNum);
|
||||
nativeAddressFreq[nativeAddr] = new Tuple<int, float>(lineNum, keyValue.Value);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var keyValue in nativeAddressFreq)
|
||||
Console.WriteLine(" {0,12:x} : {1,6} {2,10:f1}", keyValue.Key, keyValue.Value.Item1, keyValue.Value.Item2);
|
||||
|
||||
if (sourceLocation == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var keyVal in metricOnLine)
|
||||
Console.WriteLine(" Line {0,5}: Metric {1,5:n1}", keyVal.Key, keyVal.Value);
|
||||
|
||||
return sourceLocation;
|
||||
}
|
||||
|
||||
internal static TraceEventStackSource GetTraceEventStackSource(StackSource source)
|
||||
{
|
||||
StackSourceStacks rawSource = source;
|
||||
for (;;)
|
||||
{
|
||||
var asTraceEventStackSource = rawSource as TraceEventStackSource;
|
||||
if (asTraceEventStackSource != null)
|
||||
{
|
||||
return asTraceEventStackSource;
|
||||
}
|
||||
|
||||
var asCopyStackSource = rawSource as CopyStackSource;
|
||||
if (asCopyStackSource != null)
|
||||
{
|
||||
rawSource = asCopyStackSource.SourceStacks;
|
||||
continue;
|
||||
}
|
||||
|
||||
var asStackSource = rawSource as StackSource;
|
||||
if (asStackSource != null && asStackSource != asStackSource.BaseStackSource)
|
||||
{
|
||||
rawSource = asStackSource.BaseStackSource;
|
||||
continue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
namespace TraceEventAPIServer
|
||||
{
|
||||
using System.Diagnostics.Tracing;
|
||||
|
||||
public sealed class CallTreeDataProviderEventSource : EventSource
|
||||
{
|
||||
public static CallTreeDataProviderEventSource Log = new CallTreeDataProviderEventSource();
|
||||
|
||||
public void NodeCacheHit(string node)
|
||||
{
|
||||
this.WriteEvent(1, node);
|
||||
}
|
||||
|
||||
public void NodeCacheMisss(string node)
|
||||
{
|
||||
this.WriteEvent(2, node);
|
||||
}
|
||||
|
||||
public void NodeCacheNotFound(string node)
|
||||
{
|
||||
this.WriteEvent(3, node);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
namespace TraceEventAPIServer
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using Diagnostics.Tracing.StackSources;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Diagnostics.Symbols;
|
||||
using Microsoft.Diagnostics.Tracing.Etlx;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
|
||||
public sealed class CallTreeDataProviderFactory : ICallTreeDataProviderFactory
|
||||
{
|
||||
private const string etlExtension = ".etl";
|
||||
|
||||
private const string etlxExtension = ".etlx";
|
||||
|
||||
private readonly HttpRequest httpRequest;
|
||||
|
||||
private readonly TextWriter textWriter;
|
||||
|
||||
private readonly EtlxCache etlxCache;
|
||||
|
||||
private readonly StackViewerSessionCache stackViewerSessionCache;
|
||||
|
||||
private readonly DateTimeOffset cacheExpirationTime;
|
||||
|
||||
private readonly string tempPath;
|
||||
|
||||
public CallTreeDataProviderFactory(IHttpContextAccessor httpContextAccessor, ITemporaryPathProvider temporaryPathProvider, ICacheExpirationTimeProvider cacheExpirationTimeProvider, TextWriter textWriter, EtlxCache etlxCache, StackViewerSessionCache stackViewerSessionCache)
|
||||
{
|
||||
if (httpContextAccessor == null)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullException(nameof(httpContextAccessor));
|
||||
}
|
||||
|
||||
if (temporaryPathProvider == null)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullException(nameof(temporaryPathProvider));
|
||||
}
|
||||
|
||||
if (cacheExpirationTimeProvider == null)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullException(nameof(cacheExpirationTimeProvider));
|
||||
}
|
||||
|
||||
if (textWriter == null)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullException(nameof(textWriter));
|
||||
}
|
||||
|
||||
if (etlxCache == null)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullException(nameof(etlxCache));
|
||||
}
|
||||
|
||||
if (stackViewerSessionCache == null)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullException(nameof(stackViewerSessionCache));
|
||||
}
|
||||
|
||||
this.httpRequest = httpContextAccessor.HttpContext.Request;
|
||||
this.textWriter = textWriter;
|
||||
this.tempPath = temporaryPathProvider.Path;
|
||||
this.cacheExpirationTime = cacheExpirationTimeProvider.Expiration;
|
||||
this.etlxCache = etlxCache;
|
||||
this.stackViewerSessionCache = stackViewerSessionCache;
|
||||
}
|
||||
|
||||
public ICallTreeDataProvider Get()
|
||||
{
|
||||
var queryString = this.httpRequest.Query;
|
||||
|
||||
string filename = queryString["filename"];
|
||||
string stacktype = queryString["stacktype"];
|
||||
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
{
|
||||
throw new ArgumentNullException("filename");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(stacktype))
|
||||
{
|
||||
throw new ArgumentNullException("stacktype");
|
||||
}
|
||||
|
||||
/* symbols and sources related parameters */
|
||||
string sympath = (string)queryString["sympath"] ?? string.Empty;
|
||||
string srcpath = (string)queryString["srcpath"] ?? string.Empty;
|
||||
string imageFilter = (string)queryString["imagefilter"] ?? string.Empty;
|
||||
string modulesFilter = (string)queryString["modulesfilter"] ?? string.Empty;
|
||||
|
||||
/* filtering parameters */
|
||||
string start = (string)queryString["start"] ?? string.Empty;
|
||||
string end = (string)queryString["end"] ?? string.Empty;
|
||||
string incpats = (string)queryString["incpats"] ?? string.Empty;
|
||||
string excpats = (string)queryString["excpats"] ?? string.Empty;
|
||||
string foldpats = (string)queryString["foldpats"] ?? string.Empty;
|
||||
string grouppats = (string)queryString["grouppats"] ?? string.Empty;
|
||||
string foldpct = (string)queryString["foldpct"] ?? string.Empty;
|
||||
|
||||
EtlxFile etlxFile;
|
||||
|
||||
string etlxFilePath = Path.Combine(tempPath, Path.ChangeExtension(filename.Replace(@"\", "_").Replace(@":", "_").Replace(@"/", "_"), etlxExtension));
|
||||
SymbolReader symbolReader;
|
||||
|
||||
lock (this.etlxCache)
|
||||
{
|
||||
if (this.etlxCache.TryGetValue(filename, out etlxFile))
|
||||
{
|
||||
if (etlxFile == null)
|
||||
{
|
||||
throw new ArgumentNullException("etlxFile");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
etlxFile = new EtlxFile(filename) { Pending = true };
|
||||
this.etlxCache.Set(filename, etlxFile, this.cacheExpirationTime);
|
||||
}
|
||||
}
|
||||
|
||||
lock (etlxFile)
|
||||
{
|
||||
if (etlxFile.Pending)
|
||||
{
|
||||
if (!File.Exists(etlxFilePath))
|
||||
{
|
||||
// if it's a zip file
|
||||
if (string.Equals(Path.GetExtension(filename), ".vspx", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
using (ZipArchive archive = ZipFile.OpenRead(filename))
|
||||
{
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
{
|
||||
if (entry.FullName.EndsWith(etlExtension, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
entry.ExtractToFile(Path.ChangeExtension(etlxFilePath, etlExtension));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TraceLog.CreateFromEventTraceLogFile(Path.ChangeExtension(etlxFilePath, etlExtension), etlxFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceLog.CreateFromEventTraceLogFile(filename, etlxFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
etlxFile.TraceLog = TraceLog.OpenOrConvert(etlxFilePath);
|
||||
etlxFile.Pending = false;
|
||||
var processes = etlxFile.TraceLog.Processes;
|
||||
|
||||
symbolReader = new SymbolReader(this.textWriter, sympath) { SourcePath = srcpath };
|
||||
|
||||
foreach (var process in processes)
|
||||
{
|
||||
if (string.IsNullOrEmpty(imageFilter) || string.Equals(process.ImageFileName, imageFilter, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
foreach (var module in process.LoadedModules)
|
||||
{
|
||||
if (string.IsNullOrEmpty(modulesFilter) || modulesFilter.Split(',').Contains(module.Name))
|
||||
{
|
||||
etlxFile.TraceLog.CodeAddresses.LookupSymbolsForModule(symbolReader, module.ModuleFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StackViewerSession stackViewerSession;
|
||||
lock (this.stackViewerSessionCache)
|
||||
{
|
||||
var filterParams = new FilterParams { Name = filename + stacktype, StartTimeRelativeMSec = start, EndTimeRelativeMSec = end, MinInclusiveTimePercent = foldpct, FoldRegExs = foldpats, IncludeRegExs = incpats, ExcludeRegExs = excpats, GroupRegExs = grouppats };
|
||||
|
||||
var stackViewerKey = filterParams.ToString();
|
||||
if (this.stackViewerSessionCache.TryGetValue(stackViewerKey, out stackViewerSession))
|
||||
{
|
||||
if (stackViewerSession == null)
|
||||
{
|
||||
throw new ArgumentNullException("stackViewerSession");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var processes = etlxFile.TraceLog.Processes;
|
||||
symbolReader = new SymbolReader(this.textWriter, sympath) { SourcePath = srcpath };
|
||||
|
||||
foreach (var process in processes)
|
||||
{
|
||||
if (string.IsNullOrEmpty(imageFilter) || string.Equals(process.ImageFileName, imageFilter, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
foreach (var module in process.LoadedModules)
|
||||
{
|
||||
if (string.IsNullOrEmpty(modulesFilter) || modulesFilter.Split(',').Contains(module.Name))
|
||||
{
|
||||
etlxFile.TraceLog.CodeAddresses.LookupSymbolsForModule(symbolReader, module.ModuleFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stackViewerSession = new StackViewerSession(filename, stacktype, etlxFile.TraceLog, filterParams, symbolReader);
|
||||
this.stackViewerSessionCache.Set(stackViewerKey, stackViewerSession, cacheExpirationTime);
|
||||
}
|
||||
}
|
||||
|
||||
lock (stackViewerSession)
|
||||
{
|
||||
if (stackViewerSession.Pending)
|
||||
{
|
||||
stackViewerSession.InitializeDataProvider();
|
||||
stackViewerSession.Pending = false;
|
||||
}
|
||||
}
|
||||
|
||||
return stackViewerSession.GetDataProvider();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BPerfViewer.Controllers
|
||||
{
|
||||
public class HomeController : Controller
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
this.Response.Headers["Connection"] = "close";
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult About()
|
||||
{
|
||||
ViewData["Message"] = "Your application description page.";
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult Contact()
|
||||
{
|
||||
ViewData["Message"] = "Your contact page.";
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
namespace TraceEventAPIServer.Controllers
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Models;
|
||||
|
||||
public sealed class StackViewerController
|
||||
{
|
||||
private readonly ICallTreeDataProvider dataProvider;
|
||||
|
||||
public StackViewerController(ICallTreeDataProviderFactory dataProviderFactory)
|
||||
{
|
||||
if (dataProviderFactory == null)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullException(nameof(dataProviderFactory));
|
||||
}
|
||||
|
||||
this.dataProvider = dataProviderFactory.Get();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("stackviewer/summary")]
|
||||
public List<TreeNode> Get(int numNodes)
|
||||
{
|
||||
return this.dataProvider.GetSummaryTree(numNodes);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("stackviewer/node")]
|
||||
public TreeNode Node(string name)
|
||||
{
|
||||
return this.dataProvider.GetNode(name);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("stackviewer/callertree")]
|
||||
public TreeNode[] CallerTree(string name)
|
||||
{
|
||||
return this.dataProvider.GetCallerTree(name);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("stackviewer/callertree")]
|
||||
public TreeNode[] CallerTree(string name, string path)
|
||||
{
|
||||
return this.dataProvider.GetCallerTree(name, path);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("stackviewer/calleetree")]
|
||||
public TreeNode[] CalleeTree(string name)
|
||||
{
|
||||
return this.dataProvider.GetCalleeTree(name);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("stackviewer/calleetree")]
|
||||
public TreeNode[] CalleeTree(string name, string path)
|
||||
{
|
||||
return this.dataProvider.GetCalleeTree(name, path);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("stackviewer/source")]
|
||||
public SourceInformation Source(string name)
|
||||
{
|
||||
return this.dataProvider.Source(this.dataProvider.GetNode(name));
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("stackviewer/source/caller")]
|
||||
public SourceInformation CallerContextSource(string name, string path)
|
||||
{
|
||||
return this.dataProvider.Source(this.dataProvider.GetCallerTreeNode(name, path));
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("stackviewer/source/callee")]
|
||||
public SourceInformation CalleeContextSource(string name, string path)
|
||||
{
|
||||
return this.dataProvider.Source(this.dataProvider.GetCalleeTreeNode(name, path));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
namespace TraceEventAPIServer.Controllers
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using TraceEventAPIServer.Models;
|
||||
using System.Net.Http;
|
||||
|
||||
public sealed class StackViewerUIController : Controller
|
||||
{
|
||||
private readonly IServerAddressesFeature serverAddressesFeature;
|
||||
|
||||
public StackViewerUIController(IServerAddressesFeature serverAddressesFeature)
|
||||
{
|
||||
this.serverAddressesFeature = serverAddressesFeature;
|
||||
}
|
||||
|
||||
[Route("ui/stackviewer/summary", Name = "HotSpots")]
|
||||
public ActionResult Hotspots(StackViewerViewModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Filename))
|
||||
{
|
||||
throw new ArgumentNullException("filename");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(model.StackType))
|
||||
{
|
||||
throw new ArgumentNullException("stacktype");
|
||||
}
|
||||
|
||||
model.TreeNodes = JsonConvert.DeserializeObject<List<TreeNode>>(new HttpClient().GetStringAsync($"{this.LookupHostname()}/stackviewer/summary?{model}&numNodes=100").Result);
|
||||
|
||||
this.ViewBag.Title = "Hotspots Viewer";
|
||||
return this.View(model);
|
||||
}
|
||||
|
||||
[Route("ui/stackviewer/callertree", Name = "Callers", Order = 2)]
|
||||
public ActionResult Callers(CallersViewStackViewerViewModel model, string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Filename))
|
||||
{
|
||||
throw new ArgumentNullException("filename");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(model.StackType))
|
||||
{
|
||||
throw new ArgumentNullException("stacktype");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
if (name.Contains("&"))
|
||||
{
|
||||
name = name.Replace("&", "%26");
|
||||
}
|
||||
|
||||
this.ViewBag.Title = "Callers Viewer";
|
||||
|
||||
string key = this.LookupHostname();
|
||||
model.Node = JsonConvert.DeserializeObject<TreeNode>(new HttpClient().GetStringAsync($"{key}/stackviewer/node?{model}&name={name}").Result);
|
||||
model.TreeNodes = JsonConvert.DeserializeObject<List<TreeNode>>(new HttpClient().GetStringAsync($"{key}/stackviewer/callertree?{model}&name={name}").Result);
|
||||
|
||||
return this.View(model);
|
||||
}
|
||||
|
||||
[Route("ui/stackviewer/callertree/children", Name = "CallersChildren", Order = 1)]
|
||||
public ActionResult CallersChildren(CallersViewStackViewerViewModel model, string name, string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Filename))
|
||||
{
|
||||
throw new ArgumentNullException("filename");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(model.StackType))
|
||||
{
|
||||
throw new ArgumentNullException("stacktype");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
if (name.Contains("&"))
|
||||
{
|
||||
name = name.Replace("&", "%26");
|
||||
}
|
||||
|
||||
model.TreeNodes = JsonConvert.DeserializeObject<List<TreeNode>>(new HttpClient().GetStringAsync($"{this.LookupHostname()}/stackviewer/callertree?{model}&name={name}&path={path}").Result);
|
||||
|
||||
this.ViewBag.Title = "Callers Viewer";
|
||||
return this.View(model);
|
||||
}
|
||||
|
||||
[Route("ui/stackviewer/source/callertree", Name = "SourceViewer")]
|
||||
public ActionResult SourceViewer(CallersViewStackViewerViewModel model, string name, string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model.Filename))
|
||||
{
|
||||
throw new ArgumentNullException("filename");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(model.StackType))
|
||||
{
|
||||
throw new ArgumentNullException("stacktype");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
if (name.Contains("&"))
|
||||
{
|
||||
name = name.Replace("&", "%26");
|
||||
}
|
||||
|
||||
return this.View(JsonConvert.DeserializeObject<SourceInformation>(new HttpClient().GetStringAsync($"{this.LookupHostname()}/stackviewer/source/callertree?{model}&name={name}&path={path}").Result));
|
||||
}
|
||||
|
||||
private string LookupHostname()
|
||||
{
|
||||
return this.serverAddressesFeature.Addresses.First();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
namespace TraceEventAPIServer
|
||||
{
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
public sealed class EtlxCache : MemoryCache
|
||||
{
|
||||
public EtlxCache(IOptions<MemoryCacheOptions> optionsAccessor)
|
||||
: base(optionsAccessor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
namespace TraceEventAPIServer
|
||||
{
|
||||
using Microsoft.Diagnostics.Tracing.Etlx;
|
||||
|
||||
public sealed class EtlxFile
|
||||
{
|
||||
public EtlxFile(string filename)
|
||||
{
|
||||
this.FileName = filename;
|
||||
}
|
||||
|
||||
public string FileName { get; private set; }
|
||||
|
||||
public bool Pending { get; set; }
|
||||
|
||||
public TraceLog TraceLog { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
namespace TraceEventAPIServer
|
||||
{
|
||||
using System.Diagnostics.Tracing;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
public sealed class EventSourceTextWriter : TextWriter
|
||||
{
|
||||
private static readonly TextWriterEventSource textWriterEventSource = new TextWriterEventSource();
|
||||
|
||||
public override void Write(string value)
|
||||
{
|
||||
textWriterEventSource.WriteMessage(value);
|
||||
}
|
||||
|
||||
public override void Write(string format, object arg0)
|
||||
{
|
||||
textWriterEventSource.WriteMessage(string.Format(format, arg0));
|
||||
}
|
||||
|
||||
public override void Write(string format, object arg0, object arg1)
|
||||
{
|
||||
textWriterEventSource.WriteMessage(string.Format(format, arg0, arg1));
|
||||
}
|
||||
|
||||
public override void Write(string format, object arg0, object arg1, object arg2)
|
||||
{
|
||||
textWriterEventSource.WriteMessage(string.Format(format, arg0, arg1, arg2));
|
||||
}
|
||||
|
||||
public override void Write(string format, params object[] arg)
|
||||
{
|
||||
textWriterEventSource.WriteMessage(string.Format(format, arg));
|
||||
}
|
||||
|
||||
public override void Write(char value)
|
||||
{
|
||||
textWriterEventSource.WriteMessage(value.ToString());
|
||||
}
|
||||
|
||||
public override void WriteLine()
|
||||
{
|
||||
textWriterEventSource.WriteMessage(string.Empty);
|
||||
}
|
||||
|
||||
public override void WriteLine(string value)
|
||||
{
|
||||
textWriterEventSource.WriteLine(value);
|
||||
}
|
||||
|
||||
public override void WriteLine(string format, object arg0)
|
||||
{
|
||||
textWriterEventSource.WriteLine(string.Format(format, arg0));
|
||||
}
|
||||
|
||||
public override void WriteLine(string format, object arg0, object arg1)
|
||||
{
|
||||
textWriterEventSource.WriteLine(string.Format(format, arg0, arg1));
|
||||
}
|
||||
|
||||
public override void WriteLine(string format, object arg0, object arg1, object arg2)
|
||||
{
|
||||
textWriterEventSource.WriteLine(string.Format(format, arg0, arg1, arg2));
|
||||
}
|
||||
|
||||
public override void WriteLine(string format, params object[] arg)
|
||||
{
|
||||
textWriterEventSource.WriteLine(string.Format(format, arg));
|
||||
}
|
||||
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
|
||||
private class TextWriterEventSource : EventSource
|
||||
{
|
||||
public void WriteMessage(string message)
|
||||
{
|
||||
this.WriteEvent(1, message);
|
||||
}
|
||||
|
||||
public void WriteLine(string message)
|
||||
{
|
||||
this.WriteEvent(2, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
namespace TraceEventAPIServer.Extensions
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
|
||||
public static class HtmlExtensions
|
||||
{
|
||||
public static string Json(this IHtmlHelper html, object obj)
|
||||
{
|
||||
return Newtonsoft.Json.JsonConvert.SerializeObject(obj);
|
||||
}
|
||||
|
||||
public static HtmlString MyRouteLink(this IHtmlHelper htmlHelper, string linkText, string routeName, object routeValues, string[] excludeUrlKeys)
|
||||
{
|
||||
UrlHelper urlHelper = new UrlHelper(htmlHelper.ViewContext);
|
||||
var request = htmlHelper.ViewContext.HttpContext.Request;
|
||||
|
||||
UriBuilder uriBuilder = new UriBuilder(urlHelper.RouteUrl(routeName, routeValues, request.Scheme));
|
||||
NameValueCollection nameValueCollection1 = new NameValueCollection();
|
||||
|
||||
foreach (string index in request.Query.Keys)
|
||||
{
|
||||
if (!excludeUrlKeys.Contains(index))
|
||||
{
|
||||
nameValueCollection1[index] = request.Query[index];
|
||||
}
|
||||
}
|
||||
|
||||
var nameValueCollection2 = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uriBuilder.Query);
|
||||
foreach (string index in nameValueCollection2.Keys)
|
||||
{
|
||||
nameValueCollection1[index] = nameValueCollection2[index];
|
||||
}
|
||||
|
||||
uriBuilder.Query = nameValueCollection1.ToString();
|
||||
|
||||
TagBuilder tagBuilder = new TagBuilder("a");
|
||||
tagBuilder.Attributes.Add(new System.Collections.Generic.KeyValuePair<string, string>("href", uriBuilder.ToString()));
|
||||
tagBuilder.InnerHtml.SetContent(linkText);
|
||||
return new HtmlString(tagBuilder.ToString());
|
||||
}
|
||||
|
||||
public static HtmlString MyRouteLinkTargetTab(this IHtmlHelper htmlHelper, string linkText, string routeName, object routeValues, string[] excludeUrlKeys)
|
||||
{
|
||||
UrlHelper urlHelper = new UrlHelper(htmlHelper.ViewContext);
|
||||
var request = htmlHelper.ViewContext.HttpContext.Request;
|
||||
|
||||
UriBuilder uriBuilder = new UriBuilder(urlHelper.RouteUrl(routeName, routeValues, request.Scheme));
|
||||
NameValueCollection nameValueCollection1 = new NameValueCollection();
|
||||
|
||||
foreach (string index in request.Query.Keys)
|
||||
{
|
||||
if (!excludeUrlKeys.Contains(index))
|
||||
{
|
||||
nameValueCollection1[index] = request.Query[index];
|
||||
}
|
||||
}
|
||||
|
||||
var nameValueCollection2 = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uriBuilder.Query);
|
||||
foreach (string index in nameValueCollection2.Keys)
|
||||
{
|
||||
nameValueCollection1[index] = nameValueCollection2[index];
|
||||
}
|
||||
|
||||
uriBuilder.Query = nameValueCollection1.ToString();
|
||||
|
||||
TagBuilder tagBuilder = new TagBuilder("a");
|
||||
tagBuilder.Attributes.Add(new System.Collections.Generic.KeyValuePair<string, string>("href", uriBuilder.ToString()));
|
||||
tagBuilder.Attributes.Add(new System.Collections.Generic.KeyValuePair<string, string>("target", "_blank"));
|
||||
tagBuilder.InnerHtml.SetContent(linkText);
|
||||
return new HtmlString(tagBuilder.ToString());
|
||||
}
|
||||
|
||||
public static string MyRouteLinkAjax(this IHtmlHelper htmlHelper)
|
||||
{
|
||||
return htmlHelper.ViewContext.HttpContext.Request.QueryString.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace BPerfViewer
|
||||
{
|
||||
using System;
|
||||
|
||||
public class HttpParserException : Exception
|
||||
{
|
||||
public HttpParserException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace TraceEventAPIServer
|
||||
{
|
||||
using System;
|
||||
|
||||
public interface ICacheExpirationTimeProvider
|
||||
{
|
||||
DateTimeOffset Expiration { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
namespace TraceEventAPIServer
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Models;
|
||||
|
||||
public interface ICallTreeDataProvider
|
||||
{
|
||||
// gets a node without a context
|
||||
TreeNode GetNode(string name);
|
||||
|
||||
// gets a node with a caller tree context and looks up the path
|
||||
TreeNode GetCallerTreeNode(string name, string path = "");
|
||||
|
||||
// gets a node with a callee tree context and looks up the path
|
||||
TreeNode GetCalleeTreeNode(string name, string path = "");
|
||||
|
||||
// gets a list of nodes with no context
|
||||
List<TreeNode> GetSummaryTree(int numNodes);
|
||||
|
||||
// returns a flat caller tree given a node
|
||||
TreeNode[] GetCallerTree(string name);
|
||||
|
||||
// returns a flat caller tree given a node and its context
|
||||
TreeNode[] GetCallerTree(string name, string path);
|
||||
|
||||
// returns a flat callee tree given a node
|
||||
TreeNode[] GetCalleeTree(string name);
|
||||
|
||||
// returns a flat callee tree given a node and its context
|
||||
TreeNode[] GetCalleeTree(string name, string path);
|
||||
|
||||
SourceInformation Source(TreeNode node);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace TraceEventAPIServer
|
||||
{
|
||||
public interface ICallTreeDataProviderFactory
|
||||
{
|
||||
ICallTreeDataProvider Get();
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче