Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordotnet-bot <dotnet-bot@microsoft.com>2015-12-01 06:54:09 +0300
committerAlex Ghiondea <ghiondea.alexandru@microsoft.com>2015-12-01 20:44:51 +0300
commit9d362c1b211974aff8eb7a411d371713b48b2e5c (patch)
tree0e58dfbc6549827b50c9dd355688cca914a4776e /src/System.Private.StackTraceGenerator
parent8199af35a5a5bbf932417d6b9c59809ced819bfd (diff)
Port StackTraceGenerator to CoreRT
[tfs-changeset: 1552759]
Diffstat (limited to 'src/System.Private.StackTraceGenerator')
-rw-r--r--src/System.Private.StackTraceGenerator/src/Internal/Dia/DiaEnums.cs106
-rw-r--r--src/System.Private.StackTraceGenerator/src/Internal/Dia/DiaInterfaces.cs282
-rw-r--r--src/System.Private.StackTraceGenerator/src/Internal/Dia/Guids.cs22
-rw-r--r--src/System.Private.StackTraceGenerator/src/Internal/Dia/StdCall.cs25
-rw-r--r--src/System.Private.StackTraceGenerator/src/Internal/LightweightInterop/ComInterface.cs53
-rw-r--r--src/System.Private.StackTraceGenerator/src/Internal/LightweightInterop/MarshalExtensions.cs39
-rw-r--r--src/System.Private.StackTraceGenerator/src/Internal/LightweightInterop/StdCall.cs16
-rw-r--r--src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/Attributes.cs9
-rw-r--r--src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/StackTraceGenerator.cs611
-rw-r--r--src/System.Private.StackTraceGenerator/src/System.Private.StackTraceGenerator.csproj59
10 files changed, 1222 insertions, 0 deletions
diff --git a/src/System.Private.StackTraceGenerator/src/Internal/Dia/DiaEnums.cs b/src/System.Private.StackTraceGenerator/src/Internal/Dia/DiaEnums.cs
new file mode 100644
index 000000000..67bc5af7f
--- /dev/null
+++ b/src/System.Private.StackTraceGenerator/src/Internal/Dia/DiaEnums.cs
@@ -0,0 +1,106 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using global::System;
+using global::System.Diagnostics;
+
+namespace Internal.StackGenerator.Dia
+{
+ internal enum SymTagEnum
+ {
+ SymTagNull,
+ SymTagExe,
+ SymTagCompiland,
+ SymTagCompilandDetails,
+ SymTagCompilandEnv,
+ SymTagFunction,
+ SymTagBlock,
+ SymTagData,
+ SymTagAnnotation,
+ SymTagLabel,
+ SymTagPublicSymbol,
+ SymTagUDT,
+ SymTagEnum,
+ SymTagFunctionType,
+ SymTagPointerType,
+ SymTagArrayType,
+ SymTagBaseType,
+ SymTagTypedef,
+ SymTagBaseClass,
+ SymTagFriend,
+ SymTagFunctionArgType,
+ SymTagFuncDebugStart,
+ SymTagFuncDebugEnd,
+ SymTagUsingNamespace,
+ SymTagVTableShape,
+ SymTagVTable,
+ SymTagCustom,
+ SymTagThunk,
+ SymTagCustomType,
+ SymTagManagedType,
+ SymTagDimension,
+ SymTagCallSite,
+ SymTagInlineSite,
+ SymTagBaseInterface,
+ SymTagVectorType,
+ SymTagMatrixType,
+ SymTagHLSLType,
+ SymTagMax
+ }
+
+ internal enum NameSearchOptions
+ {
+ nsNone,
+ nsfCaseSensitive = 0x1,
+ nsfCaseInsensitive = 0x2,
+ nsfFNameExt = 0x4,
+ nsfRegularExpression = 0x8,
+ nsfUndecoratedName = 0x10,
+
+ // For backward compabibility:
+ nsCaseSensitive = nsfCaseSensitive,
+ nsCaseInsensitive = nsfCaseInsensitive,
+ nsFNameExt = nsfFNameExt,
+ nsRegularExpression = nsfRegularExpression | nsfCaseSensitive,
+ nsCaseInRegularExpression = nsfRegularExpression | nsfCaseInsensitive
+ }
+
+ internal enum BasicType
+ {
+ btNoType = 0,
+ btVoid = 1,
+ btChar = 2,
+ btWChar = 3,
+ btInt = 6,
+ btUInt = 7,
+ btFloat = 8,
+ btBCD = 9,
+ btBool = 10,
+ btLong = 13,
+ btULong = 14,
+ btCurrency = 25,
+ btDate = 26,
+ btVariant = 27,
+ btComplex = 28,
+ btBit = 29,
+ btBSTR = 30,
+ btHresult = 31,
+
+ btMAX = 0xffff
+ }
+
+ internal enum DataKind
+ {
+ DataIsUnknown,
+ DataIsLocal,
+ DataIsStaticLocal,
+ DataIsParam,
+ DataIsObjectPtr,
+ DataIsFileStatic,
+ DataIsGlobal,
+ DataIsMember,
+ DataIsStaticMember,
+ DataIsConstant
+ }
+}
+
diff --git a/src/System.Private.StackTraceGenerator/src/Internal/Dia/DiaInterfaces.cs b/src/System.Private.StackTraceGenerator/src/Internal/Dia/DiaInterfaces.cs
new file mode 100644
index 000000000..abf8c9c31
--- /dev/null
+++ b/src/System.Private.StackTraceGenerator/src/Internal/Dia/DiaInterfaces.cs
@@ -0,0 +1,282 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using global::System;
+using global::System.Diagnostics;
+using global::Internal.LightweightInterop;
+
+namespace Internal.StackGenerator.Dia
+{
+ internal sealed class IDiaDataSource : ComInterface
+ {
+ public IDiaDataSource(IntPtr punk)
+ : base(punk)
+ {
+ }
+
+ public int LoadDataFromPdb(String pdbPath)
+ {
+ unsafe
+ {
+ fixed (char* _pdbPath = pdbPath)
+ {
+ int hr = S.StdCall<int>(GetVTableMember(4), Punk, _pdbPath);
+ GC.KeepAlive(this);
+ return hr;
+ }
+ }
+ }
+
+ public int OpenSession(out IDiaSession session)
+ {
+ session = null;
+ IntPtr _session;
+ int hr = S.StdCall<int>(GetVTableMember(8), Punk, out _session);
+ GC.KeepAlive(this);
+ if (hr != S_OK)
+ return hr;
+ session = new IDiaSession(_session);
+ return hr;
+ }
+ }
+
+ internal sealed class IDiaSession : ComInterface
+ {
+ public IDiaSession(IntPtr punk)
+ : base(punk)
+ {
+ }
+
+ public int FindChildren(IDiaSymbol parent, SymTagEnum symTag, String name, NameSearchOptions compareFlags, out IDiaEnumSymbols enumSymbols)
+ {
+ enumSymbols = null;
+ IntPtr _enumSymbols;
+ int hr;
+ unsafe
+ {
+ fixed (char* _name = name)
+ {
+ hr = S.StdCall<int>(GetVTableMember(8), Punk, parent.Punk, (int)symTag, _name, (int)compareFlags, out _enumSymbols);
+ }
+ }
+ GC.KeepAlive(this);
+ GC.KeepAlive(parent);
+ if (hr != S_OK)
+ return hr;
+ enumSymbols = new IDiaEnumSymbols(_enumSymbols);
+ return hr;
+ }
+
+
+ public int FindSymbolByRVA(int rva, SymTagEnum symTag, out IDiaSymbol symbol)
+ {
+ symbol = null;
+ IntPtr _symbol;
+ int hr = S.StdCall<int>(GetVTableMember(14), Punk, rva, (int)symTag, out _symbol);
+ GC.KeepAlive(this);
+ if (hr != S_OK)
+ return hr;
+ symbol = new IDiaSymbol(_symbol);
+ return hr;
+ }
+
+ public int FindLinesByRVA(int rva, int length, out IDiaEnumLineNumbers enumLineNumbers)
+ {
+ enumLineNumbers = null;
+ IntPtr _enumLineNumbers;
+ int hr = S.StdCall<int>(GetVTableMember(25), Punk, rva, length, out _enumLineNumbers);
+ GC.KeepAlive(this);
+ if (hr != S_OK)
+ return hr;
+ enumLineNumbers = new IDiaEnumLineNumbers(_enumLineNumbers);
+ return hr;
+ }
+ }
+
+ internal sealed class IDiaEnumSymbols : ComInterface
+ {
+ public IDiaEnumSymbols(IntPtr punk)
+ : base(punk)
+ {
+ }
+
+ public int Count(out int count)
+ {
+ int hr = S.StdCall<int>(GetVTableMember(4), Punk, out count);
+ GC.KeepAlive(this);
+ return hr;
+ }
+
+ public int Item(int index, out IDiaSymbol symbol)
+ {
+ symbol = null;
+ IntPtr pSymbol;
+ int hr = S.StdCall<int>(GetVTableMember(5), Punk, index, out pSymbol);
+ GC.KeepAlive(this);
+ if (hr != S_OK)
+ return hr;
+ symbol = new IDiaSymbol(pSymbol);
+ return hr;
+ }
+ }
+
+ internal sealed class IDiaSymbol : ComInterface
+ {
+ public IDiaSymbol(IntPtr punk)
+ : base(punk)
+ {
+ }
+
+ public int GetSymTag(out SymTagEnum symTagEnum)
+ {
+ symTagEnum = default(SymTagEnum);
+ int _symTagEnum;
+ int hr = S.StdCall<int>(GetVTableMember(4), Punk, out _symTagEnum);
+ GC.KeepAlive(this);
+ symTagEnum = (SymTagEnum)_symTagEnum;
+ return hr;
+ }
+
+ public int GetName(out String name)
+ {
+ name = null;
+ IntPtr _name;
+ int hr = S.StdCall<int>(GetVTableMember(5), Punk, out _name);
+ GC.KeepAlive(this);
+ if (hr != S_OK)
+ return hr;
+ name = _name.MarshalBstr();
+ return hr;
+ }
+
+ public int GetType(out IDiaSymbol symbol)
+ {
+ symbol = null;
+ IntPtr _symbol;
+ int hr = S.StdCall<int>(GetVTableMember(8), Punk, out _symbol);
+ GC.KeepAlive(this);
+ if (hr != S_OK)
+ return hr;
+ symbol = new IDiaSymbol(_symbol);
+ return hr;
+ }
+
+ public int GetDataKind(out DataKind dataKind)
+ {
+ dataKind = default(DataKind);
+ int _dataKindEnum;
+ int hr = S.StdCall<int>(GetVTableMember(9), Punk, out _dataKindEnum);
+ GC.KeepAlive(this);
+ dataKind = (DataKind)_dataKindEnum;
+ return hr;
+ }
+
+ public int GetReference(out bool isReference)
+ {
+ isReference = false;
+ int _isReference;
+ int hr = S.StdCall<int>(GetVTableMember(48), Punk, out _isReference);
+ GC.KeepAlive(this);
+ isReference = (_isReference != 0);
+ return hr;
+ }
+
+ public int GetBaseType(out BasicType baseType)
+ {
+ baseType = default(BasicType);
+ int _baseType;
+ int hr = S.StdCall<int>(GetVTableMember(43), Punk, out _baseType);
+ GC.KeepAlive(this);
+ baseType = (BasicType)_baseType;
+ return hr;
+ }
+
+ public int GetLength(out long length)
+ {
+ int hr = S.StdCall<int>(GetVTableMember(17), Punk, out length);
+ GC.KeepAlive(this);
+ return hr;
+ }
+ }
+
+ internal sealed class IDiaEnumLineNumbers : ComInterface
+ {
+ public IDiaEnumLineNumbers(IntPtr punk)
+ : base(punk)
+ {
+ }
+
+ public int Count(out int count)
+ {
+ int hr = S.StdCall<int>(GetVTableMember(4), Punk, out count);
+ GC.KeepAlive(this);
+ return hr;
+ }
+
+ public int Item(int index, out IDiaLineNumber lineNumber)
+ {
+ lineNumber = null;
+ IntPtr pLineNumber;
+ int hr = S.StdCall<int>(GetVTableMember(5), Punk, index, out pLineNumber);
+ GC.KeepAlive(this);
+ if (hr != S_OK)
+ return hr;
+ lineNumber = new IDiaLineNumber(pLineNumber);
+ return hr;
+ }
+ }
+
+ internal sealed class IDiaLineNumber : ComInterface
+ {
+ public IDiaLineNumber(IntPtr punk)
+ : base(punk)
+ {
+ }
+
+ public int SourceFile(out IDiaSourceFile sourceFile)
+ {
+ sourceFile = null;
+ IntPtr _sourceFile;
+ int hr = S.StdCall<int>(GetVTableMember(4), Punk, out _sourceFile);
+ GC.KeepAlive(this);
+ if (hr != S_OK)
+ return hr;
+ sourceFile = new IDiaSourceFile(_sourceFile);
+ return hr;
+ }
+
+ public int LineNumber(out int lineNumber)
+ {
+ int hr = S.StdCall<int>(GetVTableMember(5), Punk, out lineNumber);
+ GC.KeepAlive(this);
+ return hr;
+ }
+
+ public int ColumnNumber(out int columnNumber)
+ {
+ int hr = S.StdCall<int>(GetVTableMember(7), Punk, out columnNumber);
+ GC.KeepAlive(this);
+ return hr;
+ }
+ }
+
+ internal sealed class IDiaSourceFile : ComInterface
+ {
+ public IDiaSourceFile(IntPtr punk)
+ : base(punk)
+ {
+ }
+
+ public int FileName(out String fileName)
+ {
+ fileName = null;
+ IntPtr _fileName;
+ int hr = S.StdCall<int>(GetVTableMember(4), Punk, out _fileName);
+ GC.KeepAlive(this);
+ if (hr != S_OK)
+ return hr;
+ fileName = _fileName.MarshalBstr();
+ return hr;
+ }
+ }
+}
diff --git a/src/System.Private.StackTraceGenerator/src/Internal/Dia/Guids.cs b/src/System.Private.StackTraceGenerator/src/Internal/Dia/Guids.cs
new file mode 100644
index 000000000..21fb5d7fa
--- /dev/null
+++ b/src/System.Private.StackTraceGenerator/src/Internal/Dia/Guids.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using global::System;
+using global::System.Diagnostics;
+using global::System.Collections.Generic;
+
+namespace Internal.StackGenerator.Dia
+{
+ internal static class Guids
+ {
+ public static readonly IEnumerable<Guid> DiaSource_CLSIDs =
+ new Guid[]
+ {
+ new Guid("3BFCEA48-620F-4B6B-81F7-B9AF75454C7D"), // msdia120.dll
+ new Guid("761D3BCD-1304-41D5-94E8-EAC54E4AC172"), // msdia110.dll
+ };
+
+ public static readonly Guid IID_IDiaDataSource = new Guid("79F1BB5F-B66E-48E5-B6A9-1545C323CA3D");
+ }
+}
+
diff --git a/src/System.Private.StackTraceGenerator/src/Internal/Dia/StdCall.cs b/src/System.Private.StackTraceGenerator/src/Internal/Dia/StdCall.cs
new file mode 100644
index 000000000..6c2dc1e64
--- /dev/null
+++ b/src/System.Private.StackTraceGenerator/src/Internal/Dia/StdCall.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using global::System;
+using global::System.Diagnostics;
+using global::System.Runtime.InteropServices;
+
+namespace Internal.StackGenerator.Dia
+{
+ [McgIntrinsics]
+ internal static class S
+ {
+ public static unsafe T StdCall<T>(IntPtr pMethod, IntPtr pThis) { throw NotImplemented.ByDesign; }
+ public static unsafe T StdCall<T>(IntPtr pMethod, IntPtr pThis, char* pc) { throw NotImplemented.ByDesign; }
+ public static unsafe T StdCall<T>(IntPtr pMethod, IntPtr pThis, out IntPtr ppOut) { throw NotImplemented.ByDesign; }
+ public static unsafe T StdCall<T>(IntPtr pMethod, IntPtr pThis, out long ppOut) { throw NotImplemented.ByDesign; }
+ public static unsafe T StdCall<T>(IntPtr pMethod, IntPtr pThis, out int pOut) { throw NotImplemented.ByDesign; }
+ public static unsafe T StdCall<T>(IntPtr pMethod, IntPtr pThis, int i, out IntPtr ppOut) { throw NotImplemented.ByDesign; }
+ public static unsafe T StdCall<T>(IntPtr pMethod, IntPtr pThis, int i, int j, out IntPtr ppOut) { throw NotImplemented.ByDesign; }
+ public static unsafe T StdCall<T>(IntPtr pMethod, IntPtr pThis, IntPtr parent, int symTag, char* name, int compareFlags, out IntPtr ppResult) { throw NotImplemented.ByDesign; }
+ }
+}
+
+
+
diff --git a/src/System.Private.StackTraceGenerator/src/Internal/LightweightInterop/ComInterface.cs b/src/System.Private.StackTraceGenerator/src/Internal/LightweightInterop/ComInterface.cs
new file mode 100644
index 000000000..e0f1d2982
--- /dev/null
+++ b/src/System.Private.StackTraceGenerator/src/Internal/LightweightInterop/ComInterface.cs
@@ -0,0 +1,53 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using global::System;
+using global::System.Diagnostics;
+
+namespace Internal.LightweightInterop
+{
+ //
+ // Lightweight managed wrapper for a COM interface pointer. This is used for the very limited purpose of accessing Dia to generate decent stack traces.
+ // Thus, the support is intentionally minimal:
+ //
+ // - Each ComIfc wraps a single COM interface pointer and owns one ref-count: there's no attempt to make COM identity match managed object identity.
+ //
+ internal unsafe abstract class ComInterface
+ {
+ public const int S_OK = 0;
+
+ protected ComInterface(IntPtr punk)
+ {
+ Punk = punk;
+ }
+
+ public IntPtr Punk { get; private set; } // Unmanaged COM interface pointer.
+
+ protected IntPtr GetVTableMember(int index)
+ {
+ unsafe
+ {
+ IntPtr* pVTable = *((IntPtr**)Punk);
+ IntPtr member = pVTable[index];
+ return member;
+ }
+ }
+
+ private void Release()
+ {
+ IntPtr punk = Punk;
+ Punk = (IntPtr)0;
+ if (punk != (IntPtr)0)
+ {
+ IntPtr* pVTable = *((IntPtr**)punk);
+ IntPtr releaseMember = pVTable[2];
+ S.StdCall<uint>(releaseMember, punk);
+ }
+ }
+
+ ~ComInterface()
+ {
+ this.Release();
+ }
+ }
+}
diff --git a/src/System.Private.StackTraceGenerator/src/Internal/LightweightInterop/MarshalExtensions.cs b/src/System.Private.StackTraceGenerator/src/Internal/LightweightInterop/MarshalExtensions.cs
new file mode 100644
index 000000000..4d2b7030c
--- /dev/null
+++ b/src/System.Private.StackTraceGenerator/src/Internal/LightweightInterop/MarshalExtensions.cs
@@ -0,0 +1,39 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using global::System;
+using global::System.Diagnostics;
+using global::System.Runtime.InteropServices;
+
+namespace Internal.LightweightInterop
+{
+ internal static class MarshalExtensions
+ {
+ public static String MarshalBstr(this IntPtr bstr)
+ {
+ unsafe
+ {
+ if (bstr == ((IntPtr)0))
+ return null;
+ try
+ {
+ char* pc = (char*)bstr;
+ int cchLen = 0;
+ // This marshaler is for stack traces so if a string looks suspiciously long, chop it.
+ while (pc[cchLen] != '\0' && cchLen < 300)
+ {
+ cchLen++;
+ }
+ return new String(pc, 0, cchLen);
+ }
+ finally
+ {
+ SysFreeString(bstr);
+ }
+ }
+ }
+
+ [DllImport("OleAut32")]
+ private static extern void SysFreeString(IntPtr bstr);
+ }
+}
diff --git a/src/System.Private.StackTraceGenerator/src/Internal/LightweightInterop/StdCall.cs b/src/System.Private.StackTraceGenerator/src/Internal/LightweightInterop/StdCall.cs
new file mode 100644
index 000000000..7d2f18ddc
--- /dev/null
+++ b/src/System.Private.StackTraceGenerator/src/Internal/LightweightInterop/StdCall.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using global::System;
+using global::System.Diagnostics;
+using global::System.Runtime.InteropServices;
+
+namespace Internal.LightweightInterop
+{
+ [McgIntrinsics]
+ internal static class S
+ {
+ public static T StdCall<T>(IntPtr pMethod, IntPtr pThis) { throw NotImplemented.ByDesign; }
+ }
+}
+
diff --git a/src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/Attributes.cs b/src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/Attributes.cs
new file mode 100644
index 000000000..30fe725ba
--- /dev/null
+++ b/src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/Attributes.cs
@@ -0,0 +1,9 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace System.Runtime.InteropServices
+{
+ internal class McgIntrinsicsAttribute : Attribute
+ {
+ }
+} \ No newline at end of file
diff --git a/src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/StackTraceGenerator.cs b/src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/StackTraceGenerator.cs
new file mode 100644
index 000000000..ab91659d4
--- /dev/null
+++ b/src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/StackTraceGenerator.cs
@@ -0,0 +1,611 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using global::System;
+using global::System.Text;
+using global::System.Diagnostics;
+using global::System.Runtime.InteropServices;
+using global::Internal.Runtime.Augments;
+using global::Internal.StackGenerator.Dia;
+
+namespace Internal.StackTraceGenerator
+{
+ public static class StackTraceGenerator
+ {
+ //
+ // Makes reasonable effort to construct one useful line of a stack trace. Returns null if it can't.
+ //
+ public static String CreateStackTraceString(IntPtr ip, bool includeFileInfo)
+ {
+ try
+ {
+ int hr;
+
+ int rva;
+ IDiaSession session = GetDiaSession(ip, out rva);
+ if (session == null)
+ return null;
+
+ StringBuilder sb = new StringBuilder();
+ IDiaSymbol symbol;
+ hr = session.FindSymbolByRVA(rva, SymTagEnum.SymTagFunction, out symbol);
+ if (hr != S_OK)
+ return null;
+ String functionName;
+ hr = symbol.GetName(out functionName);
+ if (hr == S_OK)
+ sb.Append(functionName.Demanglify());
+ else
+ sb.Append("<Function Name Not Available>");
+
+ sb.Append(CreateParameterListString(session, symbol));
+
+ if (includeFileInfo)
+ {
+ sb.Append(CreateSourceInfoString(session, rva));
+ }
+
+ return sb.ToString();
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ //
+ // Makes reasonable effort to get source info. Returns null sourceFile and 0 lineNumber/columnNumber if it can't.
+ //
+ public static void TryGetSourceLineInfo(IntPtr ip, out string fileName, out int lineNumber, out int columnNumber)
+ {
+ fileName = null;
+ lineNumber = 0;
+ columnNumber = 0;
+ int rva;
+ IDiaSession session = GetDiaSession(ip, out rva);
+ if (session == null)
+ return;
+ TryGetSourceLineInfo(session, rva, out fileName, out lineNumber, out columnNumber);
+ }
+
+ //
+ // Get a IDiaDataSource object
+ //
+ private static IDiaDataSource GetDiaDataSource()
+ {
+ Guid iid = Guids.IID_IDiaDataSource;
+ int hr;
+ foreach (Guid clsId in Guids.DiaSource_CLSIDs)
+ {
+ unsafe
+ {
+ byte[] _clsid = clsId.ToByteArray();
+ byte[] _iid = iid.ToByteArray();
+ fixed (byte* pclsid = _clsid)
+ {
+ fixed (byte* piid = _iid)
+ {
+ IntPtr _dataSource;
+ hr = CoCreateInstance(pclsid, (IntPtr)0, CLSCTX_INPROC, piid, out _dataSource);
+ if (hr == S_OK)
+ return new IDiaDataSource(_dataSource);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ //
+ // Create the method parameter list.
+ //
+ private static String CreateParameterListString(IDiaSession session, IDiaSymbol symbol)
+ {
+ StringBuilder sb = new StringBuilder("(");
+
+ // find the parameters
+ IDiaEnumSymbols dataSymbols;
+ int hr = session.FindChildren(symbol, SymTagEnum.SymTagData, null, NameSearchOptions.nsNone, out dataSymbols);
+ if (hr == S_OK)
+ {
+ int count;
+ hr = dataSymbols.Count(out count);
+ if (hr == S_OK)
+ {
+ for (int i = 0, iParam = 0; i < count; i++)
+ {
+ IDiaSymbol dataSym;
+ hr = dataSymbols.Item(i, out dataSym);
+ if (hr != S_OK)
+ continue;
+
+ DataKind dataKind;
+ hr = dataSym.GetDataKind(out dataKind);
+ if (hr != S_OK || dataKind != DataKind.DataIsParam)
+ continue;
+
+ string paramName;
+ hr = dataSym.GetName(out paramName);
+ if (hr != S_OK)
+ {
+ continue;
+ }
+
+ //this approximates the way C# displays methods by not including these hidden arguments
+ if (paramName == "InstParam" || paramName == "this")
+ {
+ continue;
+ }
+
+ IDiaSymbol parameterType;
+ hr = dataSym.GetType(out parameterType);
+ if (hr != S_OK)
+ {
+ continue;
+ }
+
+ if (iParam++ != 0)
+ sb.Append(", ");
+
+ sb.Append(parameterType.ToTypeString(session));
+ sb.Append(' ');
+ sb.Append(paramName);
+ }
+ }
+ }
+ sb.Append(')');
+ return sb.ToString();
+ }
+
+ //
+ // Retrieve the source fileName, line number, and column
+ //
+ private static void TryGetSourceLineInfo(IDiaSession session, int rva, out string fileName, out int lineNumber, out int columnNumber)
+ {
+ fileName = null;
+ lineNumber = 0;
+ columnNumber = 0;
+ IDiaEnumLineNumbers lineNumbers;
+ int hr = session.FindLinesByRVA(rva, 1, out lineNumbers);
+ if (hr == S_OK)
+ {
+ int numLineNumbers;
+ hr = lineNumbers.Count(out numLineNumbers);
+ if (hr == S_OK && numLineNumbers > 0)
+ {
+ IDiaLineNumber ln;
+ hr = lineNumbers.Item(0, out ln);
+ if (hr == S_OK)
+ {
+ IDiaSourceFile sourceFile;
+ hr = ln.SourceFile(out sourceFile);
+ if (hr == S_OK)
+ {
+ hr = sourceFile.FileName(out fileName);
+ if (hr == S_OK)
+ {
+ hr = ln.LineNumber(out lineNumber);
+ if (hr == S_OK)
+ {
+ hr = ln.ColumnNumber(out columnNumber);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // Generate the " in <filename>:line <line#>" section.
+ //
+ private static String CreateSourceInfoString(IDiaSession session, int rva)
+ {
+ StringBuilder sb = new StringBuilder();
+ string fileName;
+ int lineNumber, columnNumber;
+ TryGetSourceLineInfo(session, rva, out fileName, out lineNumber, out columnNumber);
+ if(!string.IsNullOrEmpty(fileName))
+ {
+ sb.Append(" in ").Append(fileName);
+ if(lineNumber >= 0)
+ {
+ sb.Append(":line ").Append(lineNumber);
+ }
+ }
+ return sb.ToString();
+ }
+
+ //
+ // Clean up all the "$2_" sweetness that ILMerge contributes and
+ // replace type-name separator "::" by "."
+ //
+ private static String Demanglify(this String s)
+ {
+ StringBuilder sb = new StringBuilder();
+ int i = 0;
+ while (i < s.Length)
+ {
+ sb.Append(s[i++]);
+
+ if (i == s.Length)
+ continue;
+
+ if (s[i - 1] == '$' && Char.IsNumber(s[i]))
+ {
+ if (i != 1 && (s[i - 2] != ' ' && s[i - 2] != '<'))
+ continue;
+ int lookAhead = i + 1;
+ while (lookAhead < s.Length && Char.IsNumber(s[lookAhead]))
+ lookAhead++;
+ if (lookAhead == s.Length || s[lookAhead] != '_')
+ continue;
+ sb = sb.Remove(sb.Length - 1, 1);
+ i = lookAhead + 1;
+ }
+ else if (s[i - 1] == ':' && s[i] == ':')
+ {
+ sb.Remove(sb.Length - 1, 1);
+ sb.Append('.');
+ i++;
+ }
+ }
+
+ return sb.ToString();
+ }
+
+ private static String ToTypeString(this IDiaSymbol parameterType, IDiaSession session)
+ {
+ bool ignore;
+ return parameterType.ToTypeStringWorker(session, 0, out ignore);
+ }
+
+ private static String ToTypeStringWorker(this IDiaSymbol parameterType, IDiaSession session, int recursionLevel, out bool isValueTypeOrByRef)
+ {
+ int hr;
+ isValueTypeOrByRef = false;
+
+ // Block runaway recursions.
+ if (recursionLevel++ > 10)
+ return "?";
+
+ SymTagEnum symTag;
+ hr = parameterType.GetSymTag(out symTag);
+ if (hr != S_OK)
+ return "?";
+ if (symTag == SymTagEnum.SymTagPointerType)
+ {
+ bool isReference;
+ hr = parameterType.GetReference(out isReference);
+ if (hr != S_OK)
+ return "?";
+
+ if (isReference)
+ {
+ // An isReference pointer can mean one of two things:
+ // 1. ELEMENT_TYPE_BYREF
+ // 2. An indication that the UDT that follows is actually a class, not a struct.
+ //
+ isValueTypeOrByRef = true;
+ IDiaSymbol targetType;
+ hr = parameterType.GetType(out targetType);
+ if (hr != S_OK)
+ return "?";
+ bool targetIsValueTypeOrByRef;
+ String targetTypeString = targetType.ToTypeStringWorker(session, recursionLevel, out targetIsValueTypeOrByRef);
+ if (targetIsValueTypeOrByRef)
+ return targetTypeString + "&";
+ else
+ return targetTypeString;
+ }
+ else
+ {
+ // A non-isReference pointer means an ELEMENT_TYPE_PTR
+ IDiaSymbol targetType;
+ hr = parameterType.GetType(out targetType);
+ if (hr != S_OK)
+ return "?";
+ bool ignore;
+ return targetType.ToTypeStringWorker(session, recursionLevel, out ignore) + "*";
+ }
+ }
+ else if (symTag == SymTagEnum.SymTagArrayType)
+ {
+ // Note: We don't actually hit this case in NUTC-generated PDB's as NUTC emits arrays as if they were UDT's with square brackets in the name.
+ // But just in case NUTC ever changes its PDB emission, we'll print out the most obvious interpretation and hope we're right.
+ IDiaSymbol elementType;
+ hr = parameterType.GetType(out elementType);
+ if (hr != S_OK)
+ return "?";
+ bool ignore;
+ return elementType.ToTypeStringWorker(session, recursionLevel, out ignore) + "[]";
+ }
+ else if (symTag == SymTagEnum.SymTagUDT || symTag == SymTagEnum.SymTagEnum)
+ {
+ // Need to figure out whether this is a value type as our recursive caller needs to know whether the "byref pointer" that wrapped this
+ // is a true managed byref or just the "byref pointer" that wraps all non-valuetypes.
+ if (symTag == SymTagEnum.SymTagEnum)
+ {
+ isValueTypeOrByRef = true;
+ }
+ else
+ {
+ IDiaEnumSymbols baseClasses;
+ hr = session.FindChildren(parameterType, SymTagEnum.SymTagBaseClass, null, 0, out baseClasses);
+ if (hr != S_OK)
+ return "?";
+ int count;
+ hr = baseClasses.Count(out count);
+ if (hr != S_OK)
+ return "?";
+ for (int i = 0; i < count; i++)
+ {
+ IDiaSymbol baseClass;
+ if (S_OK == baseClasses.Item(i, out baseClass))
+ {
+ String baseClassName;
+ if (S_OK == baseClass.GetName(out baseClassName))
+ {
+ if (baseClassName == "System::ValueType")
+ isValueTypeOrByRef = true;
+ }
+ }
+ }
+ }
+
+ String name;
+ hr = parameterType.GetName(out name);
+ if (hr != S_OK)
+ return "?";
+ return name.RemoveNamespaces().Demanglify();
+ }
+ else if (symTag == SymTagEnum.SymTagBaseType)
+ {
+ // Certain "primitive" types are encoded specially.
+ BasicType basicType;
+ hr = parameterType.GetBaseType(out basicType);
+ if (hr != S_OK)
+ return "?";
+ long length;
+ hr = parameterType.GetLength(out length);
+ if (hr != S_OK)
+ return "?";
+ return ConvertBasicTypeToTypeString(basicType, length, out isValueTypeOrByRef);
+ }
+ else
+ {
+ return "?";
+ }
+ }
+
+ private static String ConvertBasicTypeToTypeString(BasicType basicType, long length, out bool isValueTypeOrByRef)
+ {
+ isValueTypeOrByRef = true;
+ switch (basicType)
+ {
+ case BasicType.btNoType:
+ return "Unknown";
+
+ case BasicType.btVoid:
+ return "Void";
+
+ case BasicType.btChar:
+ return "Byte";
+
+ case BasicType.btWChar:
+ return "Char";
+
+ case BasicType.btInt:
+ if (length != 1L)
+ {
+ if (length == 2L)
+ {
+ return "Int16";
+ }
+ if ((length != 4L) && (length == 8L))
+ {
+ return "Int64";
+ }
+ return "Int32";
+ }
+ return "SByte";
+
+ case BasicType.btUInt:
+ if (length != 1L)
+ {
+ if (length == 2L)
+ {
+ return "UInt16";
+ }
+ if ((length != 4L) && (length == 8L))
+ {
+ return "UInt64";
+ }
+ return "UInt32";
+ }
+ return "Byte";
+
+ case BasicType.btFloat:
+ if (length != 8L)
+ {
+ return "Single";
+ }
+ return "Double";
+
+ case BasicType.btBCD:
+ return "BCD";
+
+ case BasicType.btBool:
+ return "Boolean";
+
+ case BasicType.btLong:
+ return "Int64";
+
+ case BasicType.btULong:
+ return "UInt64";
+
+ case BasicType.btCurrency:
+ return "Currency";
+
+ case BasicType.btDate:
+ return "Date";
+
+ case BasicType.btVariant:
+ return "Variant";
+
+ case BasicType.btComplex:
+ return "Complex";
+
+ case BasicType.btBit:
+ return "Bit";
+
+ case BasicType.btBSTR:
+ return "BSTR";
+
+ case BasicType.btHresult:
+ return "Hresult";
+
+ default:
+ return "?";
+ }
+ }
+
+ //
+ // Attempt to remove namespaces from types. Unfortunately, this isn't straightforward as PDB's present generic instances as "regular types with angle brackets"
+ // so "s" could be something like "System::Collections::Generics::List$1<System::String>". Worse, the PDB also uses "::" to separate nested types from
+ // their outer types so these represent collateral damage. And we assume that namespaces (unlike types) have reasonable names (i.e. no names with wierd characters.)
+ //
+ // Fortunately, this is just for diagnostic information so we don't need to let perfect be the enemy of good.
+ //
+ private static String RemoveNamespaces(this String s)
+ {
+ int firstIndexOfColonColon = s.IndexOf("::");
+ if (firstIndexOfColonColon == -1)
+ return s;
+ int lookBack = firstIndexOfColonColon - 1;
+ for (; ;)
+ {
+ if (lookBack < 0)
+ break;
+ if (!(Char.IsLetterOrDigit(s[lookBack]) || s[lookBack] == '_'))
+ break;
+ lookBack--;
+ }
+ s = s.Remove(lookBack + 1, firstIndexOfColonColon - lookBack + 1);
+ return s.RemoveNamespaces();
+ }
+
+ /// <summary>
+ /// Locate and lazily load debug info for the native app module overlapping given
+ /// virtual address.
+ /// </summary>
+ /// <param name="ip">Instruction pointer address (code address for the lookup)</param>
+ /// <param name="rva">Output VA relative to module base</param>
+ private static IDiaSession GetDiaSession(IntPtr ip, out int rva)
+ {
+ if (ip == IntPtr.Zero)
+ {
+ rva = -1;
+ return null;
+ }
+
+ IntPtr moduleBase = RuntimeAugments.GetModuleFromPointer(ip);
+ if (moduleBase == IntPtr.Zero)
+ {
+ rva = -1;
+ return null;
+ }
+
+ rva = (int)(ip.ToInt64() - moduleBase.ToInt64());
+
+ if (s_loadedModules == null)
+ {
+ // Lazily create the parallel arrays s_loadedModules and s_perModuleDebugInfo
+ int moduleCount = RuntimeAugments.GetLoadedModules(null);
+
+ s_loadedModules = new IntPtr[moduleCount];
+ s_perModuleDebugInfo = new IDiaSession[moduleCount];
+
+ // Actually read the module addresses into the array
+ RuntimeAugments.GetLoadedModules(s_loadedModules);
+ }
+
+ // Locate module index based on base address
+ int moduleIndex = s_loadedModules.Length;
+ do
+ {
+ if (--moduleIndex < 0)
+ {
+ return null;
+ }
+ }
+ while(s_loadedModules[moduleIndex] != moduleBase);
+
+ IDiaSession diaSession = s_perModuleDebugInfo[moduleIndex];
+ if (diaSession != null)
+ {
+ return diaSession;
+ }
+
+ string modulePath = RuntimeAugments.TryGetFullPathToApplicationModule(moduleBase);
+ if (modulePath == null)
+ {
+ return null;
+ }
+
+ int indexOfLastDot = modulePath.LastIndexOf('.');
+ if (indexOfLastDot == -1)
+ {
+ return null;
+ }
+
+ IDiaDataSource diaDataSource = GetDiaDataSource();
+ if (diaDataSource == null)
+ {
+ return null;
+ }
+
+ // Look for .pdb next to .exe / dll - if it's not there, bail.
+ String pdbPath = modulePath.Substring(0, indexOfLastDot) + ".pdb";
+ int hr = diaDataSource.LoadDataFromPdb(pdbPath);
+ if (hr != S_OK)
+ {
+ return null;
+ }
+
+ hr = diaDataSource.OpenSession(out diaSession);
+ if (hr != S_OK)
+ {
+ return null;
+ }
+
+ s_perModuleDebugInfo[moduleIndex] = diaSession;
+ return diaSession;
+ }
+
+ // CoCreateInstance is not in WindowsApp_Downlevel.lib and ExactSpelling = true is required
+ // to force MCG to resolve it.
+ [DllImport("api-ms-win-core-com-l1-1-0.dll", ExactSpelling =true)]
+ private static extern unsafe int CoCreateInstance(byte* rclsid, IntPtr pUnkOuter, int dwClsContext, byte* riid, out IntPtr ppv);
+
+ private const int S_OK = 0;
+ private const int CLSCTX_INPROC = 0x3;
+
+ /// <summary>
+ /// Loaded binary module addresses.
+ /// </summary>
+ [ThreadStatic]
+ private static IntPtr[] s_loadedModules;
+
+ /// <summary>
+ /// DIA session COM interfaces for the individual native application modules.
+ /// The array is constructed upon the first call to GetDiaSession but the
+ /// COM interface instances are created lazily on demand.
+ /// This array is parallel to s_loadedModules - it has the same number of elements
+ /// and the corresponding entries have the same indices.
+ /// </summary>
+ [ThreadStatic]
+ private static IDiaSession[] s_perModuleDebugInfo;
+ }
+}
+
diff --git a/src/System.Private.StackTraceGenerator/src/System.Private.StackTraceGenerator.csproj b/src/System.Private.StackTraceGenerator/src/System.Private.StackTraceGenerator.csproj
new file mode 100644
index 000000000..742117333
--- /dev/null
+++ b/src/System.Private.StackTraceGenerator/src/System.Private.StackTraceGenerator.csproj
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <AssemblyName>System.Private.StackTraceGenerator</AssemblyName>
+ <OutputType>Library</OutputType>
+ <ProjectGuid>{35A616DA-EEC6-49C4-BD06-AD27938DC94B}</ProjectGuid>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the options -->
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+ <PlatformTarget>x86</PlatformTarget>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+ <PlatformTarget>x86</PlatformTarget>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|amd64' ">
+ <PlatformTarget>x64</PlatformTarget>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|amd64' ">
+ <PlatformTarget>x64</PlatformTarget>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|arm' ">
+ <PlatformTarget>arm</PlatformTarget>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|arm' ">
+ <PlatformTarget>arm</PlatformTarget>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\System.Private.CoreLib\src\System.Private.CoreLib.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Compile Include="$(MSBuildThisFileDirectory)Internal\StackTraceGenerator\StackTraceGenerator.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Internal\StackTraceGenerator\Attributes.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Internal\LightweightInterop\ComInterface.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Internal\LightweightInterop\StdCall.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Internal\LightweightInterop\MarshalExtensions.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Internal\Dia\Guids.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Internal\Dia\StdCall.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Internal\Dia\DiaEnums.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Internal\Dia\DiaInterfaces.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="$(MSBuildThisFileDirectory)..\..\Common\src\System\NotImplemented.cs" />
+ </ItemGroup>
+
+ <PropertyGroup>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <TargetPlatformIdentifier>Portable</TargetPlatformIdentifier>
+ <TargetFrameworkIdentifier>.NETPortable</TargetFrameworkIdentifier>
+ <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
+ <TargetFrameworkMonikerDisplayName>.NET Portable Subset</TargetFrameworkMonikerDisplayName>
+ <ImplicitlyExpandTargetFramework>false</ImplicitlyExpandTargetFramework>
+ </PropertyGroup>
+
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>