diff --git a/.gitignore b/.gitignore index 3d5294a..6bf45f9 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,8 @@ # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs +build/ + # Mono auto generated files mono_crash.* diff --git a/dotnetInsights/src/DotnetInsightsRuntimeLoadEventsEditor.ts b/dotnetInsights/src/DotnetInsightsRuntimeLoadEventsEditor.ts index 72cc2ac..32fba43 100644 --- a/dotnetInsights/src/DotnetInsightsRuntimeLoadEventsEditor.ts +++ b/dotnetInsights/src/DotnetInsightsRuntimeLoadEventsEditor.ts @@ -8,7 +8,7 @@ import { JitMethodInfo, ProcessInfo, GcListener } from "./GcListener"; import { DotnetInsightsGcDocument } from "./DotnetInsightsGcEditor"; -export class DotnetInsightsRuntimeLoadEventsEditor implements vscode.CustomReadonlyEditorProvider { +export class DotnetInsightsRuntimeLoadEventsEditor implements vscode.CustomEditorProvider { public static register(context: vscode.ExtensionContext, insights: DotnetInsights): vscode.Disposable { const provider = new DotnetInsightsRuntimeLoadEventsEditor(context, insights, null, insights.listener!); const providerRegistration = vscode.window.registerCustomEditorProvider(DotnetInsightsRuntimeLoadEventsEditor.viewType, provider); @@ -34,6 +34,23 @@ export class DotnetInsightsRuntimeLoadEventsEditor implements vscode.CustomReado this.processName = ""; } + onDidChangeCustomDocument() : any { + + } + + saveCustomDocument(document: vscode.CustomDocument, cancellation: vscode.CancellationToken): Thenable { + throw new Error("Method not implemented."); + } + saveCustomDocumentAs(document: vscode.CustomDocument, destination: vscode.Uri, cancellation: vscode.CancellationToken): Thenable { + throw new Error("Method not implemented."); + } + revertCustomDocument(document: vscode.CustomDocument, cancellation: vscode.CancellationToken): Thenable { + throw new Error("Method not implemented."); + } + backupCustomDocument(document: vscode.CustomDocument, context: vscode.CustomDocumentBackupContext, cancellation: vscode.CancellationToken): Thenable { + throw new Error("Method not implemented."); + } + openCustomDocument(uri: vscode.Uri, openContext: vscode.CustomDocumentOpenContext, token: vscode.CancellationToken): vscode.CustomDocument | Thenable { var filename = path.basename(uri.path); var endofLine = os.platform() === "win32" ? vscode.EndOfLine.CRLF : vscode.EndOfLine.LF; @@ -639,7 +656,7 @@ export class DotnetInsightsRuntimeLoadEventsEditor implements vscode.CustomReado var evenOdd = index % 2 == 0 ? "even" : "odd"; - data += `${dataAtIndex.methodName}${dataAtIndex.loadDuration}`; + data += `${dataAtIndex.methodId}${dataAtIndex.methodName}${dataAtIndex.loadDuration}`; } data += ``; @@ -669,7 +686,7 @@ export class DotnetInsightsRuntimeLoadEventsEditor implements vscode.CustomReado var dataAtIndex : JitMethodInfo = tierZeroLoadTimes[index]; var evenOdd = index % 2 == 0 ? "even" : "odd"; - data += `${dataAtIndex.methodName}${dataAtIndex.loadDuration}`; + data += `${dataAtIndex.methodId}${dataAtIndex.methodName}${dataAtIndex.loadDuration}`; } data += ``; @@ -699,7 +716,7 @@ export class DotnetInsightsRuntimeLoadEventsEditor implements vscode.CustomReado var dataAtIndex : JitMethodInfo = tierOneLoadTimes[index]; var evenOdd = index % 2 == 0 ? "even" : "odd"; - data += `${dataAtIndex.methodName}${dataAtIndex.loadDuration}`; + data += `${dataAtIndex.methodId}${dataAtIndex.methodName}${dataAtIndex.loadDuration}`; } data += ``; diff --git a/dotnetInsights/src/GcListener.ts b/dotnetInsights/src/GcListener.ts index a7650e7..62bd67e 100644 --- a/dotnetInsights/src/GcListener.ts +++ b/dotnetInsights/src/GcListener.ts @@ -3,6 +3,7 @@ import { DotnetInsightsGcTreeDataProvider } from "./dotnetInsightsGc"; import { createServer } from "http"; import { IncomingMessage, ServerResponse } from 'http'; import { DotnetInsightsJitTreeDataProvider } from "./dotnetInsightsJit"; +import { Profiler } from "./Profiler"; export class GcData { public data: any; @@ -178,6 +179,8 @@ export class GcListener { public sendShutdown: boolean; + public profiler: Profiler | undefined; + public requests: number; public secondTimer: any; @@ -194,6 +197,8 @@ export class GcListener { console.log(`Requests per second: ${this.requests}`); this.requests = 0; }, 1000); + + this.profiler = Profiler.getInstance(10); } start() { @@ -218,6 +223,7 @@ export class GcListener { } catch(e) { response.end("eol"); + return; } if (jsonData == undefined) { @@ -228,6 +234,17 @@ export class GcListener { console.log(request.url); const isAllocData = request.url == "/gcAllocation"; const isJitEvent = request.url == "/jitEvent"; + const isProfilerEvent = request.url == "/profiler"; + + if (isProfilerEvent) { + const profilerData = jsonData; + console.assert(this.profiler != undefined); + + this.profiler?.addData(profilerData); + + response.end("eol"); + return; + } var processById: ProcessInfo | undefined = this.processes.get(jsonData["ProcessID"]); diff --git a/dotnetInsights/src/Profiler.ts b/dotnetInsights/src/Profiler.ts new file mode 100644 index 0000000..88f33d0 --- /dev/null +++ b/dotnetInsights/src/Profiler.ts @@ -0,0 +1,88 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: Profiler.ts +//////////////////////////////////////////////////////////////////////////////// + +import { time } from "console"; + +export class ProfilerEvent { + //////////////////////////////////////////////////////////////////////////// + // Member variables + //////////////////////////////////////////////////////////////////////////// + + private methodName: string; + private methodId: number; + private timeInMethod: number; + private timestamp: number; + + //////////////////////////////////////////////////////////////////////////// + // Constructor + //////////////////////////////////////////////////////////////////////////// + + constructor(methodName: string, methodId: number, timeInMethod: number) { + this.methodName = methodName; + this.methodId = methodId; + this.timeInMethod = timeInMethod; + this.timestamp = Date.now(); + } +} + +export class Profiler { + //////////////////////////////////////////////////////////////////////////// + // Member variables + //////////////////////////////////////////////////////////////////////////// + + private static instance: Profiler | undefined | null; + private pid: number; + private data: ProfilerEvent[]; + + private methodMap: Map>; + + //////////////////////////////////////////////////////////////////////////// + // Constructor + //////////////////////////////////////////////////////////////////////////// + + private constructor(pid: number) { + this.data = [] as ProfilerEvent[]; + this.methodMap = new Map>(); + + this.pid = pid; + } + + //////////////////////////////////////////////////////////////////////////// + // Member methods + //////////////////////////////////////////////////////////////////////////// + + public addData(jsonData: any) { + const methodName = jsonData["methodName"]; + const methodId = jsonData["id"]; + const timeInMethod = jsonData["time"]; + + const profilerEvent = new ProfilerEvent(methodName, methodId, timeInMethod); + this.data.push(profilerEvent); + + var methodByProfilerFunctionId: Map | undefined = undefined; + if (this.methodMap.has(methodName)) + { + methodByProfilerFunctionId = this.methodMap.get(methodName); + } + else { + methodByProfilerFunctionId = new Map(); + } + + if (methodByProfilerFunctionId?.has(methodId)) { + const previousValue = methodByProfilerFunctionId.get(methodId); + methodByProfilerFunctionId.set(methodId, previousValue + timeInMethod); + } + else { + methodByProfilerFunctionId?.set(methodId, timeInMethod); + } + } + + public static getInstance(pid: number): Profiler { + if (Profiler.instance == null || Profiler.instance == undefined || Profiler.instance.pid !== pid) { + Profiler.instance = new Profiler(pid); + } + + return Profiler.instance; + } +} \ No newline at end of file diff --git a/profiler/.vscode/launch.json b/profiler/.vscode/launch.json new file mode 100644 index 0000000..a98e01c --- /dev/null +++ b/profiler/.vscode/launch.json @@ -0,0 +1,82 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "native_profiler", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceRoot}\\..\\..\\runtime\\artifacts\\tests\\coreclr\\windows.x64.Debug\\Tests\\Core_Root\\CoreRun.exe", + "args": [ + "${workspaceRoot}\\tests\\bringup\\jit_compilation\\bin\\Debug\\net6.0\\jit_compilation.dll" + ], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [ + { + "name": "CORECLR_PROFILER_PATH", + "value": "${workspaceRoot}\\native\\build\\Debug\\ev31_profiler.dll" + }, + { + "name": "CORECLR_PROFILER", + "value": "{cf0d821e-299b-5307-a3d8-b283c03916dd}" + }, + { + "name": "CORECLR_ENABLE_PROFILING", + "value": "1" + } + ], + "console": "internalConsole" + }, + { + "name": "(lldb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "/Users/jashoo/Library/Application Support/Code/User/globalStorage/jashoo.dotnetinsights/coreRoot/net5.0/Core_Root/corerun", + "args": [ + "/Users/jashoo/projects/temp/bin/Release/net5.0/temp.dll" + ], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [ + { + "name": "CORECLR_PROFILER_PATH", + "value": "/Users/jashoo/projects/vscode-dotnet-insights/profiler/native/build/libev31_profiler.dylib" + }, + { + "name": "CORECLR_PROFILER", + "value": "{cf0d821e-299b-5307-a3d8-b283c03916dd}" + }, + { + "name": "CORECLR_ENABLE_PROFILING", + "value": "1" + } + ], + "externalConsole": false, + "MIMode": "lldb" + }, + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/managed/bin/Debug/net5.0/profiler.dll", + "args": [ + "-pid", + "88850" + ], + "cwd": "${workspaceFolder}", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/profiler/.vscode/settings.json b/profiler/.vscode/settings.json new file mode 100644 index 0000000..2c78afd --- /dev/null +++ b/profiler/.vscode/settings.json @@ -0,0 +1,75 @@ +{ + "cmake.sourceDirectory": "${workspaceFolder}/native", + "files.associations": { + "atomic": "cpp", + "algorithm": "cpp", + "array": "cpp", + "bit": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "exception": "cpp", + "format": "cpp", + "forward_list": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "memory": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "utility": "cpp", + "vector": "cpp", + "xfacet": "cpp", + "xhash": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xstddef": "cpp", + "xstring": "cpp", + "xtr1common": "cpp", + "xtree": "cpp", + "xutility": "cpp", + "functional": "cpp", + "codecvt": "cpp", + "complex": "cpp", + "mutex": "cpp", + "stop_token": "cpp", + "thread": "cpp" + } +} \ No newline at end of file diff --git a/profiler/.vscode/tasks.json b/profiler/.vscode/tasks.json new file mode 100644 index 0000000..72e5279 --- /dev/null +++ b/profiler/.vscode/tasks.json @@ -0,0 +1,42 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/managed/profiler.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile", + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/profiler.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "${workspaceFolder}/profiler.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/profiler/docs/background.md b/profiler/docs/background.md new file mode 100644 index 0000000..0302b94 --- /dev/null +++ b/profiler/docs/background.md @@ -0,0 +1 @@ +# Unknown \ No newline at end of file diff --git a/profiler/managed/Profiler.cs b/profiler/managed/Profiler.cs new file mode 100644 index 0000000..0148769 --- /dev/null +++ b/profiler/managed/Profiler.cs @@ -0,0 +1,93 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: Profiler.cs +// +// Notes: +// Setup a profiler for the process id passed +//////////////////////////////////////////////////////////////////////////////// + +namespace ev31 { + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Invocation; +using System.Diagnostics; +using System.Threading.Tasks; + +using Microsoft.Diagnostics.NETCore.Client; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +public class Profiler +{ + public static async Task Main(string[] args) + { + var rootCommand = new RootCommand + { + new Option( + "-pid", + description: "pid for the process to profile") + }; + + rootCommand.Description = ".NET Insights Profiler"; + + int processId = 0; + + // Note that the parameters of the handler method are matched according to the names of the options + rootCommand.Handler = CommandHandler.Create((pid) => + { + if (string.IsNullOrEmpty(pid)) return -2; + + IEnumerable processIds = DiagnosticsClient.GetPublishedProcesses(); + + int passedPid = 0; + try + { + passedPid = int.Parse(pid); + } + catch + { + return -3; + } + + bool found = false; + foreach (int id in processIds) + { + if (passedPid == id) + { + found = true; + break; + } + } + + if (!found) return -1; + processId = passedPid; + + return 0; + }); + + // Parse the incoming args and invoke the handler + int success = await rootCommand.InvokeAsync(args); + + if (success != 0) + { + return success; + } + + ProfilerSetup setup = new ProfilerSetup(processId); + // Now that the profiler has been setup + // we will wait for the duration of the process attached to to + } +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +} // namepsace ev31 + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// diff --git a/profiler/managed/Profiler.csproj b/profiler/managed/Profiler.csproj new file mode 100644 index 0000000..9feabeb --- /dev/null +++ b/profiler/managed/Profiler.csproj @@ -0,0 +1,14 @@ + + + + Exe + net5.0 + Portable + + + + + + + + diff --git a/profiler/managed/ProfilerSetup.cs b/profiler/managed/ProfilerSetup.cs new file mode 100644 index 0000000..d5bf705 --- /dev/null +++ b/profiler/managed/ProfilerSetup.cs @@ -0,0 +1,41 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: ProfilerSetup.cs +//////////////////////////////////////////////////////////////////////////////// + +namespace ev31 { + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +using System; + +using Microsoft.Diagnostics.NETCore.Client; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +public class ProfilerSetup +{ + private DiagnosticsClient Client { get; set; } + + public ProfilerSetup(int processId) + { + this.Client = new DiagnosticsClient(processId); + + Guid profilerGuid = Guid.Parse("cf0d821e-299b-5307-a3d8-b283c03916dd"); + + this.Client.AttachProfiler( + TimeSpan.FromMinutes(10), + profilerGuid, + "/Users/jashoo/projects/vscode-dotnet-insights/profiler/native/build/libev31_profiler.dylib" // Profiler path + ); + } +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +} // namespace (ev31) + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/profiler/native/CMakeLists.txt b/profiler/native/CMakeLists.txt new file mode 100644 index 0000000..bca4b4f --- /dev/null +++ b/profiler/native/CMakeLists.txt @@ -0,0 +1,67 @@ +################################################################################ +# Module: CMakeLists.txt +# +################################################################################ + +cmake_minimum_required(VERSION 3.21) +project (ev31_profiler) + +if (WIN32) + enable_language(ASM_MASM) +else() + enable_language(C ASM) +endif() + +FILE(GLOB cpp_sources *.cpp src/*.cpp) + +if (WIN32) +else() + add_definitions(-fms-extensions) + add_definitions(-fPIC) + add_definitions(-Wno-invalid-noreturn) + add_definitions(-Wno-pragma-pack) + add_definitions(-Wno-int-to-pointer-cast) + add_compile_options(-Wno-unsafe-buffer-usage) + add_compile_options(-Wno-cast-function-type-strict) + add_compile_options(-Wno-incompatible-function-pointer-types-strict) + add_compile_options(-Wno-unknown-warning-option) + add_compile_options(-Wvoid-pointer-to-int-cast) + add_definitions(-DPLATFORM_UNIX) + add_definitions(-DPAL_STDCPP_COMPAT) + add_compile_options(-Wno-unused-but-set-variable) +endif() + +if (WIN32) + FILE(GLOB asm_sources asm/amd64/windows/*.asm) + set(def_sources ${PROJECT_SOURCE_DIR}/ev31_profiler.def) +else() + if (CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL amd64) + FILE(GLOB asm_sources asm/amd64/unix/*.S) + else () + FILE(GLOB asm_sources asm/aarch64/unix/*.S) + endif() +endif() + +# Create the executable +add_library(ev31_profiler SHARED ${cpp_sources} ${asm_sources} ${def_sources}) + +if (WIN32) + target_include_directories(ev31_profiler PRIVATE inc ../../../runtime/src/coreclr/inc) +else() + target_include_directories(ev31_profiler PRIVATE inc ../../../runtime/src/coreclr/inc ../../../runtime/src/coreclr/pal/inc/rt ../../../runtime/src/coreclr/pal/inc ../../../runtime/src/coreclr/pal/prebuilt/inc) +endif() + +add_compile_options(-fexceptions) +add_definitions(-DUSE_STL) + +add_definitions(-DBIT64) + +if(WIN32) + add_definitions(-DWIN32_LEAN_AND_MEAN) + target_link_libraries(ev31_profiler wsock32 ws2_32) + target_link_libraries(ev31_profiler httpapi) + + add_definitions(-DHOST_WINDOWS) +endif() + +target_compile_features(ev31_profiler PRIVATE cxx_std_20) \ No newline at end of file diff --git a/profiler/native/asm/aarch64/unix/asm_helper.S b/profiler/native/asm/aarch64/unix/asm_helper.S new file mode 100644 index 0000000..55d3a64 --- /dev/null +++ b/profiler/native/asm/aarch64/unix/asm_helper.S @@ -0,0 +1,114 @@ +.section __TEXT,__text + +.globl _EnterWrapper +.globl _ExitWrapper +.globl _TailcallWrapper + +_EnterWrapper: + stp x0, x1, [sp, #-0xF0] + stp x2, x3, [sp, #-0xE0] + stp x4, x5, [sp, #-0xD0] + stp x6, x7, [sp, #-0xC0] + stp x8, x9, [sp, #-0xB0] + stp x10, x11, [sp, #-0xA0] + stp x12, x13, [sp, #-0x90] + stp x14, x15, [sp, #-0x80] + stp x16, x17, [sp, #-0x70] + stp x18, x19, [sp, #-0x60] + stp x20, x21, [sp, #-0x50] + stp x22, x23, [sp, #-0x40] + stp x24, x25, [sp, #-0x30] + stp x26, x27, [sp, #-0x20] + stp x28, x0, [sp, #-0x10] + + b _EnterStub + + ldp x0, x1, [sp, #-0xF0] + ldp x2, x3, [sp, #-0xE0] + ldp x4, x5, [sp, #-0xD0] + ldp x6, x7, [sp, #-0xC0] + ldp x8, x9, [sp, #-0xB0] + ldp x10, x11, [sp, #-0xA0] + ldp x12, x13, [sp, #-0x90] + ldp x14, x15, [sp, #-0x80] + ldp x16, x17, [sp, #-0x70] + ldp x18, x19, [sp, #-0x60] + ldp x20, x21, [sp, #-0x50] + ldp x22, x23, [sp, #-0x40] + ldp x24, x25, [sp, #-0x30] + ldp x26, x27, [sp, #-0x20] + ldp x28, x0, [sp, #-0x10] + + ret + +_ExitWrapper: + + stp x0, x1, [sp, #-0xF0] + stp x2, x3, [sp, #-0xE0] + stp x4, x5, [sp, #-0xD0] + stp x6, x7, [sp, #-0xC0] + stp x8, x9, [sp, #-0xB0] + stp x10, x11, [sp, #-0xA0] + stp x12, x13, [sp, #-0x90] + stp x14, x15, [sp, #-0x80] + stp x16, x17, [sp, #-0x70] + stp x18, x19, [sp, #-0x60] + stp x20, x21, [sp, #-0x50] + stp x22, x23, [sp, #-0x40] + stp x24, x25, [sp, #-0x30] + stp x26, x27, [sp, #-0x20] + stp x28, x0, [sp, #-0x10] + + b _ExitStub + + ldp x0, x1, [sp, #-0xF0] + ldp x2, x3, [sp, #-0xE0] + ldp x4, x5, [sp, #-0xD0] + ldp x6, x7, [sp, #-0xC0] + ldp x8, x9, [sp, #-0xB0] + ldp x10, x11, [sp, #-0xA0] + ldp x12, x13, [sp, #-0x90] + ldp x14, x15, [sp, #-0x80] + ldp x16, x17, [sp, #-0x70] + ldp x18, x19, [sp, #-0x60] + ldp x20, x21, [sp, #-0x50] + ldp x22, x23, [sp, #-0x40] + ldp x24, x25, [sp, #-0x30] + ldp x26, x27, [sp, #-0x20] + ldp x28, x0, [sp, #-0x10] + +_TailcallWrapper: + + stp x0, x1, [sp, #-0xF0] + stp x2, x3, [sp, #-0xE0] + stp x4, x5, [sp, #-0xD0] + stp x6, x7, [sp, #-0xC0] + stp x8, x9, [sp, #-0xB0] + stp x10, x11, [sp, #-0xA0] + stp x12, x13, [sp, #-0x90] + stp x14, x15, [sp, #-0x80] + stp x16, x17, [sp, #-0x70] + stp x18, x19, [sp, #-0x60] + stp x20, x21, [sp, #-0x50] + stp x22, x23, [sp, #-0x40] + stp x24, x25, [sp, #-0x30] + stp x26, x27, [sp, #-0x20] + stp x28, x0, [sp, #-0x10] + + b _TailcallStub + + ldp x0, x1, [sp, #-0xF0] + ldp x2, x3, [sp, #-0xE0] + ldp x4, x5, [sp, #-0xD0] + ldp x6, x7, [sp, #-0xC0] + ldp x8, x9, [sp, #-0xB0] + ldp x10, x11, [sp, #-0xA0] + ldp x12, x13, [sp, #-0x90] + ldp x14, x15, [sp, #-0x80] + ldp x16, x17, [sp, #-0x70] + ldp x18, x19, [sp, #-0x60] + ldp x20, x21, [sp, #-0x50] + ldp x22, x23, [sp, #-0x40] + ldp x24, x25, [sp, #-0x30] + ldp x26, x27, [sp, #-0x20] + ldp x28, x0, [sp, #-0x10] \ No newline at end of file diff --git a/profiler/native/asm/amd64/unix/asm_helper.S b/profiler/native/asm/amd64/unix/asm_helper.S new file mode 100644 index 0000000..0916a52 --- /dev/null +++ b/profiler/native/asm/amd64/unix/asm_helper.S @@ -0,0 +1,74 @@ +.section __TEXT,__text + +.globl _EnterWrapper +.globl _ExitWrapper +.globl _TailcallWrapper + +_EnterWrapper: + + push %rax + push %rcx + push %rdx + push %rsi + push %rdi + push %r8 + push %r9 + push %r10 + push %r11 + call _EnterStub + pop %r11 + pop %r10 + pop %r9 + pop %r8 + pop %rdi + pop %rsi + pop %rdx + pop %rcx + pop %rax + ret + +_ExitWrapper: + + push %rax + push %rcx + push %rdx + push %rsi + push %rdi + push %r8 + push %r9 + push %r10 + push %r11 + call _ExitStub + pop %r11 + pop %r10 + pop %r9 + pop %r8 + pop %rdi + pop %rsi + pop %rdx + pop %rcx + pop %rax + ret + +_TailcallWrapper: + + push %rax + push %rcx + push %rdx + push %rsi + push %rdi + push %r8 + push %r9 + push %r10 + push %r11 + call _TailcallStub + pop %r11 + pop %r10 + pop %r9 + pop %r8 + pop %rdi + pop %rsi + pop %rdx + pop %rcx + pop %rax + ret \ No newline at end of file diff --git a/profiler/native/asm/amd64/windows/asm_helper.asm b/profiler/native/asm/amd64/windows/asm_helper.asm new file mode 100644 index 0000000..8456a01 --- /dev/null +++ b/profiler/native/asm/amd64/windows/asm_helper.asm @@ -0,0 +1,132 @@ +EXTERN EnterStub:PROC +EXTERN ExitStub:PROC +EXTERN TailcallStub:PROC + +_text SEGMENT PARA 'CODE' + +ALIGN 16 +PUBLIC EnterWrapper + +EnterWrapper PROC FRAME + + PUSH RAX + .PUSHREG RAX + PUSH RCX + .PUSHREG RCX + PUSH RDX + .PUSHREG RDX + PUSH R8 + .PUSHREG R8 + PUSH R9 + .PUSHREG R9 + PUSH R10 + .PUSHREG R10 + PUSH R11 + .PUSHREG R11 + + SUB RSP, 20H + .ALLOCSTACK 20H + + .ENDPROLOG + + CALL EnterStub + + ADD RSP, 20H + + POP R11 + POP R10 + POP R9 + POP R8 + POP RDX + POP RCX + POP RAX + + RET + +EnterWrapper ENDP + +ALIGN 16 +PUBLIC ExitWrapper + +ExitWrapper PROC FRAME + + PUSH RAX + .PUSHREG RAX + PUSH RCX + .PUSHREG RCX + PUSH RDX + .PUSHREG RDX + PUSH R8 + .PUSHREG R8 + PUSH R9 + .PUSHREG R9 + PUSH R10 + .PUSHREG R10 + PUSH R11 + .PUSHREG R11 + + SUB RSP, 20H + .ALLOCSTACK 20H + + .ENDPROLOG + + CALL ExitStub + + ADD RSP, 20H + + POP R11 + POP R10 + POP R9 + POP R8 + POP RDX + POP RCX + POP RAX + + RET + +ExitWrapper ENDP + +ALIGN 16 +PUBLIC TailcallWrapper + +TailcallWrapper PROC FRAME + + PUSH RAX + .PUSHREG RAX + PUSH RCX + .PUSHREG RCX + PUSH RDX + .PUSHREG RDX + PUSH R8 + .PUSHREG R8 + PUSH R9 + .PUSHREG R9 + PUSH R10 + .PUSHREG R10 + PUSH R11 + .PUSHREG R11 + + SUB RSP, 20H + .ALLOCSTACK 20H + + .ENDPROLOG + + CALL TailcallStub + + ADD RSP, 20H + + POP R11 + POP R10 + POP R9 + POP R8 + POP RDX + POP RCX + POP RAX + + RET + +TailcallWrapper ENDP + +_text ENDS + +END \ No newline at end of file diff --git a/profiler/native/dylib_main.cpp b/profiler/native/dylib_main.cpp new file mode 100644 index 0000000..136f58f --- /dev/null +++ b/profiler/native/dylib_main.cpp @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: dylib_main.cpp +// +//////////////////////////////////////////////////////////////////////////////// + +#include "class_factory.hpp" + +#include + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +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) +{ + // {cf0d821e-299b-5307-a3d8-b283c03916dd} + const GUID CLSID_Ev31Profiler = { 0xcf0d821e, 0x299b, 0x5307, { 0xa3, 0xd8, 0xb2, 0x83, 0xc0, 0x39, 0x16, 0xdd } }; + + if (ppv == nullptr || rclsid != CLSID_Ev31Profiler) + { + return E_FAIL; + } + + auto factory = new ev31::class_factory; + if (factory == nullptr) + { + return E_FAIL; + } + + return factory->QueryInterface(riid, ppv); +} + +extern "C" HRESULT STDMETHODCALLTYPE DllCanUnloadNow() +{ + return S_OK; +} \ No newline at end of file diff --git a/profiler/native/ev31_profiler.def b/profiler/native/ev31_profiler.def new file mode 100644 index 0000000..070aba0 --- /dev/null +++ b/profiler/native/ev31_profiler.def @@ -0,0 +1,5 @@ +LIBRARY "ev31_profiler.dll" + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE \ No newline at end of file diff --git a/profiler/native/inc/CComPtr.hpp b/profiler/native/inc/CComPtr.hpp new file mode 100644 index 0000000..b456b6c --- /dev/null +++ b/profiler/native/inc/CComPtr.hpp @@ -0,0 +1,84 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: CComPtr.hpp +//////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include + +template +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 + { + return this->pointer; + } + + TInterface& operator *() + { + return *this->pointer; + } + + TInterface& operator *() const + { + return *this->pointer; + } + + TInterface** operator&() + { + return &this->pointer; + } + + TInterface** operator&() const + { + return &this->pointer; + } + + TInterface* operator->() + { + return this->pointer; + } + + TInterface* operator->() const + { + return this->pointer; + } + + void Release() + { + this->~CComPtr(); + } +}; \ No newline at end of file diff --git a/profiler/native/inc/assembly_tracker.hpp b/profiler/native/inc/assembly_tracker.hpp new file mode 100644 index 0000000..14bcf98 --- /dev/null +++ b/profiler/native/inc/assembly_tracker.hpp @@ -0,0 +1,53 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: assembly_tracker.hpp +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __ASSEMBLY_TRACKER_HPP__ +#define __ASSEMBLY_TRACKER_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +namespace ev31 { + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +class assembly_tracker +{ + // Ctor / Dtor + public: + assembly_tracker(); + ~assembly_tracker(); + + // Member Methods + public: + void start_assembly_timing(const std::size_t, const std::wstring&); + std::size_t stop_assembly_timing(const std::size_t, const std::wstring&); + + private: + std::unordered_map assembly_map; + std::unordered_map*> assembly_timing_map; +}; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +} // end of namespace (ev31) + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#endif // __ASSEMBLY_TRACKER_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/profiler/native/inc/class_factory.hpp b/profiler/native/inc/class_factory.hpp new file mode 100644 index 0000000..e2aaab4 --- /dev/null +++ b/profiler/native/inc/class_factory.hpp @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: class_factor.hpp +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __CLASS_FACTORY_HPP__ +#define __CLASS_FACTORY_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#include "unknwn.h" + +#include "ev31_profiler.hpp" +#include "globals.hpp" + +#include + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +namespace ev31 { + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +class class_factory : public IClassFactory +{ + // Ctor / Dtor + public: + class_factory(); + virtual ~class_factory(); + + // Member methods + public: + ::ULONG STDMETHODCALLTYPE AddRef(void) override; + ::HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject) override; + ::HRESULT STDMETHODCALLTYPE LockServer(::BOOL fLock) override; + ::HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override; + ::ULONG STDMETHODCALLTYPE Release(void) override; + + // Member variables + private: + std::atomic ref_count; +}; // class_factory + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +} // namespace (ev31) + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#endif // __CLASS_FACTORY_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/profiler/native/inc/ev31_profiler.hpp b/profiler/native/inc/ev31_profiler.hpp new file mode 100644 index 0000000..40fb7c2 --- /dev/null +++ b/profiler/native/inc/ev31_profiler.hpp @@ -0,0 +1,177 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: ev31_profiler.hpp +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __EV31_PROFILER_HPP__ +#define __EV31_PROFILER_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#include "CComPtr.hpp" +#include "cor.h" +#include "corhlpr.h" +#include "corprof.h" +#include "profiler_pal.h" + +#include +#include +#include +#include +#include +#include + +#include "assembly_tracker.hpp" +#include "method_tracker.hpp" +#include "profiler_io.hpp" + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +EXTERN_C void EnterWrapper(FunctionIDOrClientID functionIDOrClientID, COR_PRF_ELT_INFO eltInfo); +EXTERN_C void ExitWrapper(FunctionIDOrClientID functionIDOrClientID, COR_PRF_ELT_INFO eltInfo); +EXTERN_C void TailcallWrapper(FunctionIDOrClientID functionIDOrClientID, COR_PRF_ELT_INFO eltInfo); + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +namespace ev31 { + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +class ev31_profiler : public ICorProfilerCallback8 +{ + // Ctor / Dtor + public: + ev31_profiler(); + virtual ~ev31_profiler(); + + // Member methods + public: + ::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 DynamicMethodJITCompilationStarted(FunctionID functionId, BOOL fIsSafeToBlock, LPCBYTE ilHeader, ULONG cbILHeader) override; + ::HRESULT STDMETHODCALLTYPE DynamicMethodJITCompilationFinished(FunctionID functionId, ::HRESULT hrStatus, BOOL fIsSafeToBlock) override; + + ::HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; + ULONG STDMETHODCALLTYPE AddRef(void) override; + ULONG STDMETHODCALLTYPE Release(void) override; + + void EnterMethod(FunctionIDOrClientID function_id, COR_PRF_ELT_INFO elt_info); + void LeaveMethod(FunctionID function_id, COR_PRF_ELT_INFO elt_info); + + // Private member methods + private: + const std::wstring get_assembly_name(AssemblyID assembly_id); + const std::wstring get_method_name(FunctionID function_id, bool is_dynamic_method=false); + + // Member variables + private: + std::atomic ref_count; + ev31::assembly_tracker assembly_tracker; + ev31::method_tracker method_tracker; + ICorProfilerInfo8* profiler_info; + // ev31::profiler_io io; +}; // (ev31_profiler) + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +} // namespace (ev31) + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#endif // __EV31_PROFILER_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/profiler/native/inc/globals.hpp b/profiler/native/inc/globals.hpp new file mode 100644 index 0000000..b6a080c --- /dev/null +++ b/profiler/native/inc/globals.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: globals.hpp +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __GLOBALS_HPP__ +#define __GLOBALS_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#include "ev31_profiler.hpp" + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +extern ev31::ev31_profiler* global_profiler; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#endif // __GLOBALS_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/profiler/native/inc/http_request.hpp b/profiler/native/inc/http_request.hpp new file mode 100644 index 0000000..43e63ab --- /dev/null +++ b/profiler/native/inc/http_request.hpp @@ -0,0 +1,330 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: http_request.hpp +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __HTTP_REQUEST_HPP__ +#define __HTTP_REQUEST_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////////////////// + +#include + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +namespace ev31 { + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +namespace http { + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +enum content_type +{ + Json, + FormEncoded +}; + +enum version +{ + Version1, + Version1_1, + Version1_2 +}; + +enum request_type +{ + Get, + Head, + Post, + Put, + Delete, + Connect, + Options, + Trace, + Patch +}; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +} // namespace (http) + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +template class http_request +{ + private: + std::stringstream request_header_builder; + std::string host; + std::string path; + std::string request; + http::request_type request_type; + http::version version; + + public: + http_request() { } + + http_request(const std::string& request_unparsed) + { + std::stringstream ss(request_unparsed); + + std::string request_type; + ss >> request_type; + + std::string path; + ss >> path; + + std::string version; + ss >> version; + + std::string host; + std::getline(ss, host); + + ss >> host; + ss >> host; + + if (*(host.end() - 1) == '\r') + { + host.erase(host.end() - 1); + } + + http::request_type parsed_request_type; + if (request_type == "GET") parsed_request_type = ev31::http::Get; + else if (request_type == "HEAD") parsed_request_type = ev31::http::Head; + else if (request_type == "POST") parsed_request_type = ev31::http::Post; + else if (request_type == "PUT") parsed_request_type = ev31::http::Put; + else if (request_type == "DELETE") parsed_request_type = ev31::http::Delete; + else if (request_type == "CONNECT") parsed_request_type = ev31::http::Connect; + else if (request_type == "OPTIONS") parsed_request_type = ev31::http::Options; + else if (request_type == "TRACE") parsed_request_type = ev31::http::Trace; + else if (request_type == "PATCH") parsed_request_type = ev31::http::Patch; + + http::version parsed_version; + + if (version == "HTTP/1.0") parsed_version = ev31::http::Version1; + else if (version == "HTTP/1.1") parsed_version = ev31::http::Version1_1; + else if (version == "HTTP/1.2") parsed_version = ev31::http::Version1_1; + + this->request_type = parsed_request_type; + this->path = path; + this->version = parsed_version; + this->host = host; + + this->request = request_unparsed; + } + + http_request(http::version version, http::request_type request_type, const std::string& host, const std::string& path) + { + this->request_type = request_type; + this->version = version; + this->host = host; + this->path = path; + + this->stamp_request_type(); + this->stamp_path(); + this->stamp_version(); + this->stamp_host(); + this->stamp_accept(); + this->stamp_user_agent(); + + this->add_line_to_header(""); + + this->request = this->request_header_builder.str(); + } + + http_request(http::version version, http::request_type request_type, const std::string& host, const std::string& path, http::content_type content_type, const __Type& body) + { + this->request_type = request_type; + this->version = version; + this->host = host; + this->path = path; + + this->stamp_request_type(); + this->stamp_path(); + this->stamp_version(); + this->stamp_host(); + this->stamp_accept(); + this->stamp_user_agent(); + this->stamp_content_length(body.size()); + this->stamp_content_type(content_type); + this->add_line_to_header(""); + + this->request = this->request_header_builder.str(); + + this->add_body(body); + } + + http_request(http_request& rhs) + { + this->request_type = rhs.request_type; + this->version = rhs.version; + this->host = rhs.host; + this->path = rhs.path; + + this->request = rhs.request; + } + + ev31::http_request<__Type, __Iterator> operator= (const ev31::http_request<__Type, __Iterator> & rhs) + { + http_request temp(rhs.get_version(), rhs.get_request_type(), rhs.get_host(), rhs.get_path()); + return temp; + } + + // Member variables + public: + + http::request_type get_request_type() const { return this->request_type; } + http::version get_version() const { return this->host; } + const std::string& get_host() const { return this->path; } + const std::string& get_path() const { return this->version; } + const std::string& to_string() const { return this->request; } + + std::size_t size() const + { + return this->request.size(); + } + + private: + + void add_body(const __Type& body) + { + std::copy(body.begin(), body.end(), std::back_insert_iterator(this->request)); + this->request += "\r\n"; + } + + void add_to_header(const std::string& content, std::string end = " ") + { + this->request_header_builder << content << end; + } + + void add_line_to_header(const std::string& content) + { + this->add_to_header(content, "\r\n"); + } + + void stamp_accept() + { + this->add_line_to_header("Accept: */*"); + } + + void stamp_content_length(std::size_t length) + { + std::stringstream content_length; + + content_length << "Content-Length: "; + content_length << length; + + this->add_line_to_header(content_length.str()); + } + + void stamp_content_type(http::content_type type) + { + assert(type == http::content_type::Json); + this->add_line_to_header("Content-Type: application/json"); + } + + void stamp_host() + { + std::stringstream ss; + + ss << "Host: " << this->host; + this->add_line_to_header(ss.str()); + } + + void stamp_request_type() + { + std::string request_type; + + if (this->request_type == ev31::http::Get) + { + request_type = "GET"; + } + else if (this->request_type == ev31::http::Head) + { + request_type = "HEAD"; + } + else if (this->request_type == ev31::http::Post) + { + request_type = "POST"; + } + else if (this->request_type == ev31::http::Put) + { + request_type = "PUT"; + } + else if (this->request_type == ev31::http::Delete) + { + request_type = "DELETE"; + } + else if (this->request_type == ev31::http::Connect) + { + request_type = "CONNECT"; + } + else if (this->request_type == ev31::http::Options) + { + request_type = "OPTIONS"; + } + else if (this->request_type == ev31::http::Trace) + { + request_type = "TRACE"; + } + else if (this->request_type == ev31::http::Patch) + { + request_type = "PATCH"; + } + + this->add_to_header(request_type); + } + + void stamp_path() + { + this->add_to_header(this->path); + } + + void stamp_user_agent() + { + this->add_line_to_header("User-Agent: ev31/1.0.0"); + } + + void stamp_version() + { + if (this->version == ev31::http::version::Version1) + { + this->add_line_to_header("HTTP/1.0"); + } + else if (this->version == ev31::http::version::Version1_1) + { + this->add_line_to_header("HTTP/1.1"); + } + else if (this->version == ev31::http::version::Version1_2) + { + this->add_line_to_header("HTTP/1.2"); + } + } +}; + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +} // end namespace(ev31) + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#endif // __HTTP_REQUEST_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// diff --git a/profiler/native/inc/http_response.hpp b/profiler/native/inc/http_response.hpp new file mode 100644 index 0000000..4f0a93c --- /dev/null +++ b/profiler/native/inc/http_response.hpp @@ -0,0 +1,100 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: http_response.hpp +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __HTTP_RESPONSE_HPP__ +#define __HTTP_RESPONSE_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include +#include +#include +#include + +#include "http_request.hpp" + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +namespace ev31 { + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +enum http_response_type +{ + Ok, + Created, + MovedPermanently, + BadRequest, + Unauthorized, + Forbidden, + NotFound, + LengthRequired, + TooManyRequests, + InternalServerError, + ServiceUnavailable, + HttpVersionNotSupported +}; + +enum http_content_type +{ + text, + html, + json +}; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +class http_response +{ + private: + ev31::http_request request; + std::stringstream response_header_builder; + std::string response; + http_response_type response_type; + http::version version; + + public: + http_response(const std::string& response); + http_response(const ev31::http_request& request, + ev31::http::version version, + ev31::http_response_type response_type, + const std::string& content, + ev31::http_content_type content_type); + + std::string to_string(); + + private: + void add_to_header(const std::string& content, std::string end = " "); + void add_line_to_header(const std::string& content); + void stamp_connection(); + void stamp_content_length(const std::string& content); + void stamp_content_type(http_content_type content_type); + void stamp_date(); + void stamp_last_modified(); + void stamp_server(); + void stamp_status_code(); + void stamp_version(); +}; + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +} // end namespace(ev31) + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#endif // __HTTP_RESPONSE_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// diff --git a/profiler/native/inc/method_info.hpp b/profiler/native/inc/method_info.hpp new file mode 100644 index 0000000..35361d5 --- /dev/null +++ b/profiler/native/inc/method_info.hpp @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: method_info.hpp +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __METHOD_INFO_HPP__ +#define __METHOD_INFO_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#include + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +namespace ev31 { + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +class method_info +{ + // Ctor / Dtor + public: + method_info(const std::wstring&); + ~method_info(); + + // Member Methods + public: + const std::wstring& get_method_name(); + + // Member variables + private: + std::wstring method_name; + double total_time_in_method; +}; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +} // end of namespace (ev31) + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#endif // __METHOD_INFO_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/profiler/native/inc/method_tracker.hpp b/profiler/native/inc/method_tracker.hpp new file mode 100644 index 0000000..e2ae3a5 --- /dev/null +++ b/profiler/native/inc/method_tracker.hpp @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: method_tracker.hpp +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef __METHOD_TRACKER_HPP__ +#define __METHOD_TRACKER_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +#include "method_info.hpp" + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +namespace ev31 { + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +class method_tracker +{ + // Ctor / Dtor + public: + method_tracker(); + ~method_tracker(); + + // Member Methods + public: + const ev31::method_info* get_method_info_for_name(const std::wstring&); + void start_method_compilation_timing(const std::size_t, const std::wstring&); + void start_method_execution_timing(const std::size_t, const std::wstring&); + std::size_t stop_method_compilation_timing(const std::size_t, const std::wstring&); + std::size_t stop_method_execution_timing(const std::size_t, const std::wstring&); + + private: + std::unordered_map method_map; + std::unordered_map*> method_compilation_timing_map; + std::unordered_map*> method_execution_timing_map; +}; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +} // end of namespace (ev31) + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#endif // __METHOD_TRACKER_HPP__ + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/profiler/native/inc/profiler_io.hpp b/profiler/native/inc/profiler_io.hpp new file mode 100644 index 0000000..f3b1c0d --- /dev/null +++ b/profiler/native/inc/profiler_io.hpp @@ -0,0 +1,153 @@ +// //////////////////////////////////////////////////////////////////////////////// +// // Module: profiler_io.hpp +// // +// //////////////////////////////////////////////////////////////////////////////// + +// #ifndef __PROFILER_IO_HPP__ +// #define __PROFILER_IO_HPP__ + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// + +// #include +// #include +// #include +// #include +// #include + +// #include "http_request.hpp" +// #include "http_response.hpp" +// #include "tcp_connection.hpp" + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// + +// namespace ev31 { + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// + +// class profiler_io +// { +// // Ctor / Dtor +// public: +// profiler_io(bool use_console=false); +// ~profiler_io(); + +// public: // Template Functions +// template void communicate(const std::string& id_name, +// const std::size_t id, +// const __Type& method_name, +// double time, +// const std::string& event_name) +// { +// auto&& json_payload = this->_get_json_payload<__Type>(id_name, id, method_name, time, event_name); + +// if (!this->use_console) +// { +// auto&& http_request = this->_get_http_request(json_payload); + +// this->_attempt_open_connection(); +// if (this->_is_open_connection()) +// { +// const std::string& request_string = http_request.to_string(); +// int bytes_sent = this->connection->send(request_string); + +// if (bytes_sent != http_request.size()) +// { +// // We should close the connection and ignore failures. +// this->_close_connection(); +// } + +// // Unused +// std::array bytes_received; +// int receive_count = this->connection->receive(bytes_received); + +// if (receive_count == 0) +// { +// // This is most likely an issue with us overloading the http server. +// std::cout << "Receive count zero" << std::endl; +// } + +// std::string response_string(bytes_received.begin(), bytes_received.begin() + receive_count); +// std::cout << response_string << std::endl; +// } +// else +// { +// int i = 0; +// } +// } +// else +// { +// for (unsigned index = 0; index < json_payload.size(); ++index) +// { +// std::cout << (char)json_payload[index]; +// } +// std::cout << std::endl; +// } +// } + +// // Member Methods +// public: +// void io_via_console(bool use_console=true); + +// // Private member methods +// private: +// void _attempt_open_connection(); +// void _close_connection(); +// bool _is_open_connection() const; + +// private: // Template Methods +// template const std::vector _get_json_payload(const std::string& id_name, +// const std::size_t id, +// const __Type& method_name, +// double time, +// const std::string& event_name) +// { +// std::vector payload; + +// std::stringstream ss; +// ss << "{ \"eventName\": " << event_name << ','; + +// ss << "\"" << id_name << "\": " << (std::size_t)id << ','; + +// std::copy(std::istream_iterator(ss), std::istream_iterator(), std::back_insert_iterator>(payload)); + +// ss.clear(); +// ss << "methodName: "; + +// std::copy(std::istream_iterator(ss), std::istream_iterator(), std::back_insert_iterator>(payload)); +// std::copy(method_name.begin(), method_name.end(), std::back_insert_iterator>(payload)); + +// ss.clear(); +// ss << ", time: " << time << '}'; + +// std::copy(std::istream_iterator(ss), std::istream_iterator(), std::back_insert_iterator>(payload)); +// return payload; +// } + +// // Private member methods +// private: +// const std::vector _get_json_payload(const std::size_t, const std::wstring&, double, const std::string&); +// const ev31::http_request _get_http_request(const std::vector&); + +// bool should_open_connection; +// bool use_console; + +// std::string ip; +// std::string port; +// ev31::tcp_connection* connection; +// }; + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// + +// } // end of namespace (ev31) + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// + +// #endif // __PROFILER_IO_HPP__ + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/profiler/native/inc/profiler_pal.h b/profiler/native/inc/profiler_pal.h new file mode 100644 index 0000000..75e7180 --- /dev/null +++ b/profiler/native/inc/profiler_pal.h @@ -0,0 +1,23 @@ +// 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 + +#ifndef WIN32 +#include +#include "pal_mstypes.h" +#include "pal.h" +#include "ntimage.h" +#include "corhdr.h" + +#define CoTaskMemAlloc(cb) malloc(cb) +#define CoTaskMemFree(cb) free(cb) + +#define UINT_PTR_FORMAT "lx" + +#define PROFILER_STUB __attribute__((visibility("hidden"))) EXTERN_C void STDMETHODCALLTYPE + +#else +#define PROFILER_STUB EXTERN_C void STDMETHODCALLTYPE +#define UINT_PTR_FORMAT "llx" +#endif \ No newline at end of file diff --git a/profiler/native/inc/tcp_connection.hpp b/profiler/native/inc/tcp_connection.hpp new file mode 100644 index 0000000..642981f --- /dev/null +++ b/profiler/native/inc/tcp_connection.hpp @@ -0,0 +1,113 @@ +// //////////////////////////////////////////////////////////////////////////////// +// // Module: tcp_connection.hpp +// //////////////////////////////////////////////////////////////////////////////// + +// #ifndef __TCP_CONNECTION_HPP__ +// #define __TCP_CONNECTION_HPP__ + +// //////////////////////////////////////////////////////////////////////////////// +// // Includes +// //////////////////////////////////////////////////////////////////////////////// + +// #include +// #include +// #include +// #include + +// #include +// #include +// #include +// #include +// #include + +// #include +// #include +// #include + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// + +// namespace ev31 { + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// + +// class tcp_connection +// { +// private: +// std::string client_ip; +// std::string port; +// bool connection_open; +// bool connection_closed; +// bool received_end_of_request; +// bool moved; +// ::SOCKET socket; +// ::WSAData wsa_data; + +// public: +// tcp_connection(const std::string& outbound_ip, const std::string& port); +// tcp_connection(::SOCKET client_socket, const std::string& ip); +// tcp_connection(tcp_connection&& rhs); +// ~tcp_connection(); + +// void close(); +// bool closed(bool required_sys_call=false); +// void connect(); +// const std::string& connecting_ip(); +// bool end_of_request(); +// void initialize_winsock(); +// void reset_receive_status(); +// int send(const std::string& response); +// int send(const unsigned char* buffer, int size); + +// private: +// void initialize_addrinfo(); +// void toggle_connection_open(); +// void shutdown(); + +// public: +// template std::size_t receive(std::array& buffer) +// { +// if (this->closed(true)) +// { +// this->toggle_connection_open(); +// return 0; +// } + +// this->received_end_of_request = false; + +// assert(this->socket != INVALID_SOCKET); + +// char* buffer_signed = (char*)buffer.data(); +// int bytes_received = ::recv(this->socket, buffer_signed, __Size, 0); + +// if (bytes_received < 0) +// { +// std::string error_message("Receive failed. Error code: "); +// error_message += std::to_string(::WSAGetLastError()); + +// std::cout << error_message << std::endl; +// throw std::runtime_error(error_message); +// } +// else if (bytes_received != __Size) +// { +// this->received_end_of_request = true; +// } + +// return bytes_received; +// } +// }; + + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// + +// } // end namespace(ev31) + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// + +// #endif // __TCP_CONNECTION_HPP__ + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// diff --git a/profiler/native/src/assembly_tracker.cpp b/profiler/native/src/assembly_tracker.cpp new file mode 100644 index 0000000..2e96f78 --- /dev/null +++ b/profiler/native/src/assembly_tracker.cpp @@ -0,0 +1,87 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: assembly_tracker.cpp +// +//////////////////////////////////////////////////////////////////////////////// + +#include "assembly_tracker.hpp" + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +ev31::assembly_tracker::assembly_tracker() +{ + +} + +ev31::assembly_tracker::~assembly_tracker() +{ + +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void ev31::assembly_tracker::start_assembly_timing(const std::size_t profiler_method_id, + const std::wstring& method_name) +{ + // Get the method map + std::unordered_map* assembly_map = nullptr; + + if (this->assembly_timing_map.find(method_name) != this->assembly_timing_map.end()) + { + assembly_map = (this->assembly_timing_map.find(method_name))->second; + } + else + { + // First time we have found, create it + assembly_map = new std::unordered_map(); + + this->assembly_timing_map.insert({ method_name, assembly_map }); + } + + // Use the method map to add the instance of this method + if (assembly_map->find(profiler_method_id) != assembly_map->end()) + { + return; + } + + assembly_map->insert({ profiler_method_id, std::chrono::high_resolution_clock::now() }); +} + +std::size_t ev31::assembly_tracker::stop_assembly_timing(const std::size_t profiler_method_id, + const std::wstring& method_name) +{ + // Get the method map + std::unordered_map* assembly_map = nullptr; + + if (this->assembly_timing_map.find(method_name) != this->assembly_timing_map.end()) + { + assembly_map = (this->assembly_timing_map.find(method_name))->second; + } + else + { + // First time we have found, create it + assembly_map = new std::unordered_map(); + + this->assembly_timing_map.insert({ method_name, assembly_map }); + } + + // Use the method map to the instance of this method + std::size_t return_value = 0; + + // Assert that this is not an instance of the profiler id method already. + assert(assembly_map->find(profiler_method_id) != assembly_map->end()); + if (assembly_map->find(profiler_method_id) != assembly_map->end()) + { + auto start_time_point = (assembly_map->find(profiler_method_id))->second; + auto end_time_point = std::chrono::high_resolution_clock::now(); + + std::chrono::nanoseconds diff_ns = std::chrono::duration_cast(end_time_point - start_time_point); + return_value = diff_ns.count(); + } + + return return_value; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/profiler/native/src/class_factory.cpp b/profiler/native/src/class_factory.cpp new file mode 100644 index 0000000..8bbc8d4 --- /dev/null +++ b/profiler/native/src/class_factory.cpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: class_factor.cpp +// +//////////////////////////////////////////////////////////////////////////////// + +#include "class_factory.hpp" + +//////////////////////////////////////////////////////////////////////////////// +// Ctor / Dtor +//////////////////////////////////////////////////////////////////////////////// + +ev31::class_factory::class_factory() : ref_count(0) { } +ev31::class_factory::~class_factory() { } + +//////////////////////////////////////////////////////////////////////////////// +// Member methods +//////////////////////////////////////////////////////////////////////////////// + +::ULONG STDMETHODCALLTYPE ev31::class_factory::AddRef() +{ + return std::atomic_fetch_add(&this->ref_count, 1) + 1; +} + +::HRESULT STDMETHODCALLTYPE ev31::class_factory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject) +{ + if (pUnkOuter != nullptr) + { + *ppvObject = nullptr; + return CLASS_E_NOAGGREGATION; + } + + ev31_profiler* profiler = new ev31_profiler(); + if (profiler == nullptr) + { + return E_FAIL; + } + + return profiler->QueryInterface(riid, ppvObject); +} + +::HRESULT STDMETHODCALLTYPE ev31::class_factory::LockServer(::BOOL fLock) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::class_factory::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 ev31::class_factory::Release() +{ + int count = std::atomic_fetch_sub(&this->ref_count, 1) - 1; + if (count <= 0) + { + delete this; + } + + return count; +} \ No newline at end of file diff --git a/profiler/native/src/ev31_profiler.cpp b/profiler/native/src/ev31_profiler.cpp new file mode 100644 index 0000000..00fa1bc --- /dev/null +++ b/profiler/native/src/ev31_profiler.cpp @@ -0,0 +1,664 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: ev31_profiler.cpp +//////////////////////////////////////////////////////////////////////////////// + +#include "ev31_profiler.hpp" + +#include "globals.hpp" + +//////////////////////////////////////////////////////////////////////////////// +// Ctor / Dtor +//////////////////////////////////////////////////////////////////////////////// + +ev31::ev31_profiler::ev31_profiler() : ref_count(0), profiler_info(nullptr) +{ +} + +ev31::ev31_profiler::~ev31_profiler() +{ + if (this->profiler_info != nullptr) + { + this->profiler_info->Release(); + this->profiler_info = nullptr; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Member methods +//////////////////////////////////////////////////////////////////////////////// + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::Initialize(IUnknown *pICorProfilerInfoUnk) +{ + ::HRESULT queryInterfaceResult = pICorProfilerInfoUnk->QueryInterface(__uuidof(ICorProfilerInfo8), reinterpret_cast(&this->profiler_info)); + + if (FAILED(queryInterfaceResult)) + { + return E_FAIL; + } + + DWORD eventMask = COR_PRF_MONITOR_ENTERLEAVE | COR_PRF_ENABLE_FUNCTION_ARGS | COR_PRF_ENABLE_FUNCTION_RETVAL | COR_PRF_ENABLE_FRAME_INFO | COR_PRF_MONITOR_JIT_COMPILATION; + + auto hr = this->profiler_info->SetEventMask(eventMask); + if (hr != S_OK) + { + std::cout << "ERROR: Profiler SetEventMask failed (HRESULT: " << hr << ")"; + } + + hr = this->profiler_info->SetEnterLeaveFunctionHooks3WithInfo(EnterWrapper, ExitWrapper, TailcallWrapper); + + if (hr != S_OK) + { + std::cout << "ERROR: Profiler SetEnterLeaveFunctionHooks3WithInfo faled (HRESULT: " << hr << ")"; + } + + global_profiler = this; + + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::Shutdown() +{ + if (this->profiler_info != nullptr) + { + this->profiler_info->Release(); + this->profiler_info = nullptr; + } + + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::AssemblyLoadStarted(AssemblyID assembly_id) +{ + std::wstring assembly_name = this->get_assembly_name(assembly_id); + this->assembly_tracker.start_assembly_timing((std::size_t)assembly_id, assembly_name); + + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::AssemblyLoadFinished(AssemblyID assembly_id, ::HRESULT hrStatus) +{ + std::wstring assembly_name = this->get_assembly_name(assembly_id); + double time = this->assembly_tracker.stop_assembly_timing(assembly_id, assembly_name); + + // this->io.communicate("assembly_id", assembly_id, assembly_name, time, "JitCompilation"); + + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::FunctionUnloadStarted(FunctionID functionId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::JITCompilationStarted(FunctionID function_id, BOOL fIsSafeToBlock) +{ + std::wstring method_name = this->get_method_name(function_id); + method_tracker.start_method_execution_timing((std::size_t)function_id, method_name); + + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::JITCompilationFinished(FunctionID function_id, ::HRESULT hrStatus, BOOL fIsSafeToBlock) +{ + std::wstring method_name = this->get_method_name(function_id); + double time = method_tracker.stop_method_execution_timing(function_id, method_name); + + // this->io.communicate("function_id", function_id, method_name, time, "JitCompilation"); + + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::JITCachedFunctionSearchStarted(FunctionID function_id, BOOL *pbUseCachedFunction) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::JITCachedFunctionSearchFinished(FunctionID function_id, COR_PRF_JIT_CACHE result) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::JITFunctionPitched(FunctionID functionId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::JITInlining(FunctionID callerId, FunctionID calleeId, BOOL* pfShouldInline) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ThreadCreated(ThreadID threadId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ThreadDestroyed(ThreadID threadId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ThreadAssignedToOSThread(ThreadID managedThreadId, DWORD osThreadId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::UnmanagedToManagedTransition(FunctionID function_id, COR_PRF_TRANSITION_REASON reason) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ManagedToUnmanagedTransition(FunctionID function_id, COR_PRF_TRANSITION_REASON reason) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RuntimeSuspendStarted(COR_PRF_SUSPEND_REASON suspendReason) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RuntimeSuspendFinished() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RuntimeSuspendAborted() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RuntimeResumeStarted() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RuntimeResumeFinished() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RuntimeThreadSuspended(ThreadID threadId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RuntimeThreadResumed(ThreadID threadId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::MovedReferences(ULONG cMovedObjectIDRanges, ObjectID oldObjectIDRangeStart[], ObjectID newObjectIDRangeStart[], ULONG cObjectIDRangeLength[]) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ObjectAllocated(ObjectID objectId, ClassID classId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ObjectsAllocatedByClass(ULONG cClassCount, ClassID classIds[], ULONG cObjects[]) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ObjectReferences(ObjectID objectId, ClassID classId, ULONG cObjectRefs, ObjectID objectRefIds[]) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RootReferences(ULONG cRootRefs, ObjectID rootRefIds[]) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionThrown(ObjectID thrownObjectId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionSearchFunctionEnter(FunctionID functionId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionSearchFunctionLeave() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionSearchFilterEnter(FunctionID functionId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionSearchFilterLeave() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionSearchCatcherFound(FunctionID functionId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionOSHandlerEnter(UINT_PTR __unused) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionOSHandlerLeave(UINT_PTR __unused) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionUnwindFunctionEnter(FunctionID functionId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionUnwindFunctionLeave() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionUnwindFinallyEnter(FunctionID functionId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionUnwindFinallyLeave() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionCatcherEnter(FunctionID function_id, ObjectID objectId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionCatcherLeave() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::COMClassicVTableCreated(ClassID wrappedClassId, REFGUID implementedIID, void *pVTable, ULONG cSlots) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::COMClassicVTableDestroyed(ClassID wrappedClassId, REFGUID implementedIID, void *pVTable) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionCLRCatcherFound() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ExceptionCLRCatcherExecute() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ThreadNameChanged(ThreadID threadId, ULONG cchName, WCHAR name[]) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::GarbageCollectionStarted(int cGenerations, BOOL generationCollected[], COR_PRF_GC_REASON reason) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::SurvivingReferences(ULONG cSurvivingObjectIDRanges, ObjectID objectIDRangeStart[], ULONG cObjectIDRangeLength[]) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::GarbageCollectionFinished() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::FinalizeableObjectQueued(DWORD finalizerFlags, ObjectID objectID) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::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 ev31::ev31_profiler::HandleCreated(GCHandleID handleId, ObjectID initialObjectId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::HandleDestroyed(GCHandleID handleId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::InitializeForAttach(IUnknown *pCorProfilerInfoUnk, void *pvClientData, UINT cbClientData) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ProfilerAttachComplete() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ProfilerDetachSucceeded() +{ + return S_OK; +} + +// Most likely this is due to tier up +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ReJITCompilationStarted(FunctionID function_id, ReJITID rejitId, BOOL fIsSafeToBlock) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::GetReJITParameters(ModuleID moduleId, mdMethodDef methodId, ICorProfilerFunctionControl *pFunctionControl) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ReJITCompilationFinished(FunctionID function_id, ReJITID rejitId, ::HRESULT hrStatus, BOOL fIsSafeToBlock) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ReJITError(ModuleID moduleId, mdMethodDef methodId, FunctionID function_id, ::HRESULT hrStatus) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::MovedReferences2(ULONG cMovedObjectIDRanges, ObjectID oldObjectIDRangeStart[], ObjectID newObjectIDRangeStart[], SIZE_T cObjectIDRangeLength[]) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::SurvivingReferences2(ULONG cSurvivingObjectIDRanges, ObjectID objectIDRangeStart[], SIZE_T cObjectIDRangeLength[]) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ConditionalWeakTableElementReferences(ULONG cRootRefs, ObjectID keyRefIds[], ObjectID valueRefIds[], GCHandleID rootIds[]) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::GetAssemblyReferences(const WCHAR *wszAssemblyPath, ICorProfilerAssemblyReferenceProvider *pAsmRefProvider) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ModuleInMemorySymbolsUpdated(ModuleID moduleId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::DynamicMethodJITCompilationStarted(FunctionID function_id, BOOL fIsSafeToBlock, LPCBYTE ilHeader, ULONG cbILHeader) +{ + std::wstring method_name = this->get_method_name(function_id, true); + method_tracker.start_method_execution_timing((std::size_t)function_id, method_name); + + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::DynamicMethodJITCompilationFinished(FunctionID function_id, ::HRESULT hrStatus, BOOL fIsSafeToBlock) +{ + std::wstring method_name = this->get_method_name(function_id, true); + double time = method_tracker.stop_method_execution_timing(function_id, method_name); + + // this->io.communicate("function_id", function_id, method_name, time, "JitCompilation"); + + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::QueryInterface(REFIID riid, void** ppvObject) +{ + if (riid == __uuidof(ICorProfilerCallback8) || + 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 ev31::ev31_profiler::AddRef() +{ + return std::atomic_fetch_add(&this->ref_count, 1) + 1; +} + +ULONG STDMETHODCALLTYPE ev31::ev31_profiler::Release() +{ + int count = std::atomic_fetch_sub(&this->ref_count, 1) - 1; + + if (count <= 0) + { + delete this; + } + + return count; +} + +void ev31::ev31_profiler::EnterMethod(FunctionIDOrClientID function_id, COR_PRF_ELT_INFO elt_info) +{ + std::wstring method_name = this->get_method_name(function_id.functionID); + method_tracker.start_method_execution_timing((std::size_t)function_id.functionID, method_name); +} + +void ev31::ev31_profiler::LeaveMethod(FunctionID function_id, COR_PRF_ELT_INFO elt_info) +{ + std::wstring method_name = this->get_method_name(function_id); + double time = method_tracker.stop_method_execution_timing(function_id, method_name); + + // this->io.communicate("function_id", function_id, method_name, time, "MethodExecution"); + std::wcout << "[" << method_name << "] " << (time / (1000 * 1000)) << std::fixed << std::setprecision(2) << "ms" << std::endl; +} + +//////////////////////////////////////////////////////////////////////////////// +// Private member methods +//////////////////////////////////////////////////////////////////////////////// + +const std::wstring ev31::ev31_profiler::get_assembly_name(AssemblyID assembly_id) +{ + ModuleID module_id = 0; + AppDomainID app_domain_id = 0; + + WCHAR assembly_name[1024]; + std::size_t name_size = 1024; + ULONG copied_count = 0; + + this->profiler_info->GetAssemblyInfo(assembly_id, name_size, &copied_count, assembly_name, &app_domain_id, &module_id); + + return std::wstring(assembly_name); +} + +const std::wstring ev31::ev31_profiler::get_method_name(FunctionID function_id, bool is_dynamic_method) +{ + if (!is_dynamic_method) + { + ClassID class_id = 0; + ModuleID module_id = 0; + mdToken token = 0; + + IMetaDataImport* metadata_import = nullptr; + + this->profiler_info->GetFunctionInfo(function_id, &class_id, &module_id, &token); + this->profiler_info->GetModuleMetaData(module_id, ofRead, IID_IMetaDataImport, (IUnknown**)&metadata_import); + + std::wstring return_value; + return_value.reserve(2048); + + WCHAR method_name[1024]; + std::size_t name_size = 1024; + ULONG copied_count = 0; + + // Get the class name + + mdTypeDef type_information = 0; + metadata_import->GetMethodProps(token, &type_information, method_name, name_size, &copied_count, nullptr, nullptr, nullptr, nullptr, nullptr); + + WCHAR type_name[1024]; + ULONG type_copied_count = 0; + metadata_import->GetTypeDefProps(type_information, type_name, name_size, &type_copied_count, nullptr, nullptr); + + std::size_t copy_count_minus_one = type_copied_count > 0 ? type_copied_count - 1 : 0; + for (std::size_t index = 0; index < copy_count_minus_one; ++index) + { + return_value += type_name[index]; + } + + return_value += ':'; + + for (std::size_t index = 0; index < copied_count; ++index) + { + return_value += method_name[index]; + } + + return return_value; + } + else + { + return L"DynamicMethod.DyanmicMethod"; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Unimplemented Callback +//////////////////////////////////////////////////////////////////////////////// + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::AppDomainCreationStarted(AppDomainID appDomainId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::AppDomainCreationFinished(AppDomainID appDomainId, ::HRESULT hrStatus) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::AppDomainShutdownStarted(AppDomainID appDomainId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::AppDomainShutdownFinished(AppDomainID appDomainId, ::HRESULT hrStatus) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ClassLoadStarted(ClassID classId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ClassLoadFinished(ClassID classId, ::HRESULT hrStatus) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ClassUnloadStarted(ClassID classId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ClassUnloadFinished(ClassID classId, ::HRESULT hrStatus) +{ + return S_OK; +} + + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RemotingClientInvocationStarted() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RemotingClientSendingMessage(GUID *pCookie, BOOL fIsAsync) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RemotingClientReceivingReply(GUID *pCookie, BOOL fIsAsync) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RemotingClientInvocationFinished() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RemotingServerReceivingMessage(GUID *pCookie, BOOL fIsAsync) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RemotingServerInvocationStarted() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RemotingServerInvocationReturned() +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::RemotingServerSendingReply(GUID *pCookie, BOOL fIsAsync) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::AssemblyUnloadStarted(AssemblyID assembly_id) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::AssemblyUnloadFinished(AssemblyID assembly_id, ::HRESULT hrStatus) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ModuleLoadStarted(ModuleID moduleId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ModuleLoadFinished(ModuleID moduleId, ::HRESULT hrStatus) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ModuleUnloadStarted(ModuleID moduleId) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ModuleUnloadFinished(ModuleID moduleId, ::HRESULT hrStatus) +{ + return S_OK; +} + +::HRESULT STDMETHODCALLTYPE ev31::ev31_profiler::ModuleAttachedToAssembly(ModuleID moduleId, AssemblyID Assembly_id) +{ + return S_OK; +} \ No newline at end of file diff --git a/profiler/native/src/globals.cpp b/profiler/native/src/globals.cpp new file mode 100644 index 0000000..5069314 --- /dev/null +++ b/profiler/native/src/globals.cpp @@ -0,0 +1,10 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: globals.cpp +//////////////////////////////////////////////////////////////////////////////// + +#include "globals.hpp" + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +ev31::ev31_profiler* global_profiler; \ No newline at end of file diff --git a/profiler/native/src/guids.cpp b/profiler/native/src/guids.cpp new file mode 100644 index 0000000..f396639 --- /dev/null +++ b/profiler/native/src/guids.cpp @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include + +#ifndef EXTERN_C +#define EXTERN_C extern "C" +#endif//EXTERN_C + +#ifdef DEFINE_GUID +#undef DEFINE_GUID +#endif + +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + EXTERN_C const GUID name \ + = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + +DEFINE_GUID(GUID_NULL, 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + +DEFINE_GUID(IID_ICorProfilerCallback, 0x176FBED1,0xA55C,0x4796,0x98,0xCA,0xA9,0xDA,0x0E,0xF8,0x83,0xE7); +DEFINE_GUID(IID_ICorProfilerCallback2, 0x8A8CC829,0xCCF2,0x49FE,0xBB,0xAE,0x0F,0x02,0x22,0x28,0x07,0x1A); +DEFINE_GUID(IID_ICorProfilerCallback3, 0x4FD2ED52,0x7731,0x4B8D,0x94,0x69,0x03,0xD2,0xCC,0x30,0x86,0xC5); +DEFINE_GUID(IID_ICorProfilerCallback4, 0x7B63B2E3,0x107D,0x4D48,0xB2,0xF6,0xF6,0x1E,0x22,0x94,0x70,0xD2); +DEFINE_GUID(IID_ICorProfilerCallback5, 0x8DFBA405,0x8C9F,0x45F8,0xBF,0xFA,0x83,0xB1,0x4C,0xEF,0x78,0xB5); +DEFINE_GUID(IID_ICorProfilerCallback6, 0xFC13DF4B,0x4448,0x4F4F,0x95,0x0C,0xBA,0x8D,0x19,0xD0,0x0C,0x36); +DEFINE_GUID(IID_ICorProfilerCallback7, 0xF76A2DBA,0x1D52,0x4539,0x86,0x6C,0x2A,0xA5,0x18,0xF9,0xEF,0xC3); +DEFINE_GUID(IID_ICorProfilerCallback8, 0x5BED9B15,0xC079,0x4D47,0xBF,0xE2,0x21,0x5A,0x14,0x0C,0x07,0xE0); +DEFINE_GUID(IID_ICorProfilerCallback9, 0x27583EC3,0xC8F5,0x482F,0x80,0x52,0x19,0x4B,0x8C,0xE4,0x70,0x5A); +DEFINE_GUID(IID_ICorProfilerCallback10, 0xCEC5B60E,0xC69C,0x495F,0x87,0xF6,0x84,0xD2,0x8E,0xE1,0x6F,0xFB); +DEFINE_GUID(IID_ICorProfilerCallback11, 0x42350846,0xAAED,0x47F7,0xB1,0x28,0xFD,0x0C,0x98,0x88,0x1C,0xDE); +DEFINE_GUID(IID_ICorProfilerInfo, 0x28B5557D,0x3F3F,0x48B4,0x90,0xB2,0x5F,0x9E,0xEA,0x2F,0x6C,0x48); +DEFINE_GUID(IID_ICorProfilerInfo2, 0xCC0935CD,0xA518,0x487D,0xB0,0xBB,0xA9,0x32,0x14,0xE6,0x54,0x78); +DEFINE_GUID(IID_ICorProfilerInfo3, 0xB555ED4F,0x452A,0x4E54,0x8B,0x39,0xB5,0x36,0x0B,0xAD,0x32,0xA0); +DEFINE_GUID(IID_ICorProfilerObjectEnum, 0x2C6269BD,0x2D13,0x4321,0xAE,0x12,0x66,0x86,0x36,0x5F,0xD6,0xAF); +DEFINE_GUID(IID_ICorProfilerFunctionEnum, 0xFF71301A,0xB994,0x429D,0xA1,0x0B,0xB3,0x45,0xA6,0x52,0x80,0xEF); +DEFINE_GUID(IID_ICorProfilerModuleEnum, 0xB0266D75,0x2081,0x4493,0xAF,0x7F,0x02,0x8B,0xA3,0x4D,0xB8,0x91); +DEFINE_GUID(IID_IMethodMalloc, 0xA0EFB28B,0x6EE2,0x4D7B,0xB9,0x83,0xA7,0x5E,0xF7,0xBE,0xED,0xB8); +DEFINE_GUID(IID_ICorProfilerFunctionControl, 0xF0963021,0xE1EA,0x4732,0x85,0x81,0xE0,0x1B,0x0B,0xD3,0xC0,0xC6); +DEFINE_GUID(IID_ICorProfilerInfo4, 0x0D8FDCAA,0x6257,0x47BF,0xB1,0xBF,0x94,0xDA,0xC8,0x84,0x66,0xEE); +DEFINE_GUID(IID_ICorProfilerInfo5, 0x07602928,0xCE38,0x4B83,0x81,0xE7,0x74,0xAD,0xAF,0x78,0x12,0x14); +DEFINE_GUID(IID_ICorProfilerInfo6, 0xF30A070D,0xBFFB,0x46A7,0xB1,0xD8,0x87,0x81,0xEF,0x7B,0x69,0x8A); +DEFINE_GUID(IID_ICorProfilerInfo7, 0x9AEECC0D,0x63E0,0x4187,0x8C,0x00,0xE3,0x12,0xF5,0x03,0xF6,0x63); +DEFINE_GUID(IID_ICorProfilerInfo8, 0xC5AC80A6,0x782E,0x4716,0x80,0x44,0x39,0x59,0x8C,0x60,0xCF,0xBF); +DEFINE_GUID(IID_ICorProfilerInfo9, 0x008170DB,0xF8CC,0x4796,0x9A,0x51,0xDC,0x8A,0xA0,0xB4,0x70,0x12); +DEFINE_GUID(IID_ICorProfilerInfo10, 0x2F1B5152,0xC869,0x40C9,0xAA,0x5F,0x3A,0xBE,0x02,0x6B,0xD7,0x20); +DEFINE_GUID(IID_ICorProfilerInfo11, 0x06398876,0x8987,0x4154,0xB6,0x21,0x40,0xA0,0x0D,0x6E,0x4D,0x04); +DEFINE_GUID(IID_ICorProfilerInfo12, 0x27B24CCD,0x1CB1,0x47C5,0x96,0xEE,0x98,0x19,0x0D,0xC3,0x09,0x59); +DEFINE_GUID(IID_ICorProfilerMethodEnum, 0xFCCEE788,0x0088,0x454B,0xA8,0x11,0xC9,0x9F,0x29,0x8D,0x19,0x42); +DEFINE_GUID(IID_ICorProfilerThreadEnum, 0x571194F7,0x25ED,0x419F,0xAA,0x8B,0x70,0x16,0xB3,0x15,0x97,0x01); +DEFINE_GUID(IID_ICorProfilerAssemblyReferenceProvider, 0x66A78C24,0x2EEF,0x4F65,0xB4,0x5F,0xDD,0x1D,0x80,0x38,0xBF,0x3C); \ No newline at end of file diff --git a/profiler/native/src/http_response.cpp b/profiler/native/src/http_response.cpp new file mode 100644 index 0000000..8ee6470 --- /dev/null +++ b/profiler/native/src/http_response.cpp @@ -0,0 +1,227 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: http_response.cpp +//////////////////////////////////////////////////////////////////////////////// + +#include "http_response.hpp" + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +ev31::http_response::http_response(const std::string& response_unparsed) +{ + std::stringstream ss(response_unparsed); + + std::string response_type; + ss >> response_type; + + int status_code = 0; + ss >> status_code; + + throw std::runtime_error("NYI parse http_response"); + + // std::string path; + // ss >> path; + + // std::string version; + // ss >> version; + + // std::string host; + // std::getline(ss, host); + + // ss >> host; + // ss >> host; + + // if (*(host.end() - 1) == '\r') + // { + // host.erase(host.end() - 1); + // } + + // http::request_type parsed_request_type; + // if (response_type == "GET") parsed_request_type = ev31::http::Get; + // else if (response_type == "HEAD") parsed_request_type = ev31::http::Head; + // else if (response_type == "POST") parsed_request_type = ev31::http::Post; + // else if (response_type == "PUT") parsed_request_type = ev31::http::Put; + // else if (response_type == "DELETE") parsed_request_type = ev31::http::Delete; + // else if (response_type == "CONNECT") parsed_request_type = ev31::http::Connect; + // else if (response_type == "OPTIONS") parsed_request_type = ev31::http::Options; + // else if (response_type == "TRACE") parsed_request_type = ev31::http::Trace; + // else if (response_type == "PATCH") parsed_request_type = ev31::http::Patch; + + // http::version parsed_version; + + // if (version == "HTTP/1.0") parsed_version = ev31::http::Version1; + // else if (version == "HTTP/1.1") parsed_version = ev31::http::Version1_1; + // else if (version == "HTTP/1.2") parsed_version = ev31::http::Version1_1; +} + +ev31::http_response::http_response(const ev31::http_request& request, + ev31::http::version version, + ev31::http_response_type response_type, + const std::string& content, + ev31::http_content_type content_type) +{ + this->response_type = response_type; + this->version = version; + + this->stamp_version(); + this->stamp_status_code(); + this->stamp_date(); + this->stamp_server(); + this->stamp_last_modified(); + this->stamp_content_length(content); + this->stamp_content_type(content_type); + this->stamp_connection(); + + this->add_line_to_header(""); + + if (content.size() > 0 && request.get_request_type() != ev31::http::Head) + { + this->add_line_to_header(content); + } + + this->response = this->response_header_builder.str(); +} + +std::string ev31::http_response::to_string() +{ + return this->response; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void ev31::http_response::add_to_header(const std::string& content, std::string end) +{ + this->response_header_builder << content << end; +} + +void ev31::http_response::add_line_to_header(const std::string& content) +{ + this->add_to_header(content, "\r\n"); +} + +void ev31::http_response::stamp_connection() +{ + this->add_line_to_header("Connection: Closed"); +} + +void ev31::http_response::stamp_content_length(const std::string& content) +{ + std::size_t size = content.size(); + std::string content_string = "Content-Length: " + std::to_string(size); + + this->add_line_to_header(content_string); +} + +void ev31::http_response::stamp_content_type(ev31::http_content_type content_type) +{ + std::string content; + + if (content_type == ev31::http_content_type::html || content_type == ev31::http_content_type::text) + { + content = "text/html"; + } + else + { + content = "application/json"; + } + + this->add_line_to_header(content); +} + +void ev31::http_response::stamp_date() +{ + auto now = std::chrono::system_clock::now(); + auto t_c = std::chrono::system_clock::to_time_t(now); + + char buffer[120]; + std::strftime(buffer, 120, "%a, %d %b %Y %H:%M:%S PST", std::localtime(&t_c)); + + std::string content(buffer); + this->add_line_to_header(content); +} + +void ev31::http_response::stamp_last_modified() +{ + this->add_line_to_header("Last-Modified: Tue, 2 Feb 2021 15:24:56 PST"); +} + +void ev31::http_response::stamp_server() +{ + this->add_line_to_header("Server: ev31/1.0.0 (Win32)"); +} + +void ev31::http_response::stamp_status_code() +{ + if (this->response_type == ev31::http_response_type::Ok) + { + this->add_line_to_header("200 OK"); + } + else if (this->response_type == ev31::http_response_type::BadRequest) + { + this->add_line_to_header("201 Created"); + } + else if (this->response_type == ev31::http_response_type::MovedPermanently) + { + this->add_line_to_header("301 Moved Permanently"); + } + else if (this->response_type == ev31::http_response_type::BadRequest) + { + this->add_line_to_header("400 Bad Request"); + } + else if (this->response_type == ev31::http_response_type::Unauthorized) + { + this->add_line_to_header("401 Unauthorized"); + } + else if (this->response_type == ev31::http_response_type::Forbidden) + { + this->add_line_to_header("403 Forbidden"); + } + else if (this->response_type == ev31::http_response_type::NotFound) + { + this->add_line_to_header("404 Not Found"); + } + else if (this->response_type == ev31::http_response_type::LengthRequired) + { + this->add_line_to_header("411 Length Required"); + } + else if (this->response_type == ev31::http_response_type::TooManyRequests) + { + this->add_line_to_header("429 Too Many Requests"); + } + else if (this->response_type == ev31::http_response_type::InternalServerError) + { + this->add_line_to_header("500 Internal Server Error"); + } + else if (this->response_type == ev31::http_response_type::ServiceUnavailable) + { + this->add_line_to_header("503 Server Unavailable"); + } + else if (this->response_type == ev31::http_response_type::HttpVersionNotSupported) + { + this->add_line_to_header("505 HTTP Version Not Supported"); + } + else + { + throw std::runtime_error("NYI: " + this->response_type); + } +} + +void ev31::http_response::stamp_version() +{ + if (this->version == ev31::http::version::Version1) + { + this->add_to_header("HTTP/1.0"); + } + else if (this->version == ev31::http::version::Version1_1) + { + this->add_to_header("HTTP/1.1"); + } + else if (this->version == ev31::http::version::Version1_2) + { + this->add_to_header("HTTP/1.2"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/profiler/native/src/method_info.cpp b/profiler/native/src/method_info.cpp new file mode 100644 index 0000000..7d2a824 --- /dev/null +++ b/profiler/native/src/method_info.cpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: method_info.cpp +// +//////////////////////////////////////////////////////////////////////////////// + +#include "method_info.hpp" + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +ev31::method_info::method_info(const std::wstring& method_name) +{ + this->method_name = method_name; + this->total_time_in_method = 0.0; +} + +ev31::method_info::~method_info() +{ + +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +const std::wstring& ev31::method_info::get_method_name() +{ + return this->method_name; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/profiler/native/src/method_tracker.cpp b/profiler/native/src/method_tracker.cpp new file mode 100644 index 0000000..400472b --- /dev/null +++ b/profiler/native/src/method_tracker.cpp @@ -0,0 +1,166 @@ +//////////////////////////////////////////////////////////////////////////////// +// Module: method_tracker.cpp +// +//////////////////////////////////////////////////////////////////////////////// + +#include "method_tracker.hpp" + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +ev31::method_tracker::method_tracker() +{ + +} + +ev31::method_tracker::~method_tracker() +{ + +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +const ev31::method_info* ev31::method_tracker::get_method_info_for_name(const std::wstring& method_name) +{ + ev31::method_info* method_found = nullptr; + if (this->method_map.find(method_name) != this->method_map.end()) + { + method_found = (this->method_map.find(method_name))->second; + } + else + { + // We will create a new method_info structure and pack it + method_found = new ev31::method_info(method_name); + this->method_map.insert({ method_name, method_found }); + } + + return method_found; +} + +void ev31::method_tracker::start_method_compilation_timing(const std::size_t profiler_method_id, + const std::wstring& method_name) +{ + // Get the method map + std::unordered_map* method_map = nullptr; + + if (this->method_compilation_timing_map.find(method_name) != this->method_compilation_timing_map.end()) + { + method_map = (this->method_compilation_timing_map.find(method_name))->second; + } + else + { + // First time we have found, create it + method_map = new std::unordered_map(); + + this->method_compilation_timing_map.insert({ method_name, method_map }); + } + + // Use the method map to add the instance of this method + if (method_map->find(profiler_method_id) != method_map->end()) + { + return; + } + + method_map->insert({ profiler_method_id, std::chrono::high_resolution_clock::now() }); +} + +void ev31::method_tracker::start_method_execution_timing(const std::size_t profiler_method_id, + const std::wstring& method_name) +{ + // Get the method map + std::unordered_map* method_map = nullptr; + + if (this->method_execution_timing_map.find(method_name) != this->method_execution_timing_map.end()) + { + method_map = (this->method_execution_timing_map.find(method_name))->second; + } + else + { + // First time we have found, create it + method_map = new std::unordered_map(); + + this->method_execution_timing_map.insert({ method_name, method_map }); + } + + // Use the method map to add the instance of this method + if (method_map->find(profiler_method_id) != method_map->end()) + { + return; + } + + method_map->insert({ profiler_method_id, std::chrono::high_resolution_clock::now() }); +} + +std::size_t ev31::method_tracker::stop_method_compilation_timing(const std::size_t profiler_method_id, + const std::wstring& method_name) +{ + // Get the method map + std::unordered_map* method_map = nullptr; + + if (this->method_compilation_timing_map.find(method_name) != this->method_compilation_timing_map.end()) + { + method_map = (this->method_compilation_timing_map.find(method_name))->second; + } + else + { + // First time we have found, create it + method_map = new std::unordered_map(); + + this->method_compilation_timing_map.insert({ method_name, method_map }); + } + + // Use the method map to the instance of this method + std::size_t return_value = 0; + + // Assert that this is not an instance of the profiler id method already. + assert(method_map->find(profiler_method_id) != method_map->end()); + if (method_map->find(profiler_method_id) != method_map->end()) + { + auto start_time_point = (method_map->find(profiler_method_id))->second; + auto end_time_point = std::chrono::high_resolution_clock::now(); + + std::chrono::nanoseconds diff_ns = std::chrono::duration_cast(end_time_point - start_time_point); + return_value = diff_ns.count(); + } + + return return_value; +} + +std::size_t ev31::method_tracker::stop_method_execution_timing(const std::size_t profiler_method_id, + const std::wstring& method_name) +{ + // Get the method map + std::unordered_map* method_map = nullptr; + + if (this->method_execution_timing_map.find(method_name) != this->method_execution_timing_map.end()) + { + method_map = (this->method_execution_timing_map.find(method_name))->second; + } + else + { + // First time we have found, create it + method_map = new std::unordered_map(); + + this->method_execution_timing_map.insert({ method_name, method_map }); + } + + // Use the method map to the instance of this method + std::size_t return_value = 0; + + // Assert that this is not an instance of the profiler id method already. + assert(method_map->find(profiler_method_id) != method_map->end()); + if (method_map->find(profiler_method_id) != method_map->end()) + { + auto start_time_point = (method_map->find(profiler_method_id))->second; + auto end_time_point = std::chrono::high_resolution_clock::now(); + + std::chrono::nanoseconds diff_ns = std::chrono::duration_cast(end_time_point - start_time_point); + return_value = diff_ns.count(); + } + + return return_value; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/profiler/native/src/profiler_io.cpp b/profiler/native/src/profiler_io.cpp new file mode 100644 index 0000000..0fe6ed1 --- /dev/null +++ b/profiler/native/src/profiler_io.cpp @@ -0,0 +1,87 @@ +// //////////////////////////////////////////////////////////////////////////////// +// // Module: profiler_io.cpp +// // +// //////////////////////////////////////////////////////////////////////////////// + +// #include "profiler_io.hpp" + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// + +// ev31::profiler_io::profiler_io(bool use_console) +// { +// this->ip = "127.0.0.1"; +// this->port = "2143"; + +// this->connection = new ev31::tcp_connection(this->ip, this->port); +// this->connection->initialize_winsock(); + +// this->should_open_connection = true; +// this->use_console = use_console; +// } + +// ev31::profiler_io::~profiler_io() +// { +// delete this->connection; +// } + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// + + +// void ev31::profiler_io::io_via_console(bool use_console) +// { +// this->use_console = use_console; +// } + +// //////////////////////////////////////////////////////////////////////////////// +// // Private methods +// //////////////////////////////////////////////////////////////////////////////// + +// void ev31::profiler_io::_attempt_open_connection() +// { +// if (this->should_open_connection && this->connection->closed()) +// { +// try +// { +// this->connection->connect(); +// } +// catch (std::exception& e) +// { +// this->should_open_connection = false; +// } + +// if (this->connection->closed()) +// { +// this->should_open_connection = false; +// } +// else +// { +// std::cout << "Connection opened" << std::endl; +// } +// } +// } + +// void ev31::profiler_io::_close_connection() +// { +// this->connection->close(); +// this->should_open_connection = false; +// } + +// const ev31::http_request ev31::profiler_io::_get_http_request(const std::vector& payload) +// { +// std::wstring wstring_type(payload.begin(), payload.end()); + +// std::wstring_convert, wchar_t> converter; + +// //use converter (.to_bytes: wstr->str, .from_bytes: str->wstr) +// std::string json_payload = converter.to_bytes(wstring_type); +// ev31::http_request request(ev31::http::version::Version1_1, ev31::http::request_type::Post, this->ip, "/profiler", ev31::http::content_type::Json, json_payload); + +// return request; +// } + +// bool ev31::profiler_io::_is_open_connection() const +// { +// return !this->connection->closed(); +// } \ No newline at end of file diff --git a/profiler/native/src/stubs.cpp b/profiler/native/src/stubs.cpp new file mode 100644 index 0000000..f1260e5 --- /dev/null +++ b/profiler/native/src/stubs.cpp @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#include "CComPtr.hpp" +#include "cor.h" +#include "corhlpr.h" +#include "corprof.h" +#include "profiler_pal.h" + +#include "globals.hpp" + +#include + +//////////////////////////////////////////////////////////////////////////////// +// Stubs +//////////////////////////////////////////////////////////////////////////////// + +PROFILER_STUB EnterStub(FunctionIDOrClientID function_id, COR_PRF_ELT_INFO elt_info) +{ + global_profiler->EnterMethod(function_id, elt_info); +} + +PROFILER_STUB ExitStub(FunctionID function_id, COR_PRF_ELT_INFO elt_info) +{ + global_profiler->LeaveMethod(function_id, elt_info); +} + +PROFILER_STUB TailcallStub(FunctionID function_id, COR_PRF_ELT_INFO elt_info) +{ +} \ No newline at end of file diff --git a/profiler/native/src/tcp_connection.cpp b/profiler/native/src/tcp_connection.cpp new file mode 100644 index 0000000..c7eb939 --- /dev/null +++ b/profiler/native/src/tcp_connection.cpp @@ -0,0 +1,252 @@ +// //////////////////////////////////////////////////////////////////////////////// +// // Module: tcp_connection.cpp +// //////////////////////////////////////////////////////////////////////////////// + +// #include "tcp_connection.hpp" + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// + +// ev31::tcp_connection::tcp_connection(const std::string& outbound_ip, const std::string& port) +// { +// this->socket = INVALID_SOCKET; + +// this->connection_open = false; +// this->connection_closed = true; + +// this->moved = false; +// this->received_end_of_request = false; + +// this->client_ip = outbound_ip; +// this->port = port; +// } + +// ev31::tcp_connection::tcp_connection(::SOCKET client_socket, const std::string& ip) +// { +// this->socket = client_socket; +// this->connection_open = true; +// this->connection_closed = false; + +// this->moved = false; +// this->received_end_of_request = false; + +// this->client_ip = ip; +// this->port = ""; +// } + +// ev31::tcp_connection::tcp_connection(ev31::tcp_connection&& rhs) +// { +// this->socket = rhs.socket; +// this->client_ip = rhs.client_ip; +// this->port = rhs.port; + +// this->connection_open = rhs.connection_open; +// this->connection_closed = rhs.connection_closed; + +// this->received_end_of_request = rhs.received_end_of_request; + +// this->moved = false; +// rhs.moved = true; +// } + +// ev31::tcp_connection::~tcp_connection() +// { +// // Do not shutdown and close the socket if the object has moved +// // the socket is still valid and we do not want to send the +// // shutdown signal to the client. +// if (this->moved) +// { +// return; +// } + +// if (!this->closed()) +// { +// assert(this->connection_open); +// this->shutdown(); +// } + +// if (this->socket != INVALID_SOCKET) +// { +// ::closesocket(this->socket); +// } + +// assert(this->connection_closed); +// } + +// bool ev31::tcp_connection::closed(bool sys_call_required) +// { +// bool closed = this->connection_closed; + +// if (!closed && sys_call_required) +// { +// ::fd_set set; +// FD_ZERO(&set); +// FD_SET(this->socket, &set); +// ::timeval time_value; + +// time_value.tv_sec = 1; +// time_value.tv_usec = 0; + +// int h_result = ::select((int)this->socket + 1, &set, 0, 0, &time_value); + +// if (h_result == SOCKET_ERROR) +// { +// closed = false; +// } +// else if (h_result != 1) +// { +// closed = true; +// } +// } + +// return closed; +// } + +// void ev31::tcp_connection::connect() +// { +// ::addrinfo hints; +// std::memset(&hints, 0, sizeof(::addrinfo)); + +// hints.ai_family = AF_UNSPEC; +// hints.ai_socktype = SOCK_STREAM; +// hints.ai_protocol = IPPROTO_TCP; + +// ::addrinfo* result_info = nullptr; + +// // Resolve address +// int h_result = ::getaddrinfo(this->client_ip.c_str(), this->port.c_str(), &hints, &result_info); +// if (h_result != 0) +// { +// std::string error_message("Unable to initialize addrinfo. Error code: "); +// error_message += std::to_string(::GetLastError()); + +// std::cout << error_message << std::endl; +// throw std::runtime_error(error_message); +// } + +// for(::addrinfo* info = result_info; info != nullptr; info = info->ai_next) { +// this->socket = ::socket(info->ai_family, info->ai_socktype, info->ai_protocol); +// if (this->socket == INVALID_SOCKET) { +// std::string error_message("Unable to initialize addrinfo. Error code: "); +// error_message += std::to_string(::WSAGetLastError()); + +// std::cout << error_message << std::endl; +// throw std::runtime_error(error_message); +// } + +// this->toggle_connection_open(); + +// // Connect to server. +// h_result = ::connect(this->socket, info->ai_addr, (int)info->ai_addrlen); +// if (h_result == SOCKET_ERROR) { +// ::closesocket(this->socket); +// this->socket = INVALID_SOCKET; + +// this->toggle_connection_open(); +// continue; +// } + +// break; +// } +// } + +// const std::string& ev31::tcp_connection::connecting_ip() +// { +// return this->client_ip; +// } + +// bool ev31::tcp_connection::end_of_request() +// { +// return this->received_end_of_request; +// } + +// int ev31::tcp_connection::send(const std::string& response) +// { +// this->received_end_of_request = false; +// int bytes_sent = ::send(this->socket, response.c_str(), (int)response.size(), 0); + +// if (bytes_sent == SOCKET_ERROR) { +// std::string error_message("Shutdown failed. Error code: "); +// error_message += std::to_string(::WSAGetLastError()); + +// std::cout << error_message << std::endl; +// throw std::runtime_error(error_message); +// } + +// return bytes_sent; +// } + +// int ev31::tcp_connection::send(const unsigned char* buffer, int size) +// { +// this->received_end_of_request = false; + +// char* buffer_signed = (char*)buffer; +// int bytes_sent = ::send(this->socket, buffer_signed, size, 0); + +// if (bytes_sent == SOCKET_ERROR) { +// std::string error_message("Shutdown failed. Error code: "); +// error_message += std::to_string(::WSAGetLastError()); + +// std::cout << error_message << std::endl; +// throw std::runtime_error(error_message); +// } + +// return bytes_sent; +// } + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// + +// void ev31::tcp_connection::close() +// { +// if (!this->closed(true)) +// { +// this->shutdown(); +// } + +// if (this->connection_open) +// { +// this->toggle_connection_open(); +// } +// } + +// void ev31::tcp_connection::initialize_winsock() +// { +// std::memset(&this->wsa_data, 0, sizeof(::WSADATA)); +// std::size_t h_result = ::WSAStartup(MAKEWORD(2, 2), &this->wsa_data); + +// if (h_result) +// { +// std::string error_message("Unable to initialize winsock. Error code: "); +// error_message += std::to_string(::GetLastError()); + +// std::cout << error_message << std::endl; +// throw std::runtime_error(error_message); +// } +// } + +// void ev31::tcp_connection::toggle_connection_open() +// { +// this->connection_open = !this->connection_open; +// this->connection_closed = !this->connection_closed; +// } + +// void ev31::tcp_connection::shutdown() +// { +// // Shutdown this connection. +// std::size_t h_result = ::shutdown(this->socket, SD_SEND); + +// if (h_result == SOCKET_ERROR) +// { +// std::string error_message("Shutdown failed. Error code: "); +// error_message += std::to_string(::WSAGetLastError()); + +// std::cout << error_message << std::endl; +// throw std::runtime_error(error_message); +// } + +// this->toggle_connection_open(); +// } + +// //////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/profiler/setup_env.cmd b/profiler/setup_env.cmd new file mode 100644 index 0000000..dacff26 --- /dev/null +++ b/profiler/setup_env.cmd @@ -0,0 +1,5 @@ +set scriptDir=%cd% + +set CORECLR_PROFILER_PATH=%scriptDir%\native\build\Debug\ev31_profiler.dll +set CORECLR_PROFILER={cf0d821e-299b-5307-a3d8-b283c03916dd} +set CORECLR_ENABLE_PROFILING=1 \ No newline at end of file diff --git a/profiler/setup_env.sh b/profiler/setup_env.sh new file mode 100644 index 0000000..96e6425 --- /dev/null +++ b/profiler/setup_env.sh @@ -0,0 +1,5 @@ +dir="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" + +export CORECLR_PROFILER_PATH=$dir/native/build/libev31_profiler.dylib +export CORECLR_PROFILER={cf0d821e-299b-5307-a3d8-b283c03916dd} +export CORECLR_ENABLE_PROFILING=1 \ No newline at end of file diff --git a/profiler/tests/bringup/jit_compilation/Program.cs b/profiler/tests/bringup/jit_compilation/Program.cs new file mode 100644 index 0000000..3751555 --- /dev/null +++ b/profiler/tests/bringup/jit_compilation/Program.cs @@ -0,0 +1,2 @@ +// See https://aka.ms/new-console-template for more information +Console.WriteLine("Hello, World!"); diff --git a/profiler/tests/bringup/jit_compilation/jit_compilation.csproj b/profiler/tests/bringup/jit_compilation/jit_compilation.csproj new file mode 100644 index 0000000..74abf5c --- /dev/null +++ b/profiler/tests/bringup/jit_compilation/jit_compilation.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + +