This commit is contained in:
Mukul Sabharwal 2017-01-18 09:36:16 -08:00
Коммит 78be480e8c
238 изменённых файлов: 245256 добавлений и 0 удалений

258
.gitignore поставляемый Normal file
Просмотреть файл

@ -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(&timestamp);
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, &timestamp, 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

102
CoreCLRProfiler/README.md Normal file
Просмотреть файл

@ -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
```

24
CoreCLRProfiler/build.sh Normal file
Просмотреть файл

@ -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();
};

197895
CoreCLRProfiler/src/sqlite3.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

10342
CoreCLRProfiler/src/sqlite3.h Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

21
LICENSE Normal file
Просмотреть файл

@ -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.

6
README.md Normal file
Просмотреть файл

@ -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.

3
WebViewer/.bowerrc Normal file
Просмотреть файл

@ -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();
}
}
}

13
WebViewer/EtlxCache.cs Normal file
Просмотреть файл

@ -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)
{
}
}
}

18
WebViewer/EtlxFile.cs Normal file
Просмотреть файл

@ -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();
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше