Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
Expand All @@ -9,12 +10,74 @@ namespace System.Diagnostics
{
public partial class StackTrace
{
private const string PerfMapSymbolReaderTypeName = "System.Diagnostics.PerfMapSymbolReader, System.Diagnostics.StackTrace, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct PerfMapSequencePointInfoNative
{
public int lineNumber;
public int ilOffset;
public char* fileName;
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct PerfMapLocalVarInfoNative
{
public int startOffset;
public int endOffset;
public char* name;
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct PerfMapMethodDebugInfoNative
{
public PerfMapSequencePointInfoNative* points;
public int size;
public PerfMapLocalVarInfoNative* locals;
public int localsSize;
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "StackTrace_GetStackFramesInternal")]
private static partial void GetStackFramesInternal(ObjectHandleOnStack sfh, [MarshalAs(UnmanagedType.Bool)] bool fNeedFileInfo, ObjectHandleOnStack e);

[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "GetInfoForMethod")]
private static extern unsafe bool GetPerfMapInfoForMethodFromReader(
[UnsafeAccessorType(PerfMapSymbolReaderTypeName)] object? perfMapSymbolReader,
[MarshalAs(UnmanagedType.LPUTF8Str)] string assemblyPath,
int methodToken,
IntPtr points,
int size);

[DynamicDependency(nameof(GetInfoForMethod))]
internal static void GetStackFramesInternal(StackFrameHelper sfh, bool fNeedFileInfo, Exception? e)
=> GetStackFramesInternal(ObjectHandleOnStack.Create(ref sfh), fNeedFileInfo, ObjectHandleOnStack.Create(ref e));

[UnmanagedCallersOnly]
internal static unsafe int GetInfoForMethod(byte* assemblyPath, uint methodToken, PerfMapMethodDebugInfoNative* methodDebugInfo)
{
if (assemblyPath == null || methodDebugInfo == null || methodDebugInfo->points == null || methodDebugInfo->size <= 0)
{
return 0;
}

methodDebugInfo->localsSize = 0;

try
{
string? assemblyPathString = Marshal.PtrToStringUTF8((IntPtr)assemblyPath);
if (string.IsNullOrEmpty(assemblyPathString))
{
return 0;
}

return GetPerfMapInfoForMethodFromReader(null, assemblyPathString, unchecked((int)methodToken), (IntPtr)methodDebugInfo->points, methodDebugInfo->size) ? 1 : 0;
}
catch
{
return 0;
}
}

internal static int CalculateFramesToSkip(StackFrameHelper StackF, int iNumFrames)
{
int iRetVal = 0;
Expand Down
16 changes: 8 additions & 8 deletions src/coreclr/dlls/mscoree/exports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
#include <corhost.h>
#include <configuration.h>
#include "../../vm/ceemain.h"
#ifdef FEATURE_GDBJIT
#if defined(TARGET_UNIX) && (defined(FEATURE_GDBJIT) || defined(FEATURE_PERFMAP))
#include "../../vm/gdbjithelpers.h"
#endif // FEATURE_GDBJIT
#endif // TARGET_UNIX && (FEATURE_GDBJIT || FEATURE_PERFMAP)
#include "bundle.h"
#include "pinvokeoverride.h"
#include <hostinformation.h>
Expand Down Expand Up @@ -207,10 +207,10 @@ int coreclr_set_error_writer(coreclr_error_writer_callback_fn error_writer)
return S_OK;
}

#ifdef FEATURE_GDBJIT
#if defined(TARGET_UNIX) && (defined(FEATURE_GDBJIT) || defined(FEATURE_PERFMAP))
GetInfoForMethodDelegate getInfoForMethodDelegate = NULL;
extern "C" int coreclr_create_delegate(void*, unsigned int, const char*, const char*, const char*, void**);
#endif //FEATURE_GDBJIT
#endif // TARGET_UNIX && (FEATURE_GDBJIT || FEATURE_PERFMAP)

//
// Initialize the CoreCLR. Creates and starts CoreCLR host and creates an app domain
Expand Down Expand Up @@ -328,20 +328,20 @@ int coreclr_initialize(
{
host.SuppressRelease();
*hostHandle = host;
#ifdef FEATURE_GDBJIT
#if defined(TARGET_UNIX) && (defined(FEATURE_GDBJIT) || defined(FEATURE_PERFMAP))
HRESULT createDelegateResult;
createDelegateResult = coreclr_create_delegate(*hostHandle,
*domainId,
"SOS.NETCore",
"SOS.SymbolReader",
"System.Private.CoreLib",
"System.Diagnostics.StackTrace",
"GetInfoForMethod",
(void**)&getInfoForMethodDelegate);

#if defined(_DEBUG)
if (!SUCCEEDED(createDelegateResult))
{
fprintf(stderr,
"Can't create delegate for 'SOS.SymbolReader.GetInfoForMethod' "
"Can't create delegate for 'System.Diagnostics.StackTrace.GetInfoForMethod' "
"method - status: 0x%08x\n", createDelegateResult);
}
#endif // _DEBUG
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/inc/clrconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapEnabled, W("PerfMapEnabled"), 0, "This
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapIgnoreSignal, W("PerfMapIgnoreSignal"), 0, "When perf map is enabled, this option will configure the specified signal to be accepted and ignored as a marker in the perf logs. It is disabled by default")
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapShowOptimizationTiers, W("PerfMapShowOptimizationTiers"), 1, "Shows optimization tiers in the perf map for methods, as part of the symbol name. Useful for seeing separate stack frames for different optimization tiers of each method.")
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapStubGranularity, W("PerfMapStubGranularity"), 0, "Report stubs with varying amounts of granularity (low bit being zero indicates attempt to group all stubs of a type together) (second lowest bit being non-zero records stubs at individual allocation sites, which is more expensive, but also more accurate).")
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapEmitDebugInfo, W("PerfMapEmitDebugInfo"), 0, "When jitdump output is enabled, emit JIT_CODE_DEBUG_INFO records for JIT-compiled managed methods when source information is available.")
#endif

RETAIL_CONFIG_STRING_INFO(EXTERNAL_StartupDelayMS, W("StartupDelayMS"), "")
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/pal/inc/pal.h
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ PALIMPORT
int
PALAPI
// Log a method to the jitdump file.
PAL_PerfJitDump_LogMethod(void* pCode, size_t codeSize, const char* symbol, void* debugInfo, void* unwindInfo, bool reportCodeBlock);
PAL_PerfJitDump_LogMethod(void* pCode, size_t codeSize, const char* symbol, const void* debugInfo, size_t debugInfoSize, const void* unwindInfo, size_t unwindInfoSize, bool reportCodeBlock);

PALIMPORT
int
Expand Down
61 changes: 46 additions & 15 deletions src/coreclr/pal/src/misc/perfjitdump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,13 @@ namespace
#elif defined(HOST_S390X)
ELF_MACHINE = EM_S390,
#elif defined(HOST_POWERPC64)
ELF_MACHINE = EM_PPC64,
ELF_MACHINE = EM_PPC64,
#else
#error ELF_MACHINE unsupported for target
#endif

JIT_CODE_LOAD = 0,
JIT_CODE_DEBUG_INFO = 2,
};

static bool UseArchTimeStamp()
Expand Down Expand Up @@ -146,6 +147,17 @@ namespace
// Null terminated name
// Optional native code
};

struct JitCodeDebugInfoRecord
{
JitCodeDebugInfoRecord()
{
header.id = JIT_CODE_DEBUG_INFO;
header.timestamp = GetTimeStampNS();
}

RecordHeader header;
};
};

struct PerfJitDumpState
Expand Down Expand Up @@ -247,33 +259,52 @@ struct PerfJitDumpState
return 0;
}

int LogMethod(void* pCode, size_t codeSize, const char* symbol, void* debugInfo, void* unwindInfo, bool reportCodeBlock)
int LogMethod(void* pCode, size_t codeSize, const char* symbol, const void* debugInfo, size_t debugInfoSize, const void* unwindInfo, size_t unwindInfoSize, bool reportCodeBlock)
{
int result = 0;

(void)unwindInfo;
(void)unwindInfoSize;

if (enabled)
{
size_t symbolLen = strlen(symbol);

JitCodeLoadRecord record;
JitCodeDebugInfoRecord debugRecord;

size_t reportedCodeSize = reportCodeBlock ? codeSize : 0;

size_t bytesRemaining = sizeof(JitCodeLoadRecord) + symbolLen + 1 + reportedCodeSize;
size_t bytesRemaining =
(debugInfo != nullptr ? sizeof(JitCodeDebugInfoRecord) + debugInfoSize : 0) +
sizeof(JitCodeLoadRecord) +
symbolLen +
1 +
reportedCodeSize;

record.header.timestamp = GetTimeStampNS();
record.vma = (uint64_t) pCode;
record.code_addr = (uint64_t) pCode;
record.code_size = codeSize;
record.header.total_size = bytesRemaining;
record.code_size = reportedCodeSize;
record.header.total_size = sizeof(JitCodeLoadRecord) + symbolLen + 1 + reportedCodeSize;

debugRecord.header.timestamp = record.header.timestamp;
debugRecord.header.total_size = sizeof(JitCodeDebugInfoRecord) + debugInfoSize;

iovec items[5];
size_t itemsCount = 0;

if (debugInfo != nullptr)
{
items[itemsCount++] = { &debugRecord, sizeof(JitCodeDebugInfoRecord) };
items[itemsCount++] = { const_cast<void*>(debugInfo), debugInfoSize };
}

// TODO: insert unwindInfo record items here.

iovec items[] = {
// ToDo insert debugInfo and unwindInfo record items immediately before the JitCodeLoadRecord.
{ &record, sizeof(JitCodeLoadRecord) },
{ (void *)symbol, symbolLen + 1 },
{ pCode, reportedCodeSize },
};
size_t itemsCount = sizeof(items) / sizeof(items[0]);
items[itemsCount++] = { &record, sizeof(JitCodeLoadRecord) };
items[itemsCount++] = { (void *)symbol, symbolLen + 1 };
items[itemsCount++] = { pCode, reportedCodeSize };

size_t itemsWritten = 0;

Expand Down Expand Up @@ -389,9 +420,9 @@ PAL_PerfJitDump_IsStarted()

int
PALAPI
PAL_PerfJitDump_LogMethod(void* pCode, size_t codeSize, const char* symbol, void* debugInfo, void* unwindInfo, bool reportCodeBlock)
PAL_PerfJitDump_LogMethod(void* pCode, size_t codeSize, const char* symbol, const void* debugInfo, size_t debugInfoSize, const void* unwindInfo, size_t unwindInfoSize, bool reportCodeBlock)
{
return GetState().LogMethod(pCode, codeSize, symbol, debugInfo, unwindInfo, reportCodeBlock);
return GetState().LogMethod(pCode, codeSize, symbol, debugInfo, debugInfoSize, unwindInfo, unwindInfoSize, reportCodeBlock);
}

int
Expand Down Expand Up @@ -419,7 +450,7 @@ PAL_PerfJitDump_IsStarted()

int
PALAPI
PAL_PerfJitDump_LogMethod(void* pCode, size_t codeSize, const char* symbol, void* debugInfo, void* unwindInfo, bool reportCodeBlock)
PAL_PerfJitDump_LogMethod(void* pCode, size_t codeSize, const char* symbol, const void* debugInfo, size_t debugInfoSize, const void* unwindInfo, size_t unwindInfoSize, bool reportCodeBlock)
{
return 0;
}
Expand Down
24 changes: 22 additions & 2 deletions src/coreclr/vm/gdbjit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,12 +498,18 @@ GetDebugInfoFromPDB(MethodDesc* methodDescPtr,
unsigned int &symInfoLen,
LocalsInfo &locals)
{
// Guard against re-entrancy
static thread_local int t_gdbJitDebugInfoCallbackDepth = 0;

NewArrayHolder<DebuggerILToNativeMap> map;
ULONG32 numMap;

if (!getInfoForMethodDelegate)
return E_FAIL;

if (t_gdbJitDebugInfoCallbackDepth != 0)
return E_FAIL;

if (GetMethodNativeMap(methodDescPtr, &numMap, map, &locals.countVars, &locals.vars) != S_OK)
return E_FAIL;

Expand All @@ -516,18 +522,32 @@ GetDebugInfoFromPDB(MethodDesc* methodDescPtr,

MethodDebugInfo methodDebugInfo(numMap, locals.countVars);

if (getInfoForMethodDelegate(szModName, methodDescPtr->GetMemberDef(), methodDebugInfo) == FALSE)
t_gdbJitDebugInfoCallbackDepth = 1;
if (getInfoForMethodDelegate(szModName, methodDescPtr->GetMemberDef(), &methodDebugInfo) == 0)
{
t_gdbJitDebugInfoCallbackDepth = 0;
return E_FAIL;
}
t_gdbJitDebugInfoCallbackDepth = 0;

symInfoLen = numMap;
symInfo = new SymbolsInfo[numMap];

locals.size = methodDebugInfo.localsSize;
// Only consume locals if both pointer and size are valid.
locals.size = (methodDebugInfo.locals != nullptr && methodDebugInfo.localsSize > 0) ? methodDebugInfo.localsSize : 0;
locals.localsName = new NewArrayHolder<char>[locals.size];
locals.localsScope = new LocalsInfo::Scope [locals.size];

for (int i = 0; i < locals.size; i++)
{
if (methodDebugInfo.locals[i].name == nullptr)
{
locals.localsName[i] = nullptr;
locals.localsScope[i].ilStartOffset = 0;
locals.localsScope[i].ilEndOffset = 0;
continue;
}

size_t sizeRequired = WideCharToMultiByte(CP_UTF8, 0, methodDebugInfo.locals[i].name, -1, NULL, 0, NULL, NULL);
locals.localsName[i] = new char[sizeRequired];

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/gdbjithelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ struct MethodDebugInfo
~MethodDebugInfo();
};

typedef BOOL (CALLBACK *GetInfoForMethodDelegate)(const char*, unsigned int, MethodDebugInfo& methodDebugInfo);
typedef int (CALLBACK *GetInfoForMethodDelegate)(const char*, unsigned int, MethodDebugInfo* methodDebugInfo);
extern GetInfoForMethodDelegate getInfoForMethodDelegate;

#endif // !__GDBJITHELPERS_H__
Loading
Loading