<!-- Override clean from dir.traversal.targets and just remove the full BinDir -->
<Target Name="Clean">
+ <Delete Files="$(BinDir)SOS.NETCore.*" />
<Delete Files="$(BinDir)mscorlib.*" />
<Delete Files="$(BinDir)System.Private.CoreLib.*" />
</Target>
<Exec Command="$(DnuRestoreCommand) "$(SourceDir).nuget/init/project.json" --source https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
</Target>
-</Project>
\ No newline at end of file
+</Project>
<!-- Output paths -->
<PropertyGroup>
- <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)' == ''">$(RootBinDir)obj/</BaseIntermediateOutputPath>
+ <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)' == ''">$(RootBinDir)obj\</BaseIntermediateOutputPath>
<IntermediateOutputPath Condition="'$(IntermediateOutputPath)' == ''">$(BaseIntermediateOutputPath)\$(BuildOS).$(BuildArch).$(BuildType)</IntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)' == ''">$(BaseIntermediateOutputPath)\$(BuildOS).$(BuildArch).$(BuildType)</OutputPath>
<FinalOutputPath Condition="'$(FinalOutputPath)' == ''">$(BinDir)</FinalOutputPath>
<ArchitectureSpecificNativeFile Include="$(BinDir)System.Private.CoreLib.ni.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)mscorlib.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)System.Private.CoreLib.dll" />
+ <ArchitectureSpecificLibFile Include="$(BinDir)SOS.NETCore.dll" />
<ArchitectureSpecificToolFile Include="$(BinDir)crossgen" />
<ArchitectureSpecificNativeFile Include="@(NativeSplittableBinary)" />
<File Include="@(ArchitectureSpecificNativeFile)">
</File>
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project>
\ No newline at end of file
+</Project>
<ArchitectureSpecificNativeFile Include="$(BinDir)System.Private.CoreLib.ni.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)System.Private.CoreLib.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)mscorlib.dll" />
+ <ArchitectureSpecificLibFile Include="$(BinDir)SOS.NETCore.dll" />
<ArchitectureSpecificToolFile Include="$(BinDir)crossgen" />
<ArchitectureSpecificNativeFile Include="@(NativeSplittableBinary)" />
<File Include="@(ArchitectureSpecificNativeFile)">
</File>
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project>
\ No newline at end of file
+</Project>
<ArchitectureSpecificNativeFile Include="$(BinDir)System.Private.CoreLib.ni.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)System.Private.CoreLib.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)mscorlib.dll" />
+ <ArchitectureSpecificLibFile Include="$(BinDir)SOS.NETCore.dll" />
<ArchitectureSpecificToolFile Include="$(BinDir)crossgen" />
<ArchitectureSpecificNativeFile Include="@(NativeSplittableBinary)" />
<File Include="@(ArchitectureSpecificNativeFile)">
</File>
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project>
\ No newline at end of file
+</Project>
<ArchitectureSpecificNativeFile Include="$(BinDir)System.Private.CoreLib.ni.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)mscorlib.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)System.Private.CoreLib.dll" />
+ <ArchitectureSpecificLibFile Include="$(BinDir)SOS.NETCore.dll" />
<ArchitectureSpecificToolFile Include="$(BinDir)crossgen" />
<ArchitectureSpecificNativeFile Include="@(NativeSplittableBinary)" />
<File Include="@(ArchitectureSpecificNativeFile)">
</File>
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project>
\ No newline at end of file
+</Project>
<ArchitectureSpecificNativeFile Include="$(BinDir)System.Private.CoreLib.ni.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)System.Private.CoreLib.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)mscorlib.dll" />
+ <ArchitectureSpecificLibFile Include="$(BinDir)SOS.NETCore.dll" />
<ArchitectureSpecificToolFile Include="$(BinDir)crossgen" />
<ArchitectureSpecificNativeFile Include="@(NativeSplittableBinary)" />
<File Include="@(ArchitectureSpecificNativeFile)">
</File>
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project>
\ No newline at end of file
+</Project>
<ArchitectureSpecificNativeFile Include="$(BinDir)System.Private.CoreLib.ni.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)System.Private.CoreLib.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)mscorlib.dll" />
+ <ArchitectureSpecificLibFile Include="$(BinDir)SOS.NETCore.dll" />
<ArchitectureSpecificToolFile Include="$(BinDir)crossgen" />
<ArchitectureSpecificNativeFile Include="@(NativeSplittableBinary)" />
<File Include="@(ArchitectureSpecificNativeFile)">
</File>
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project>
\ No newline at end of file
+</Project>
<ArchitectureSpecificNativeFile Include="$(BinDir)System.Private.CoreLib.ni.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)System.Private.CoreLib.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)mscorlib.dll" />
+ <ArchitectureSpecificLibFile Include="$(BinDir)SOS.NETCore.dll" />
<ArchitectureSpecificToolFile Include="$(BinDir)crossgen" />
<ArchitectureSpecificNativeFile Include="@(NativeSplittableBinary)" />
<File Include="@(ArchitectureSpecificNativeFile)">
</File>
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project>
\ No newline at end of file
+</Project>
<ArchitectureSpecificNativeFile Include="$(BinDir)System.Private.CoreLib.ni.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)System.Private.CoreLib.dll" />
<ArchitectureSpecificLibFile Include="$(BinDir)mscorlib.dll" />
+ <ArchitectureSpecificLibFile Include="$(BinDir)SOS.NETCore.dll" />
<ArchitectureSpecificToolFile Include="$(BinDir)crossgen.exe" />
<CrossArchitectureSpecificToolFile Include="$(BinDir)$(CrossTargetComponentFolder)\crossgen.exe" />
<CrossArchitectureSpecificToolFile Include="$(BinDir)$(CrossTargetComponentFolder)\mscordaccore.dll" />
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+
+ <PropertyGroup>
+ <AssemblyName>SOS.NETCore</AssemblyName>
+ <AssemblyVersion>1.0.0.0</AssemblyVersion>
+ <ProjectGuid>{20513BA2-A156-4A17-4C70-5AC2DBD4F833}</ProjectGuid>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <OutputType>Library</OutputType>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <NoStdLib>true</NoStdLib>
+ <NoCompilerStandardLib>true</NoCompilerStandardLib>
+ </PropertyGroup>
+
+ <!-- Default configurations to help VS understand the options -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" />
+
+ <!-- Configuration specific properties -->
+ <PropertyGroup Condition="'$(Configuration)' == 'Debug' or '$(Configuration)' == 'Checked'">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <DefineConstants>_DEBUG;DEBUG;TRACE;$(DefineConstants)</DefineConstants>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(OsEnvironment)' == 'Unix'">
+ <DebugSymbols>false</DebugSymbols>
+ <DebugType>none</DebugType>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Include="SymbolReader.cs" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <None Include="project.json" />
+ </ItemGroup>
+
+ <Target Name="CopyItemsToDirectory" AfterTargets="Build">
+ <Copy
+ SourceFiles="$(OutputPath)$(AssemblyName).dll"
+ DestinationFolder="$(BinDir)"
+ SkipUnchangedFiles="false"
+ OverwriteReadOnlyFiles="false"
+ UseHardlinksIfPossible="false">
+ </Copy>
+
+ <Copy
+ Condition="'$(OsEnvironment)' != 'Unix'"
+ SourceFiles="$(OutputPath)$(AssemblyName).pdb"
+ DestinationFolder="$(BinDir)\PDB"
+ SkipUnchangedFiles="false"
+ OverwriteReadOnlyFiles="false"
+ UseHardlinksIfPossible="false">
+ </Copy>
+ </Target>
+
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
--- /dev/null
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SOS.NETCore", "SOS.NETCore.csproj", "{20513BA2-A156-4A17-4C70-5AC2DBD4F833}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {20513BA2-A156-4A17-4C70-5AC2DBD4F833}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+using System.Reflection.PortableExecutable;
+using System.Runtime.InteropServices;
+
+namespace SOS
+{
+ internal class SymbolReader
+ {
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ internal struct DebugInfo
+ {
+ public int lineNumber;
+ public int ilOffset;
+ public string fileName;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct MethodDebugInfo
+ {
+ public IntPtr points;
+ public int size;
+ }
+
+ /// <summary>
+ /// Read memory callback
+ /// </summary>
+ /// <returns>number of bytes read or 0 for error</returns>
+ internal unsafe delegate int ReadMemoryDelegate(IntPtr address, byte* buffer, int count);
+
+ private sealed class OpenedReader : IDisposable
+ {
+ public readonly MetadataReaderProvider Provider;
+ public readonly MetadataReader Reader;
+
+ public OpenedReader(MetadataReaderProvider provider, MetadataReader reader)
+ {
+ Debug.Assert(provider != null);
+ Debug.Assert(reader != null);
+
+ Provider = provider;
+ Reader = reader;
+ }
+
+ public void Dispose() => Provider.Dispose();
+ }
+
+ /// <summary>
+ /// Stream implementation to read debugger target memory for in-memory PDBs
+ /// </summary>
+ private class TargetStream : Stream
+ {
+ readonly IntPtr _address;
+ readonly ReadMemoryDelegate _readMemory;
+
+ public override long Position { get; set; }
+ public override long Length { get; }
+ public override bool CanSeek { get { return true; } }
+ public override bool CanRead { get { return true; } }
+ public override bool CanWrite { get { return false; } }
+
+ public TargetStream(IntPtr address, int size, ReadMemoryDelegate readMemory)
+ : base()
+ {
+ _address = address;
+ _readMemory = readMemory;
+ Length = size;
+ Position = 0;
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (Position + count > Length)
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+ unsafe
+ {
+ fixed (byte* p = &buffer[offset])
+ {
+ int read = _readMemory(new IntPtr(_address.ToInt64() + Position), p, count);
+ Position += read;
+ return read;
+ }
+ }
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ switch (origin)
+ {
+ case SeekOrigin.Begin:
+ Position = offset;
+ break;
+ case SeekOrigin.End:
+ Position = Length + offset;
+ break;
+ case SeekOrigin.Current:
+ Position += offset;
+ break;
+ }
+ return Position;
+ }
+
+ public override void Flush()
+ {
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// Checks availability of debugging information for given assembly.
+ /// </summary>
+ /// <param name="assemblyPath">
+ /// File path of the assembly or null if the module is in-memory or dynamic (generated by Reflection.Emit)
+ /// </param>
+ /// <param name="isFileLayout">type of in-memory PE layout, if true, file based layout otherwise, loaded layout</param>
+ /// <param name="loadedPeAddress">
+ /// Loaded PE image address or zero if the module is dynamic (generated by Reflection.Emit).
+ /// Dynamic modules have their PDBs (if any) generated to an in-memory stream
+ /// (pointed to by <paramref name="inMemoryPdbAddress"/> and <paramref name="inMemoryPdbSize"/>).
+ /// </param>
+ /// <param name="loadedPeSize">loaded PE image size</param>
+ /// <param name="inMemoryPdbAddress">in memory PDB address or zero</param>
+ /// <param name="inMemoryPdbSize">in memory PDB size</param>
+ /// <returns>Symbol reader handle or zero if error</returns>
+ internal static IntPtr LoadSymbolsForModule(string assemblyPath, bool isFileLayout, IntPtr loadedPeAddress, int loadedPeSize,
+ IntPtr inMemoryPdbAddress, int inMemoryPdbSize, ReadMemoryDelegate readMemory)
+ {
+ TargetStream peStream = null;
+ if (assemblyPath == null && loadedPeAddress != IntPtr.Zero)
+ {
+ peStream = new TargetStream(loadedPeAddress, loadedPeSize, readMemory);
+ }
+ TargetStream pdbStream = null;
+ if (inMemoryPdbAddress != IntPtr.Zero)
+ {
+ pdbStream = new TargetStream(inMemoryPdbAddress, inMemoryPdbSize, readMemory);
+ }
+ OpenedReader openedReader = GetReader(assemblyPath, isFileLayout, peStream, pdbStream);
+ if (openedReader == null)
+ return IntPtr.Zero;
+
+ GCHandle gch = GCHandle.Alloc(openedReader);
+ return GCHandle.ToIntPtr(gch);
+ }
+
+ /// <summary>
+ /// Cleanup and dispose of symbol reader handle
+ /// </summary>
+ /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param>
+ internal static void Dispose(IntPtr symbolReaderHandle)
+ {
+ Debug.Assert(symbolReaderHandle != IntPtr.Zero);
+ try
+ {
+ GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
+ ((OpenedReader)gch.Target).Dispose();
+ gch.Free();
+ }
+ catch
+ {
+ }
+ }
+
+ /// <summary>
+ /// Returns method token and IL offset for given source line number.
+ /// </summary>
+ /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param>
+ /// <param name="filePath">source file name and path</param>
+ /// <param name="lineNumber">source line number</param>
+ /// <param name="methodToken">method token return</param>
+ /// <param name="ilOffset">IL offset return</param>
+ /// <returns> true if information is available</returns>
+ internal static bool ResolveSequencePoint(IntPtr symbolReaderHandle, string filePath, int lineNumber, out int methodToken, out int ilOffset)
+ {
+ Debug.Assert(symbolReaderHandle != IntPtr.Zero);
+ methodToken = 0;
+ ilOffset = 0;
+
+ GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
+ MetadataReader reader = ((OpenedReader)gch.Target).Reader;
+
+ try
+ {
+ string fileName = Path.GetFileName(filePath);
+ foreach (MethodDebugInformationHandle methodDebugInformationHandle in reader.MethodDebugInformation)
+ {
+ MethodDebugInformation methodDebugInfo = reader.GetMethodDebugInformation(methodDebugInformationHandle);
+ SequencePointCollection sequencePoints = methodDebugInfo.GetSequencePoints();
+ foreach (SequencePoint point in sequencePoints)
+ {
+ string sourceName = reader.GetString(reader.GetDocument(point.Document).Name);
+ if (point.StartLine == lineNumber && Path.GetFileName(sourceName) == fileName)
+ {
+ methodToken = MetadataTokens.GetToken(methodDebugInformationHandle.ToDefinitionHandle());
+ ilOffset = point.Offset;
+ return true;
+ }
+ }
+ }
+ }
+ catch
+ {
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns source line number and source file name for given IL offset and method token.
+ /// </summary>
+ /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param>
+ /// <param name="methodToken">method token</param>
+ /// <param name="ilOffset">IL offset</param>
+ /// <param name="lineNumber">source line number return</param>
+ /// <param name="fileName">source file name return</param>
+ /// <returns> true if information is available</returns>
+ internal static bool GetLineByILOffset(IntPtr symbolReaderHandle, int methodToken, long ilOffset, out int lineNumber, out IntPtr fileName)
+ {
+ lineNumber = 0;
+ fileName = IntPtr.Zero;
+
+ string sourceFileName = null;
+
+ if (!GetSourceLineByILOffset(symbolReaderHandle, methodToken, ilOffset, out lineNumber, out sourceFileName))
+ {
+ return false;
+ }
+ fileName = Marshal.StringToBSTR(sourceFileName);
+ sourceFileName = null;
+ return true;
+ }
+
+ /// <summary>
+ /// Helper method to return source line number and source file name for given IL offset and method token.
+ /// </summary>
+ /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param>
+ /// <param name="methodToken">method token</param>
+ /// <param name="ilOffset">IL offset</param>
+ /// <param name="lineNumber">source line number return</param>
+ /// <param name="fileName">source file name return</param>
+ /// <returns> true if information is available</returns>
+ private static bool GetSourceLineByILOffset(IntPtr symbolReaderHandle, int methodToken, long ilOffset, out int lineNumber, out string fileName)
+ {
+ Debug.Assert(symbolReaderHandle != IntPtr.Zero);
+ lineNumber = 0;
+ fileName = null;
+
+ GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
+ MetadataReader reader = ((OpenedReader)gch.Target).Reader;
+
+ try
+ {
+ Handle handle = MetadataTokens.Handle(methodToken);
+ if (handle.Kind != HandleKind.MethodDefinition)
+ return false;
+
+ MethodDebugInformationHandle methodDebugHandle = ((MethodDefinitionHandle)handle).ToDebugInformationHandle();
+ MethodDebugInformation methodDebugInfo = reader.GetMethodDebugInformation(methodDebugHandle);
+ SequencePointCollection sequencePoints = methodDebugInfo.GetSequencePoints();
+
+ SequencePoint nearestPoint = sequencePoints.GetEnumerator().Current;
+ foreach (SequencePoint point in sequencePoints)
+ {
+ if (point.Offset < ilOffset)
+ {
+ nearestPoint = point;
+ }
+ else
+ {
+ if (point.Offset == ilOffset)
+ nearestPoint = point;
+
+ if (nearestPoint.StartLine == 0 || nearestPoint.StartLine == SequencePoint.HiddenLine)
+ return false;
+
+ lineNumber = nearestPoint.StartLine;
+ fileName = reader.GetString(reader.GetDocument(nearestPoint.Document).Name);
+ return true;
+ }
+ }
+ }
+ catch
+ {
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns local variable name for given local index and IL offset.
+ /// </summary>
+ /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param>
+ /// <param name="methodToken">method token</param>
+ /// <param name="localIndex">local variable index</param>
+ /// <param name="localVarName">local variable name return</param>
+ /// <returns>true if name has been found</returns>
+ internal static bool GetLocalVariableName(IntPtr symbolReaderHandle, int methodToken, int localIndex, out IntPtr localVarName)
+ {
+ localVarName = IntPtr.Zero;
+
+ string localVar = null;
+ if (!GetLocalVariableByIndex(symbolReaderHandle, methodToken, localIndex, out localVar))
+ return false;
+
+ localVarName = Marshal.StringToBSTR(localVar);
+ localVar = null;
+ return true;
+ }
+
+ /// <summary>
+ /// Helper method to return local variable name for given local index and IL offset.
+ /// </summary>
+ /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param>
+ /// <param name="methodToken">method token</param>
+ /// <param name="localIndex">local variable index</param>
+ /// <param name="localVarName">local variable name return</param>
+ /// <returns>true if name has been found</returns>
+ internal static bool GetLocalVariableByIndex(IntPtr symbolReaderHandle, int methodToken, int localIndex, out string localVarName)
+ {
+ Debug.Assert(symbolReaderHandle != IntPtr.Zero);
+ localVarName = null;
+
+ GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
+ MetadataReader reader = ((OpenedReader)gch.Target).Reader;
+
+ try
+ {
+ Handle handle = MetadataTokens.Handle(methodToken);
+ if (handle.Kind != HandleKind.MethodDefinition)
+ return false;
+
+ MethodDebugInformationHandle methodDebugHandle = ((MethodDefinitionHandle)handle).ToDebugInformationHandle();
+ LocalScopeHandleCollection localScopes = reader.GetLocalScopes(methodDebugHandle);
+ foreach (LocalScopeHandle scopeHandle in localScopes)
+ {
+ LocalScope scope = reader.GetLocalScope(scopeHandle);
+ LocalVariableHandleCollection localVars = scope.GetLocalVariables();
+ foreach (LocalVariableHandle varHandle in localVars)
+ {
+ LocalVariable localVar = reader.GetLocalVariable(varHandle);
+ if (localVar.Index == localIndex)
+ {
+ if (localVar.Attributes == LocalVariableAttributes.DebuggerHidden)
+ return false;
+
+ localVarName = reader.GetString(localVar.Name);
+ return true;
+ }
+ }
+ }
+ }
+ catch
+ {
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns source name, line numbers and IL offsets for given method token.
+ /// </summary>
+ /// <param name="assemblyPath">file path of the assembly</param>
+ /// <param name="methodToken">method token</param>
+ /// <param name="debugInfo">structure with debug information return</param>
+ /// <returns>true if information is available</returns>
+ /// <remarks>used by the gdb JIT support (not SOS). Does not support in-memory PEs or PDBs</remarks>
+ internal static bool GetInfoForMethod(string assemblyPath, int methodToken, ref MethodDebugInfo debugInfo)
+ {
+ List<DebugInfo> points = null;
+
+ if (!GetDebugInfoForMethod(assemblyPath, methodToken, out points))
+ {
+ return false;
+ }
+ var structSize = Marshal.SizeOf<DebugInfo>();
+
+ debugInfo.size = points.Count;
+ var ptr = debugInfo.points;
+
+ foreach (var info in points)
+ {
+ Marshal.StructureToPtr(info, ptr, false);
+ ptr = (IntPtr)(ptr.ToInt64() + structSize);
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Helper method to return source name, line numbers and IL offsets for given method token.
+ /// </summary>
+ /// <param name="assemblyPath">file path of the assembly</param>
+ /// <param name="methodToken">method token</param>
+ /// <param name="points">list of debug information for each sequence point return</param>
+ /// <returns>true if information is available</returns>
+ /// <remarks>used by the gdb JIT support (not SOS). Does not support in-memory PEs or PDBs</remarks>
+ private static bool GetDebugInfoForMethod(string assemblyPath, int methodToken, out List<DebugInfo> points)
+ {
+ points = null;
+
+ OpenedReader openedReader = GetReader(assemblyPath, isFileLayout: true, peStream: null, pdbStream: null);
+ if (openedReader == null)
+ return false;
+
+ using (openedReader)
+ {
+ try
+ {
+ Handle handle = MetadataTokens.Handle(methodToken);
+ if (handle.Kind != HandleKind.MethodDefinition)
+ return false;
+
+ points = new List<DebugInfo>();
+ MethodDebugInformationHandle methodDebugHandle = ((MethodDefinitionHandle)handle).ToDebugInformationHandle();
+ MethodDebugInformation methodDebugInfo = openedReader.Reader.GetMethodDebugInformation(methodDebugHandle);
+ SequencePointCollection sequencePoints = methodDebugInfo.GetSequencePoints();
+
+ foreach (SequencePoint point in sequencePoints)
+ {
+ if (point.StartLine == 0 || point.StartLine == SequencePoint.HiddenLine)
+ continue;
+
+ DebugInfo debugInfo = new DebugInfo();
+ debugInfo.lineNumber = point.StartLine;
+ debugInfo.fileName = openedReader.Reader.GetString(openedReader.Reader.GetDocument(point.Document).Name);
+ debugInfo.ilOffset = point.Offset;
+ points.Add(debugInfo);
+ }
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Returns the portable PDB reader for the assembly path
+ /// </summary>
+ /// <param name="assemblyPath">file path of the assembly or null if the module is in-memory or dynamic</param>
+ /// <param name="isFileLayout">type of in-memory PE layout, if true, file based layout otherwise, loaded layout</param>
+ /// <param name="peStream">optional in-memory PE stream</param>
+ /// <param name="pdbStream">optional in-memory PDB stream</param>
+ /// <returns>reader/provider wrapper instance</returns>
+ /// <remarks>
+ /// Assumes that neither PE image nor PDB loaded into memory can be unloaded or moved around.
+ /// </remarks>
+ private static OpenedReader GetReader(string assemblyPath, bool isFileLayout, Stream peStream, Stream pdbStream)
+ {
+ return (pdbStream != null) ? TryOpenReaderForInMemoryPdb(pdbStream) : TryOpenReaderFromAssembly(assemblyPath, isFileLayout, peStream);
+ }
+
+ private static OpenedReader TryOpenReaderForInMemoryPdb(Stream pdbStream)
+ {
+ Debug.Assert(pdbStream != null);
+
+ byte[] buffer = new byte[sizeof(uint)];
+ if (pdbStream.Read(buffer, 0, sizeof(uint)) != sizeof(uint))
+ {
+ return null;
+ }
+ uint signature = BitConverter.ToUInt32(buffer, 0);
+
+ // quick check to avoid throwing exceptions below in common cases:
+ const uint ManagedMetadataSignature = 0x424A5342;
+ if (signature != ManagedMetadataSignature)
+ {
+ // not a Portable PDB
+ return null;
+ }
+
+ OpenedReader result = null;
+ MetadataReaderProvider provider = null;
+ try
+ {
+ pdbStream.Position = 0;
+ provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
+ result = new OpenedReader(provider, provider.GetMetadataReader());
+ }
+ catch (BadImageFormatException)
+ {
+ return null;
+ }
+ finally
+ {
+ if (result == null)
+ {
+ provider?.Dispose();
+ }
+ }
+
+ return result;
+ }
+
+ private static OpenedReader TryOpenReaderFromAssembly(string assemblyPath, bool isFileLayout, Stream peStream)
+ {
+ if (assemblyPath == null && peStream == null)
+ return null;
+
+ PEStreamOptions options = isFileLayout ? PEStreamOptions.Default : PEStreamOptions.IsLoadedImage;
+ if (peStream == null)
+ {
+ peStream = TryOpenFile(assemblyPath);
+ if (peStream == null)
+ return null;
+
+ options = PEStreamOptions.Default;
+ }
+
+ try
+ {
+ using (var peReader = new PEReader(peStream, options))
+ {
+ DebugDirectoryEntry codeViewEntry, embeddedPdbEntry;
+ ReadPortableDebugTableEntries(peReader, out codeViewEntry, out embeddedPdbEntry);
+
+ // First try .pdb file specified in CodeView data (we prefer .pdb file on disk over embedded PDB
+ // since embedded PDB needs decompression which is less efficient than memory-mapping the file).
+ if (codeViewEntry.DataSize != 0)
+ {
+ var result = TryOpenReaderFromCodeView(peReader, codeViewEntry, assemblyPath);
+ if (result != null)
+ {
+ return result;
+ }
+ }
+
+ // if it failed try Embedded Portable PDB (if available):
+ if (embeddedPdbEntry.DataSize != 0)
+ {
+ return TryOpenReaderFromEmbeddedPdb(peReader, embeddedPdbEntry);
+ }
+ }
+ }
+ catch (Exception e) when (e is BadImageFormatException || e is IOException)
+ {
+ // nop
+ }
+
+ return null;
+ }
+
+ private static void ReadPortableDebugTableEntries(PEReader peReader, out DebugDirectoryEntry codeViewEntry, out DebugDirectoryEntry embeddedPdbEntry)
+ {
+ // See spec: https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PE-COFF.md
+
+ codeViewEntry = default(DebugDirectoryEntry);
+ embeddedPdbEntry = default(DebugDirectoryEntry);
+
+ foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory())
+ {
+ if (entry.Type == DebugDirectoryEntryType.CodeView)
+ {
+ const ushort PortableCodeViewVersionMagic = 0x504d;
+ if (entry.MinorVersion != PortableCodeViewVersionMagic)
+ {
+ continue;
+ }
+
+ codeViewEntry = entry;
+ }
+ else if (entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb)
+ {
+ embeddedPdbEntry = entry;
+ }
+ }
+ }
+
+ private static OpenedReader TryOpenReaderFromCodeView(PEReader peReader, DebugDirectoryEntry codeViewEntry, string assemblyPath)
+ {
+ OpenedReader result = null;
+ MetadataReaderProvider provider = null;
+ try
+ {
+ var data = peReader.ReadCodeViewDebugDirectoryData(codeViewEntry);
+
+ string pdbPath = data.Path;
+ if (assemblyPath != null)
+ {
+ try
+ {
+ pdbPath = Path.Combine(Path.GetDirectoryName(assemblyPath), Path.GetFileName(pdbPath));
+ }
+ catch
+ {
+ // invalid characters in CodeView path
+ return null;
+ }
+ }
+
+ var pdbStream = TryOpenFile(pdbPath);
+ if (pdbStream == null)
+ {
+ return null;
+ }
+
+ provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream);
+ var reader = provider.GetMetadataReader();
+
+ // Validate that the PDB matches the assembly version
+ if (data.Age == 1 && new BlobContentId(reader.DebugMetadataHeader.Id) == new BlobContentId(data.Guid, codeViewEntry.Stamp))
+ {
+ result = new OpenedReader(provider, reader);
+ }
+ }
+ catch (Exception e) when (e is BadImageFormatException || e is IOException)
+ {
+ return null;
+ }
+ finally
+ {
+ if (result == null)
+ {
+ provider?.Dispose();
+ }
+ }
+
+ return result;
+ }
+
+ private static OpenedReader TryOpenReaderFromEmbeddedPdb(PEReader peReader, DebugDirectoryEntry embeddedPdbEntry)
+ {
+ OpenedReader result = null;
+ MetadataReaderProvider provider = null;
+
+ try
+ {
+ // TODO: We might want to cache this provider globally (across stack traces),
+ // since decompressing embedded PDB takes some time.
+ provider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedPdbEntry);
+ result = new OpenedReader(provider, provider.GetMetadataReader());
+ }
+ catch (Exception e) when (e is BadImageFormatException || e is IOException)
+ {
+ return null;
+ }
+ finally
+ {
+ if (result == null)
+ {
+ provider?.Dispose();
+ }
+ }
+
+ return result;
+ }
+
+ private static Stream TryOpenFile(string path)
+ {
+ if (!File.Exists(path))
+ {
+ return null;
+ }
+ try
+ {
+ return File.OpenRead(path);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+ }
+}
--- /dev/null
+{
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.0.1",
+ "System.IO.FileSystem": "4.0.1",
+ "System.Runtime.InteropServices": "4.1.0",
+ "System.Reflection.Metadata": "1.4.1-beta-24417-02"
+ },
+ "frameworks": {
+ "netcoreapp1.0": {}
+ },
+ "runtimes": {
+ "win7-x86": {},
+ "win7-x64": {},
+ "ubuntu.14.04-x64": {},
+ "osx.10.10-x64": {},
+ "centos.7-x64": {},
+ "rhel.7-x64": {},
+ "debian.8-x64": {}
+ }
+}
kernel32.lib
user32.lib
ole32.lib
+ oleaut32.lib
dbghelp.lib
uuid.lib
version.lib
bool fLastWasMovW = false;
INT_PTR lowbits = 0;
ULONG curLine = -1;
- char filename[MAX_PATH_FNAME+1];
+ WCHAR filename[MAX_LONGPATH];
ULONG linenum;
while (PC < PCEnd)
// Print out line numbers if needed
if (!bSuppressLines
- && SUCCEEDED(GetLineByOffset(TO_CDADDR(PC),
- &linenum, filename, MAX_PATH_FNAME+1)))
+ && SUCCEEDED(GetLineByOffset(TO_CDADDR(PC), &linenum, filename, MAX_LONGPATH)))
{
if (linenum != curLine)
{
curLine = linenum;
- ExtOut("\n%s @ %d:\n", filename, linenum);
+ ExtOut("\n%S @ %d:\n", filename, linenum);
}
}
char line[1024];
ULONG lineNum;
ULONG curLine = -1;
- char fileName[MAX_PATH_FNAME+1];
+ WCHAR fileName[MAX_LONGPATH];
char *ptr;
INT_PTR accumulatedConstant = 0;
BOOL loBitsSet = FALSE;
// This is the new instruction
-
if (IsInterrupt())
return;
//
// Print out line numbers if needed
//
- if (!bSuppressLines && SUCCEEDED(GetLineByOffset(TO_CDADDR(currentPC), &lineNum, fileName, MAX_PATH_FNAME+1)))
+ if (!bSuppressLines &&
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(currentPC), &lineNum, fileName, MAX_LONGPATH)))
{
if (lineNum != curLine)
{
curLine = lineNum;
- ExtOut("\n%s @ %d:\n", fileName, lineNum);
+ ExtOut("\n%S @ %d:\n", fileName, lineNum);
}
}
char *ptr;
ULONG curLine = -1;
- char filename[MAX_PATH_FNAME+1];
+ WCHAR filename[MAX_LONGPATH];
ULONG linenum;
while (IP < IPEnd)
// Print out line numbers if needed
if (!bSuppressLines
- && SUCCEEDED(GetLineByOffset(TO_CDADDR(IP),
- &linenum, filename, MAX_PATH_FNAME+1)))
+ && SUCCEEDED(GetLineByOffset(TO_CDADDR(IP), &linenum, filename, MAX_LONGPATH)))
{
if (linenum != curLine)
{
curLine = linenum;
- ExtOut("\n%s @ %d:\n", filename, linenum);
+ ExtOut("\n%S @ %d:\n", filename, linenum);
}
}
#pragma warning(disable:4189) // local variable is initialized but not referenced
#endif
-
+#ifdef FEATURE_PAL
+#define SOSPrefix ""
+#else
+#define SOSPrefix "!"
+#endif
#if defined _X86_ && !defined FEATURE_PAL
// disable FPO for X86 builds
#pragma optimize("y", off)
#endif
-
-
#undef assert
#ifdef _MSC_VER
DMLOut("MethodDesc: %s\n", DMLMethodDesc(pMD));
DumpMDInfo(TO_TADDR(pMD), cdaStart, FALSE /* fStackTraceFormat */);
- char filename[MAX_PATH_FNAME+1];
+ WCHAR filename[MAX_LONGPATH];
ULONG linenum;
// symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
ULONG symlines = 0;
symlines &= SYMOPT_LOAD_LINES;
}
- if (symlines != 0
- && SUCCEEDED(GetLineByOffset(TO_CDADDR(IP),
- &linenum,
- filename,
- MAX_PATH_FNAME+1)))
+ if (symlines != 0 &&
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(IP), &linenum, filename, _countof(filename))))
{
- ExtOut("Source file: %s @ %d\n", filename, linenum);
+ ExtOut("Source file: %S @ %d\n", filename, linenum);
}
return Status;
// or did not update so (when ste is an explicit frames), do not update wszBuffer
if (Status == S_OK)
{
- char filename[MAX_LONGPATH+1] = "";
+ WCHAR filename[MAX_LONGPATH] = W("");
ULONG linenum = 0;
- if (bLineNumbers && SUCCEEDED(GetLineByOffset(TO_CDADDR(ste.ip), &linenum, filename, _countof(filename))))
+ if (bLineNumbers &&
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(ste.ip), &linenum, filename, _countof(filename))))
{
- swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s [%S @ %d]\n"), so.String(), filename, linenum);
+ swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s [%s @ %d]\n"), so.String(), filename, linenum);
}
else
{
{
PendingBreakpoint *pCur = m_breakpoints;
size_t iBreakpointIndex = 1;
- ExtOut("!bpmd pending breakpoint list\n Breakpoint index - Location, ModuleID, Method Token\n");
+ ExtOut(SOSPrefix "bpmd pending breakpoint list\n Breakpoint index - Location, ModuleID, Method Token\n");
while(pCur)
{
//windbg likes to format %p as always being 64 bits
HRESULT LoadSymbolsForModule(TADDR mod, SymbolReader* pSymbolReader)
{
HRESULT Status = S_OK;
- ToRelease<IXCLRDataModule> module;
- IfFailRet(g_sos->GetModule(mod, &module));
+ ToRelease<IXCLRDataModule> pModule;
+ IfFailRet(g_sos->GetModule(mod, &pModule));
ToRelease<IMetaDataImport> pMDImport = NULL;
- IfFailRet(module->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport));
-
- WCHAR wszNameBuffer[MAX_LONGPATH];
- ULONG32 nameLen = 0;
- if(FAILED(Status = module->GetFileName(MAX_LONGPATH, &nameLen, wszNameBuffer)))
- {
- ExtOut("SOS error: IXCLRDataModule->GetFileName failed hr=0x%x\n", wszNameBuffer);
- return Status;
- }
-
- //get a pointer to just the filename (the portion after the last backslash)
- WCHAR* pModuleFilename = wszNameBuffer;
- WCHAR* pSlash = _wcschr(pModuleFilename, DIRECTORY_SEPARATOR_CHAR_W);
- while(pSlash != NULL)
- {
- pModuleFilename = pSlash+1;
- pSlash = _wcschr(pModuleFilename, DIRECTORY_SEPARATOR_CHAR_W);
- }
-#ifndef FEATURE_PAL
+ IfFailRet(pModule->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport));
- ImageInfo ii;
- if(FAILED(Status = GetClrModuleImages(module, CLRDATA_MODULE_PE_FILE, &ii)))
- {
- ExtOut("SOS error: GetClrModuleImages failed hr=0x%x\n", Status);
- return Status;
- }
+ IfFailRet(pSymbolReader->LoadSymbols(pMDImport, pModule));
- if(FAILED(Status = pSymbolReader->LoadSymbols(pMDImport, ii.modBase, pModuleFilename, FALSE)) &&
- FAILED(pSymbolReader->LoadSymbols(pMDImport, ii.modBase, pModuleFilename, TRUE)))
- {
- ExtOut("SOS warning: No symbols for module %S, source line breakpoints in this module will not bind hr=0x%x\n", wszNameBuffer, Status);
- return S_FALSE; // not finding symbols is a typical case
- }
-#else
- if(FAILED(Status = pSymbolReader->LoadSymbols(pMDImport, 0, pModuleFilename, FALSE)))
- {
- return S_FALSE;
- }
-#endif // FEATURE_PAL
return S_OK;
}
HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pModuleName, __in_z WCHAR* pMethodName, TADDR mod, DWORD ilOffset)
{
- int numModule;
+ HRESULT Status = S_OK;
char szName[mdNameLen];
+ int numModule;
ToRelease<IXCLRDataModule> module;
- HRESULT Status = S_OK;
IfFailRet(g_sos->GetModule(mod, &module));
- WideCharToMultiByte(CP_ACP, 0, pModuleName, (int) (_wcslen(pModuleName) + 1),
- szName, mdNameLen, NULL, NULL);
+ WideCharToMultiByte(CP_ACP, 0, pModuleName, (int)(_wcslen(pModuleName) + 1), szName, mdNameLen, NULL, NULL);
ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(szName, &numModule);
if (moduleList == NULL)
return E_FAIL;
}
- for(int i=0;i<numModule;i++)
+ for (int i = 0; i < numModule; i++)
{
// If any one entry in moduleList matches, then the current PendingBreakpoint
// is the right one.
if (IsDumpFile())
{
- ExtOut("!bpmd is not supported on a dump file.\n");
+ ExtOut(SOSPrefix "bpmd is not supported on a dump file.\n");
return Status;
}
fBadParam = true;
}
if(nArg != 1) fBadParam = 1;
-#ifdef FEATURE_PAL
- if (!SymbolReader::SymbolReaderDllExists())
- {
- ExtOut("Can't find dll for symbol reader.");
- ExtOut("File name:Line number not supported\n");
- fBadParam = true;
- }
-#endif // FEATURE_PAL
}
}
if (fBadParam || (commandsParsed != 1))
{
- ExtOut("Usage: !bpmd -md <MethodDesc pointer>\n");
- ExtOut("Usage: !bpmd [-nofuturemodule] <module name> <managed function name> [<il offset>]\n");
- ExtOut("Usage: !bpmd <filename>:<line number>\n");
- ExtOut("Usage: !bpmd -list\n");
- ExtOut("Usage: !bpmd -clear <pending breakpoint number>\n");
- ExtOut("Usage: !bpmd -clearall\n");
+ ExtOut("Usage: " SOSPrefix "bpmd -md <MethodDesc pointer>\n");
+ ExtOut("Usage: " SOSPrefix "bpmd [-nofuturemodule] <module name> <managed function name> [<il offset>]\n");
+ ExtOut("Usage: " SOSPrefix "bpmd <filename>:<line number>\n");
+ ExtOut("Usage: " SOSPrefix "bpmd -list\n");
+ ExtOut("Usage: " SOSPrefix "bpmd -clear <pending breakpoint number>\n");
+ ExtOut("Usage: " SOSPrefix "bpmd -clearall\n");
+#ifdef FEATURE_PAL
+ ExtOut("See \"soshelp bpmd\" for more details.\n");
+#else
ExtOut("See \"!help bpmd\" for more details.\n");
+#endif
return Status;
}
// for filename+line number only print extra info if symbols for this module are loaded (it can get quite noisy otherwise).
if ((!fIsFilename) || (fIsFilename && symbolsLoaded == S_OK))
{
- ExtOut("Found %d methods in module %p...\n", numMethods, moduleList[iModule]);
-
for (int i = 0; i < numMethods; i++)
{
if (pMDs[i] == MD_NOT_YET_LOADED)
g_bpoints.Add(Filename, lineNumber, NULL);
}
bNeedNotificationExceptions = TRUE;
+
+ ULONG32 flags = 0;
+ g_clrData->GetOtherNotificationFlags(&flags);
+ flags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD);
+ g_clrData->SetOtherNotificationFlags(flags);
}
}
else /* We were given a MethodDesc already */
if (nArg != 2)
{
- ExtOut("Usage: !Name2EE module_name item_name\n");
- ExtOut(" or !Name2EE module_name!item_name\n");
+ ExtOut("Usage: " SOSPrefix "name2ee module_name item_name\n");
+ ExtOut(" or " SOSPrefix "name2ee module_name!item_name\n");
ExtOut(" use * for module_name to search all loaded modules\n");
- ExtOut("Examples: !Name2EE mscorlib.dll System.String.ToString\n");
- ExtOut(" !Name2EE *!System.String\n");
+ ExtOut("Examples: " SOSPrefix "name2ee mscorlib.dll System.String.ToString\n");
+ ExtOut(" " SOSPrefix "name2ee *!System.String\n");
return Status;
}
class ClrStackImplWithICorDebug
{
private:
-
static HRESULT DereferenceAndUnboxValue(ICorDebugValue * pValue, ICorDebugValue** ppOutputValue, BOOL * pIsNull = NULL)
{
HRESULT Status = S_OK;
(*ppOutputValue)->AddRef();
return S_OK;
}
+
static BOOL ShouldExpandVariable(__in_z WCHAR* varToExpand, __in_z WCHAR* currentExpansion)
{
if(currentExpansion == NULL || varToExpand == NULL) return FALSE;
return TRUE;
}
+
static BOOL IsEnum(ICorDebugValue * pInputValue)
{
ToRelease<ICorDebugValue> pValue;
return (_wcsncmp(baseTypeName, W("System.Enum"), 11) == 0);
}
+
static HRESULT AddGenericArgs(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
{
bool isFirst = true;
return S_OK;
}
+
static HRESULT GetTypeOfValue(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
{
HRESULT Status = S_OK;
}
return S_OK;
}
+
static HRESULT GetTypeOfValue(ICorDebugValue * pValue, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
{
HRESULT Status = S_OK;
return S_OK;
}
+
static HRESULT PrintEnumValue(ICorDebugValue* pInputValue, BYTE* enumValue)
{
HRESULT Status = S_OK;
return S_OK;
}
+
static HRESULT PrintStringValue(ICorDebugValue * pValue)
{
HRESULT Status;
return S_OK;
}
+
static HRESULT PrintSzArrayValue(ICorDebugValue * pValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
{
HRESULT Status = S_OK;
return S_OK;
}
+
static HRESULT PrintValue(ICorDebugValue * pInputValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
{
HRESULT Status = S_OK;
return S_OK;
}
+
static HRESULT PrintParameters(BOOL bParams, BOOL bLocals, IMetaDataImport * pMD, mdTypeDef typeDef, mdMethodDef methodDef, ICorDebugILFrame * pILFrame, ICorDebugModule * pModule, __in_z WCHAR* varToExpand, int currentFrame)
{
HRESULT Status = S_OK;
return S_OK;
}
-
-
-
-
-
public:
// This is the main worker function used if !clrstack is called with "-i" to indicate
static void PrintNativeStackFrame(TableOutput out, PDEBUG_STACK_FRAME frame, BOOL bSuppressLines)
{
- char filename[MAX_PATH_FNAME + 1];
+ char filename[MAX_LONGPATH + 1];
char symbol[1024];
ULONG64 displacement;
}
// Find our line in the text file
- char searchString[MAX_PATH_FNAME];
+ char searchString[MAX_LONGPATH];
sprintf_s(searchString, _countof(searchString), "COMMAND: %s.", pszCmdName);
LPSTR pStart = strstr(pText, searchString);
char symBuffer[SYM_BUFFER_SIZE];
PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL) symBuffer;
#else
-
#include <sys/stat.h>
#include <coreruncommon.h>
#include <dlfcn.h>
+#endif // !FEATURE_PAL
+
#include <coreclrhost.h>
+#include <set>
-void *SymbolReader::coreclrLib;
-ResolveSequencePointDelegate SymbolReader::resolveSequencePointDelegate;
LoadSymbolsForModuleDelegate SymbolReader::loadSymbolsForModuleDelegate;
+DisposeDelegate SymbolReader::disposeDelegate;
+ResolveSequencePointDelegate SymbolReader::resolveSequencePointDelegate;
GetLocalVariableName SymbolReader::getLocalVariableNameDelegate;
GetLineByILOffsetDelegate SymbolReader::getLineByILOffsetDelegate;
-#endif // !FEATURE_PAL
-
const char * const CorElementTypeName[ELEMENT_TYPE_MAX]=
{
#define TYPEINFO(e,ns,c,s,g,ia,ip,if,im,gv) c,
BOOL IsDumpFile ()
{
-#ifdef FEATURE_PAL
- return FALSE;
-#else
static int g_fDumpFile = -1;
if (g_fDumpFile == -1) {
ULONG Class;
g_fDumpFile = 0;
}
return g_fDumpFile != 0;
-#endif //FEATURE_PAL
}
BOOL g_InMinidumpSafeMode = FALSE;
return NULL;
}
- WCHAR StringData[MAX_PATH_FNAME];
+ WCHAR StringData[MAX_LONGPATH];
char fileName[sizeof(StringData)/2];
// Search all domains to find a module
ExtOut("Unable to get array of Assemblies for the given AppDomain..\n");
goto Failure;
}
-
- for (int nAssem = 0;nAssem < appDomain.AssemblyCount;nAssem ++)
+
+ for (int nAssem = 0; nAssem < appDomain.AssemblyCount; nAssem ++)
{
if (IsInterrupt())
{
ExtOut("Failed to get the modules for the given assembly.\n");
goto Failure;
}
-
- for (UINT nModule=0;nModule<assemblyData.ModuleCount;nModule++)
+
+ for (UINT nModule = 0; nModule < assemblyData.ModuleCount; nModule++)
{
if (IsInterrupt())
{
FileNameForModule ((DWORD_PTR)ModuleAddr, StringData);
int m;
- for (m = 0; StringData[m] != L'\0'; m ++)
+ for (m = 0; StringData[m] != L'\0'; m++)
+ {
fileName[m] = (char)StringData[m];
+ }
fileName[m] = '\0';
if ((mName == NULL) ||
DebuggerModuleNamesMatch(ModuleData.File, mName) ||
IsFusionLoadedModule(fileName, mName))
{
- AddToModuleList(moduleList, *numModule, maxList,
- (DWORD_PTR)ModuleAddr);
+ AddToModuleList(moduleList, *numModule, maxList, (DWORD_PTR)ModuleAddr);
}
}
else
ExtOut("Not JITTED yet. Use !bpmd -md %p to break on run.\n", SOS_PTR(md));
#else
- ExtOut("Not JITTED yet. Use 'sos bpmd -md %p' to break on run.\n", SOS_PTR(md));
+ ExtOut("Not JITTED yet. Use 'bpmd -md %p' to break on run.\n", SOS_PTR(md));
#endif
}
}
// Return Value:
// HRESULT indicating success or failure
//
-
HRESULT LoadClrDebugDll(void)
{
HRESULT hr = S_OK;
{
return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
}
- PFN_CLRDataCreateInstance pCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(hdac, "CLRDataCreateInstance");
- if (pCLRDataCreateInstance == NULL)
+ PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(hdac, "CLRDataCreateInstance");
+ if (pfnCLRDataCreateInstance == NULL)
{
FreeLibrary(hdac);
return CORDBG_E_MISSING_DEBUGGER_EXPORTS;
}
ICLRDataTarget *target = new DataTarget();
- hr = pCLRDataCreateInstance(__uuidof(IXCLRDataProcess), target, (void**)&s_clrDataProcess);
+ hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), target, (void**)&s_clrDataProcess);
if (FAILED(hr))
{
s_clrDataProcess = NULL;
//
//
-// This function retrieves ImageInfo related to the module
-// containing the addressed passed in "Base".
-//
-// NOTE: This doesn't work on xplat/PAL because the managed
-// assembly PE isn't in the native debugger's module list.
-//
-HRESULT
-GetImageFromBase(
- ___in ULONG64 Base,
- ___out ImageInfo* Image)
-{
- ULONG modIdx = 0;
- ULONG64 modBase = 0;
- if (FAILED(g_ExtSymbols->GetModuleByOffset(Base, 0, &modIdx, &modBase)))
- {
- return E_FAIL;
- }
-
- Image->modBase = modBase;
- return S_OK;
-}
-
-//
-// Retrieve ImageInfo associated with the IXCLRDataModule instance
-// passed in, and the extent type requested.
-HRESULT
-GetClrModuleImages(
- ___in IXCLRDataModule* Module,
- ___in CLRDataModuleExtentType DesiredType,
- ___out ImageInfo* FirstAdd)
-{
- HRESULT Status;
- CLRDATA_ENUM EnumExtents;
-
- if (FirstAdd)
- {
- FirstAdd->modBase = 0;
- }
-
- if ((Status = Module->StartEnumExtents(&EnumExtents)) != S_OK)
- {
- return FAILED(Status) ? Status : S_OK;
- }
-
- CLRDATA_MODULE_EXTENT Extent;
-
- while (Module->EnumExtent(&EnumExtents, &Extent) == S_OK)
- {
- if ((DesiredType == CLRDATA_MODULE_OTHER) || (DesiredType == Extent.type))
- {
- ImageInfo Image;
-
- if (FAILED(Status = GetImageFromBase(Extent.base, &Image)))
- {
- break;
- }
-
- Status = S_OK;
- if (FirstAdd)
- {
- *FirstAdd = Image;
- break;
- }
- }
- }
-
- Module->EndEnumExtents(EnumExtents);
- return Status;
-}
-
-//
-// Retrieve ImageInfo associated with the IXCLRDataModule instance
-// passed in. First look for NGENed module, second for IL modules.
-HRESULT
-FindClrModuleImage(
- ___in IXCLRDataModule* Module,
- ___out ImageInfo* Image)
-{
- HRESULT Status;
-
- // Prefer the ngen image, but look for the base image if necessary.
- if ((Status = GetClrModuleImages(Module, CLRDATA_MODULE_PREJIT_FILE, Image)) == S_OK &&
- Image->modBase != 0)
- {
- return S_OK;
- }
- if ((Status = GetClrModuleImages(Module, CLRDATA_MODULE_PE_FILE, Image)) == S_OK &&
- Image->modBase == 0)
- {
- Status = E_NOINTERFACE;
- }
- return Status;
-}
-
-//
// Retrieves the IXCLRDataMethodInstance* instance associated with the
// passed in native offset.
HRESULT
}
//
-// Retrieves the Token and ImageInfo associated with a managed method
-// identified by the passed in IXCLRDataMethodInstance* instance.
-HRESULT
-GetMethodInstanceTokenAndScope(
- ___in IXCLRDataMethodInstance* Method,
- ___out mdMethodDef* MethodToken,
- ___out ImageInfo* Image)
-{
- HRESULT Status;
- IXCLRDataModule* Module;
-
- if ((Status = Method->GetTokenAndScope(MethodToken, &Module)) != S_OK)
- {
- return Status;
- }
-#ifndef FEATURE_PAL
-
- Status = FindClrModuleImage(Module, Image);
-#endif //FEATURE_PAL
-
- Module->Release();
- return Status;
-}
-
-//
// Enumerates over the IL address map associated with the passed in
// managed method, and returns the highest non-epilog offset.
HRESULT
// represent an "IL offset".
HRESULT
ConvertNativeToIlOffset(
- ___in ULONG64 Native,
- __in_opt IXCLRDataMethodInstance* MethodInst,
- ___out ImageInfo* Image,
- ___out mdMethodDef* MethodToken,
- ___out PULONG32 MethodOffs)
+ ___in ULONG64 native,
+ ___out IXCLRDataModule** ppModule,
+ ___out mdMethodDef* methodToken,
+ ___out PULONG32 methodOffs)
{
+ ToRelease<IXCLRDataMethodInstance> pMethodInst(NULL);
HRESULT Status;
- if (!MethodInst)
+ if ((Status = GetClrMethodInstance(native, &pMethodInst)) != S_OK)
{
- if ((Status = GetClrMethodInstance(Native, &MethodInst)) != S_OK)
- {
- return Status;
- }
- }
- else
- {
- MethodInst->AddRef();
+ return Status;
}
- if ((Status = MethodInst->GetILOffsetsByAddress(Native, 1, NULL, MethodOffs)) != S_OK)
+ if ((Status = pMethodInst->GetILOffsetsByAddress(native, 1, NULL, methodOffs)) != S_OK)
{
- *MethodOffs = 0;
+ *methodOffs = 0;
}
else
{
- switch((LONG)*MethodOffs)
+ switch((LONG)*methodOffs)
{
case CLRDATA_IL_OFFSET_NO_MAPPING:
- MethodInst->Release();
return E_NOINTERFACE;
case CLRDATA_IL_OFFSET_PROLOG:
// Treat all of the prologue as part of
// the first source line.
- *MethodOffs = 0;
+ *methodOffs = 0;
break;
case CLRDATA_IL_OFFSET_EPILOG:
// Back up until we find the last real
// IL offset.
- if ((Status = GetLastMethodIlOffset(MethodInst, MethodOffs)) != S_OK)
+ if ((Status = GetLastMethodIlOffset(pMethodInst, methodOffs)) != S_OK)
{
- MethodInst->Release();
return Status;
}
break;
}
}
- Status = GetMethodInstanceTokenAndScope(MethodInst, MethodToken, Image);
-
- MethodInst->Release();
-
- return Status;
+ return pMethodInst->GetTokenAndScope(methodToken, ppModule);
}
// Based on a native offset, passed in the first argument this function
// identifies the corresponding source file name and line number.
HRESULT
GetLineByOffset(
- ___in ULONG64 Offset,
+ ___in ULONG64 offset,
___out ULONG *pLinenum,
- __out_ecount(cbFileName) LPSTR lpszFileName,
- ___in ULONG cbFileName)
+ __out_ecount(cchFileName) WCHAR* pwszFileName,
+ ___in ULONG cchFileName)
{
- HRESULT hr = S_OK;
- ULONG64 displacement = 0;
-
- // first let's try it the hard way, this will work with the public debuggers
- {
- ImageInfo Image = {0};
- ULONG64 modBase = 0;
- ULONG32 MethodToken;
- ULONG32 MethodOffs;
- ULONG64 IlOffset;
-
-#ifndef FEATURE_PAL
- ToRelease<IDebugSymbols3> spSym3(NULL);
- if (FAILED(g_ExtSymbols->QueryInterface(__uuidof(IDebugSymbols3), (void**)&spSym3)))
- {
- hr = E_FAIL;
- goto fallback;
- }
-#endif //!FEATURE_PAL
+ HRESULT Status = S_OK;
+ ULONG32 methodToken;
+ ULONG32 methodOffs;
- // find the image, method token and IL offset that correspond
- // to "Offset"
- if ((hr = ConvertNativeToIlOffset(Offset, NULL, &Image,
- &MethodToken, &MethodOffs)) != S_OK)
- {
- goto fallback;
- }
+ // Find the image, method token and IL offset that correspond to "offset"
+ ToRelease<IXCLRDataModule> pModule(NULL);
+ IfFailRet(ConvertNativeToIlOffset(offset, &pModule, &methodToken, &methodOffs));
-#ifndef FEATURE_PAL
- modBase = Image.modBase;
- DEBUG_MODULE_AND_ID id;
- DEBUG_SYMBOL_ENTRY symInfo;
- if (FAILED(spSym3->GetSymbolEntryByToken(modBase, MethodToken, &id))
- || FAILED(spSym3->GetSymbolEntryInformation(&id, &symInfo)))
- {
- hr = E_FAIL;
- goto fallback;
- }
+ ToRelease<IMetaDataImport> pMDImport(NULL);
+ IfFailRet(pModule->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport));
- IlOffset = symInfo.Offset + MethodOffs;
-#else
- // Source lines with 0xFEEFEE markers are filtered out on the managed side.
- const String &moduleName = ModuleNameFromIP(TO_CDADDR(Offset));
- return SymbolReader::GetLineByILOffset(moduleName.c_str(), MethodToken, MethodOffs, pLinenum, lpszFileName, cbFileName);
-#endif //!FEATURE_PAL
- //
- // Source maps for managed code can end
- // up with special 0xFEEFEE markers that
- // indicate don't-stop points. Try and
- // filter those out.
- //
- for (ULONG SkipCount = 64; SkipCount > 0; SkipCount--)
- {
- if (FAILED(g_ExtSymbols->GetLineByOffset(
- IlOffset,
- pLinenum,
- lpszFileName,
- cbFileName,
- NULL,
- &displacement)))
- {
- hr = E_FAIL;
- goto fallback;
- }
+ SymbolReader symbolReader;
+ IfFailRet(symbolReader.LoadSymbols(pMDImport, pModule));
- if (*pLinenum == 0xfeefee)
- {
- ++IlOffset;
- }
- else
- {
- return S_OK;
- }
- }
-
- // Fall into the regular translation as a last-ditch effort.
- Offset = IlOffset;
- }
-
-fallback:
- return g_ExtSymbols->GetLineByOffset(
- Offset,
- pLinenum,
- lpszFileName,
- cbFileName,
- NULL,
- &displacement);
+ return symbolReader.GetLineByILOffset(methodToken, methodOffs, pLinenum, pwszFileName, cchFileName);
}
-
void TableOutput::ReInit(int numColumns, int defaultColumnWidth, Alignment alignmentDefault, int indent, int padding)
{
Clear();
#endif // FEATURE_PAL
-HRESULT SymbolReader::LoadSymbols(IMetaDataImport * pMD, ICorDebugModule * pModule)
+HRESULT SymbolReader::LoadSymbols(___in IMetaDataImport* pMD, ___in ICorDebugModule* pModule)
{
HRESULT Status = S_OK;
- if(m_pSymReader != NULL) return S_OK;
-
BOOL isDynamic = FALSE;
BOOL isInMemory = FALSE;
IfFailRet(pModule->IsDynamic(&isDynamic));
IfFailRet(pModule->IsInMemory(&isInMemory));
- if(isDynamic)
+ if (isDynamic)
{
- // Dynamic and in memory assemblies are a special case
- // which we will ignore for now
- // now
- ExtOut("SOS Warning: Loading symbols for dynamic assemblies is not yet supported\n");
+ // Dynamic and in memory assemblies are a special case which we will ignore for now
+ ExtWarn("SOS Warning: Loading symbols for dynamic assemblies is not yet supported\n");
return E_FAIL;
}
- ULONG64 baseAddress = 0;
- IfFailRet(pModule->GetBaseAddress(&baseAddress));
+ ULONG64 peAddress = 0;
+ ULONG32 peSize = 0;
+ IfFailRet(pModule->GetBaseAddress(&peAddress));
+ IfFailRet(pModule->GetSize(&peSize));
ULONG32 len = 0;
WCHAR moduleName[MAX_LONGPATH];
IfFailRet(pModule->GetName(_countof(moduleName), &len, moduleName));
- return LoadSymbols(pMD, baseAddress, moduleName, isInMemory);
-}
-
-#ifdef FEATURE_PAL
-bool SymbolReader::SymbolReaderDllExists()
-{
- struct stat sb;
- std::string SymbolReaderDll(SymbolReaderDllName);
- SymbolReaderDll += ".dll";
-
- return stat(SymbolReaderDll.c_str(), &sb) == 0;
-}
-
-HRESULT SymbolReader::PrepareSymbolReader()
-{
- static bool attemptedSymbolReaderPreparation = false;
- if (attemptedSymbolReaderPreparation)
+#ifndef FEATURE_PAL
+ if (SUCCEEDED(LoadSymbolsForWindowsPDB(pMD, peAddress, moduleName, isInMemory)))
{
- // If we already tried to set up the symbol reader, we won't try again.
- return E_FAIL;
+ return S_OK;
}
+#endif // FEATURE_PAL
+ return LoadSymbolsForPortablePDB(moduleName, isInMemory, isInMemory, peAddress, peSize, 0, 0);
+}
- attemptedSymbolReaderPreparation = true;
-
- std::string absolutePath;
- std::string coreClrPath = g_ExtServices->GetCoreClrDirectory();
- if (!GetAbsolutePath(coreClrPath.c_str(), absolutePath))
+HRESULT SymbolReader::LoadSymbols(___in IMetaDataImport* pMD, ___in IXCLRDataModule* pModule)
+{
+ DacpGetModuleData moduleData;
+ HRESULT hr = moduleData.Request(pModule);
+ if (FAILED(hr))
{
- ExtErr("Error: fail to convert CLR files path to absolute path \n");
- return E_FAIL;
+ ExtOut("LoadSymbols moduleData.Request FAILED 0x%08x\n", hr);
+ return hr;
}
- coreClrPath.append("/");
- coreClrPath.append(coreClrDll);
- coreclrLib = dlopen(coreClrPath.c_str(), RTLD_NOW | RTLD_LOCAL);
- if (coreclrLib == nullptr)
+ if (moduleData.IsDynamic)
{
- ExtErr("Error: Fail to load %s\n", coreClrPath.c_str());
+ ExtWarn("SOS Warning: Loading symbols for dynamic assemblies is not yet supported\n");
return E_FAIL;
}
- void *hostHandle;
- unsigned int domainId;
- coreclr_initialize_ptr initializeCoreCLR =
- (coreclr_initialize_ptr)dlsym(coreclrLib, "coreclr_initialize");
-
- // FIXME: We should shutdown coreclr when it is not needed
- coreclr_shutdown_ptr shutdownCoreCLR =
- (coreclr_shutdown_ptr)dlsym(coreclrLib, "coreclr_shutdown");
-
- std::string tpaList;
- AddFilesFromDirectoryToTpaList(absolutePath.c_str(), tpaList);
-
- const char *propertyKeys[] = {
- "TRUSTED_PLATFORM_ASSEMBLIES", "APP_PATHS", "APP_NI_PATHS",
- "NATIVE_DLL_SEARCH_DIRECTORIES", "AppDomainCompatSwitch"};
-
- const char *propertyValues[] = {// TRUSTED_PLATFORM_ASSEMBLIES
- tpaList.c_str(),
- // APP_PATHS
- absolutePath.c_str(),
- // APP_NI_PATHS
- absolutePath.c_str(),
- // NATIVE_DLL_SEARCH_DIRECTORIES
- absolutePath.c_str(),
- // AppDomainCompatSwitch
- "UseLatestBehaviorWhenTFMNotSpecified"};
-
- std::string entryPointExecutablePath;
- if (!GetEntrypointExecutableAbsolutePath(entryPointExecutablePath))
+ ArrayHolder<WCHAR> pModuleName = new WCHAR[MAX_LONGPATH + 1];
+ ULONG32 nameLen = 0;
+ hr = pModule->GetFileName(MAX_LONGPATH, &nameLen, pModuleName);
+ if (FAILED(hr))
{
- ExtErr("Could not get full path to current executable");
- return E_FAIL;
+ ExtOut("LoadSymbols: IXCLRDataModule->GetFileName FAILED 0x%08x\n", hr);
+ return hr;
}
- HRESULT Status = initializeCoreCLR(entryPointExecutablePath.c_str(), "soscorerun",
- sizeof(propertyKeys) / sizeof(propertyKeys[0]),
- propertyKeys, propertyValues, &hostHandle, &domainId);
- if (Status != S_OK)
+#ifndef FEATURE_PAL
+ // TODO: in-memory windows PDB not supported
+ hr = LoadSymbolsForWindowsPDB(pMD, moduleData.LoadedPEAddress, pModuleName, moduleData.IsFileLayout);
+ if (SUCCEEDED(hr))
{
- ExtErr("Error: Fail to initialize CoreCLR\n");
- return Status;
+ return hr;
}
+#endif // FEATURE_PAL
- coreclr_create_delegate_ptr CreateDelegate =
- (coreclr_create_delegate_ptr)dlsym(coreclrLib, "coreclr_create_delegate");
-
- IfFailRet(CreateDelegate(hostHandle, domainId, SymbolReaderDllName,
- SymbolReaderClassName, "ResolveSequencePoint",
- (void **)&resolveSequencePointDelegate));
- IfFailRet(CreateDelegate(hostHandle, domainId, SymbolReaderDllName,
- SymbolReaderClassName, "LoadSymbolsForModule",
- (void **)&loadSymbolsForModuleDelegate));
- IfFailRet(CreateDelegate(hostHandle, domainId, SymbolReaderDllName,
- SymbolReaderClassName, "GetLocalVariableName",
- (void **)&getLocalVariableNameDelegate));
- IfFailRet(CreateDelegate(hostHandle, domainId, SymbolReaderDllName,
- SymbolReaderClassName, "GetLineByILOffset",
- (void **)&getLineByILOffsetDelegate));
- return Status;
+ return LoadSymbolsForPortablePDB(
+ pModuleName,
+ moduleData.IsInMemory,
+ moduleData.IsFileLayout,
+ moduleData.LoadedPEAddress,
+ moduleData.LoadedPESize,
+ moduleData.InMemoryPdbAddress,
+ moduleData.InMemoryPdbSize);
}
-HRESULT SymbolReader::GetLineByILOffset(__in_z const char* szModuleName, mdMethodDef MethodToken,
- ULONG64 IlOffset, ___out ULONG *pLinenum,
- __out_ecount(cbFileName) LPSTR lpszFileName,
- ___in ULONG cbFileName)
-{
- HRESULT Status = S_OK;
-
- if (getLineByILOffsetDelegate == nullptr)
- {
- IfFailRet(SymbolReader::PrepareSymbolReader());
- }
-
- BSTR wszFileName = SysAllocStringLen(0, cbFileName);
- if (wszFileName == nullptr)
- {
- return E_OUTOFMEMORY;
- }
-
- if (getLineByILOffsetDelegate(szModuleName, MethodToken, IlOffset, pLinenum, &wszFileName) == TRUE)
- {
- WideCharToMultiByte(CP_ACP, 0, wszFileName, (int) (_wcslen(wszFileName) + 1),
- lpszFileName, cbFileName, NULL, NULL);
- if (*pLinenum == 0)
- {
- *pLinenum = -1;
- Status = E_FAIL;
- }
- }
- else
- {
- Status = E_FAIL;
- }
-
- SysFreeString(wszFileName);
-
- return Status;
-}
-#endif //FEATURE_PAL
+#ifndef FEATURE_PAL
-HRESULT SymbolReader::LoadSymbols(IMetaDataImport * pMD, ULONG64 baseAddress, __in_z WCHAR* pModuleName, BOOL isInMemory)
+HRESULT SymbolReader::LoadSymbolsForWindowsPDB(___in IMetaDataImport* pMD, ___in ULONG64 peAddress, __in_z WCHAR* pModuleName, ___in BOOL isFileLayout)
{
- HRESULT Status = S_OK;
-
-#ifndef FEATURE_PAL
+ HRESULT Status = S_OK;
- if (m_pSymReader != NULL) return S_OK;
+ if (m_pSymReader != NULL)
+ return S_OK;
IfFailRet(CoInitialize(NULL));
-
// We now need a binder object that will take the module and return a
// reader object
ToRelease<ISymUnmanagedBinder3> pSymBinder;
return Status;
}
- // This should be about the right code to handle in-memory assemblies but after writing it all up I lost my repro case
- // This path is blocked up above but it would probably be pretty easy to get this working if someone wanted it
ToRelease<IUnknown> pCallback = NULL;
- if (isInMemory)
+ if (isFileLayout)
{
- pCallback = (IUnknown*) new PEOffsetMemoryReader(TO_TADDR(baseAddress));
+ pCallback = (IUnknown*) new PEOffsetMemoryReader(TO_TADDR(peAddress));
}
else
{
- pCallback = (IUnknown*) new PERvaMemoryReader(TO_TADDR(baseAddress));
+ pCallback = (IUnknown*) new PERvaMemoryReader(TO_TADDR(peAddress));
}
// TODO: this should be better integrated with windbg's symbol lookup
- Status = pSymBinder->GetReaderFromCallback(pMD, pModuleName, symbolPath, AllowRegistryAccess | AllowSymbolServerAccess | AllowOriginalPathAccess | AllowReferencePathAccess, pCallback, &m_pSymReader);
+ Status = pSymBinder->GetReaderFromCallback(pMD, pModuleName, symbolPath,
+ AllowRegistryAccess | AllowSymbolServerAccess | AllowOriginalPathAccess | AllowReferencePathAccess, pCallback, &m_pSymReader);
+
if (FAILED(Status) && m_pSymReader != NULL)
{
m_pSymReader->Release();
m_pSymReader = NULL;
}
return Status;
-#else
+}
+
+#endif // FEATURE_PAL
+
+//
+// Pass to managed helper code to read in-memory PEs/PDBs
+// Returns the number of bytes read.
+//
+int ReadMemoryForSymbols(ULONG64 address, char *buffer, int cb)
+{
+ ULONG read;
+ if (SafeReadMemory(address, (PVOID)buffer, cb, &read))
+ {
+ return read;
+ }
+ return 0;
+}
+
+HRESULT SymbolReader::LoadSymbolsForPortablePDB(__in_z WCHAR* pModuleName, ___in BOOL isInMemory, ___in BOOL isFileLayout,
+ ___in ULONG64 peAddress, ___in ULONG64 peSize, ___in ULONG64 inMemoryPdbAddress, ___in ULONG64 inMemoryPdbSize)
+{
+ HRESULT Status = S_OK;
+
if (loadSymbolsForModuleDelegate == nullptr)
{
IfFailRet(PrepareSymbolReader());
}
- WideCharToMultiByte(CP_ACP, 0, pModuleName, (int) (_wcslen(pModuleName) + 1), m_szModuleName, mdNameLen, NULL, NULL);
- if (loadSymbolsForModuleDelegate(m_szModuleName) == FALSE)
+ // The module name needs to be null for in-memory PE's.
+ ArrayHolder<char> szModuleName = nullptr;
+ if (!isInMemory && pModuleName != nullptr)
+ {
+ szModuleName = new char[MAX_LONGPATH];
+ if (WideCharToMultiByte(CP_ACP, 0, pModuleName, (int)(_wcslen(pModuleName) + 1), szModuleName, MAX_LONGPATH, NULL, NULL) == 0)
+ {
+ return E_FAIL;
+ }
+ }
+
+ m_symbolReaderHandle = loadSymbolsForModuleDelegate(szModuleName, isFileLayout, peAddress,
+ (int)peSize, inMemoryPdbAddress, (int)inMemoryPdbSize, ReadMemoryForSymbols);
+
+ if (m_symbolReaderHandle == 0)
{
return E_FAIL;
}
return Status;
-#endif // FEATURE_PAL
}
-HRESULT SymbolReader::GetNamedLocalVariable(ISymUnmanagedScope * pScope, ICorDebugILFrame * pILFrame, mdMethodDef methodToken, ULONG localIndex, __inout_ecount(paramNameLen) WCHAR* paramName, ULONG paramNameLen, ICorDebugValue** ppValue)
+#ifndef FEATURE_PAL
+
+void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList)
{
- HRESULT Status = S_OK;
+ const char * const tpaExtensions[] = {
+ "*.ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
+ "*.dll",
+ "*.ni.exe",
+ "*.exe",
+ };
+
+ std::set<std::string> addedAssemblies;
+
+ // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
+ // then files with .dll extension, etc.
+ for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
+ {
+ const char* ext = tpaExtensions[extIndex];
+ size_t extLength = strlen(ext);
+
+ std::string assemblyPath(directory);
+ assemblyPath.append(DIRECTORY_SEPARATOR_STR_A);
+ assemblyPath.append(tpaExtensions[extIndex]);
+
+ WIN32_FIND_DATAA data;
+ HANDLE findHandle = FindFirstFileA(assemblyPath.c_str(), &data);
+
+ if (findHandle != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ {
+
+ std::string filename(data.cFileName);
+ size_t extPos = filename.length() - extLength;
+ std::string filenameWithoutExt(filename.substr(0, extPos));
+
+ // Make sure if we have an assembly with multiple extensions present,
+ // we insert only one version of it.
+ if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
+ {
+ addedAssemblies.insert(filenameWithoutExt);
+
+ tpaList.append(directory);
+ tpaList.append(DIRECTORY_SEPARATOR_STR_A);
+ tpaList.append(filename);
+ tpaList.append(";");
+ }
+ }
+ }
+ while (0 != FindNextFileA(findHandle, &data));
+
+ FindClose(findHandle);
+ }
+ }
+}
+
+bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable)
+{
+ ArrayHolder<char> hostPath = new char[MAX_LONGPATH+1];
+ if (::GetModuleFileName(NULL, hostPath, MAX_LONGPATH) == 0)
+ {
+ return false;
+ }
+
+ entrypointExecutable.clear();
+ entrypointExecutable.append(hostPath);
+
+ return true;
+}
+
+#endif // FEATURE_PAL
+
+HRESULT SymbolReader::PrepareSymbolReader()
+{
+ static bool attemptedSymbolReaderPreparation = false;
+ if (attemptedSymbolReaderPreparation)
+ {
+ // If we already tried to set up the symbol reader, we won't try again.
+ return E_FAIL;
+ }
+
+ attemptedSymbolReaderPreparation = true;
+
+ std::string absolutePath;
+ std::string coreClrPath;
+ HRESULT Status;
+
#ifdef FEATURE_PAL
- if (getLocalVariableNameDelegate == nullptr)
+ coreClrPath = g_ExtServices->GetCoreClrDirectory();
+ if (!GetAbsolutePath(coreClrPath.c_str(), absolutePath))
{
- IfFailRet(PrepareSymbolReader());
+ ExtErr("Error: Failed to get coreclr absolute path\n");
+ return E_FAIL;
+ }
+ coreClrPath.append(DIRECTORY_SEPARATOR_STR_A);
+ coreClrPath.append(MAIN_CLR_DLL_NAME_A);
+#else
+ ULONG index;
+ Status = g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_MODULE_NAME_A, 0, &index, NULL);
+ if (FAILED(Status))
+ {
+ ExtErr("Error: Can't find coreclr module\n");
+ return Status;
}
+ ArrayHolder<char> szModuleName = new char[MAX_LONGPATH + 1];
+ Status = g_ExtSymbols->GetModuleNames(index, 0, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL, NULL, 0, NULL);
+ if (FAILED(Status))
+ {
+ ExtErr("Error: Failed to get coreclr module name\n");
+ return Status;
+ }
+ coreClrPath = szModuleName;
- BSTR wszParamName = SysAllocStringLen(0, mdNameLen);
- if (wszParamName == NULL)
+ // Parse off the module name to get just the path
+ size_t pos = coreClrPath.rfind(DIRECTORY_SEPARATOR_CHAR_A);
+ if (pos == std::string::npos)
{
- return E_OUTOFMEMORY;
+ ExtErr("Error: Failed to parse coreclr module name\n");
+ return E_FAIL;
}
+ absolutePath.assign(coreClrPath, 0, pos);
+#endif // FEATURE_PAL
- if (getLocalVariableNameDelegate(m_szModuleName, methodToken, localIndex, &wszParamName) == FALSE)
+ HMODULE coreclrLib = LoadLibraryA(coreClrPath.c_str());
+ if (coreclrLib == nullptr)
{
- SysFreeString(wszParamName);
+ ExtErr("Error: Failed to load %s\n", coreClrPath.c_str());
return E_FAIL;
}
- wcscpy_s(paramName, _wcslen(wszParamName) + 1, wszParamName);
- paramNameLen = _wcslen(paramName);
+ void *hostHandle;
+ unsigned int domainId;
+ coreclr_initialize_ptr initializeCoreCLR = (coreclr_initialize_ptr)GetProcAddress(coreclrLib, "coreclr_initialize");
+ if (initializeCoreCLR == nullptr)
+ {
+ ExtErr("Error: coreclr_initialize not found\n");
+ return E_FAIL;
+ }
+
+ std::string tpaList;
+ AddFilesFromDirectoryToTpaList(absolutePath.c_str(), tpaList);
- SysFreeString(wszParamName);
+ const char *propertyKeys[] = {
+ "TRUSTED_PLATFORM_ASSEMBLIES", "APP_PATHS", "APP_NI_PATHS",
+ "NATIVE_DLL_SEARCH_DIRECTORIES", "AppDomainCompatSwitch"};
- if (SUCCEEDED(pILFrame->GetLocalVariable(localIndex, ppValue)) && (*ppValue != NULL))
+ const char *propertyValues[] = {// TRUSTED_PLATFORM_ASSEMBLIES
+ tpaList.c_str(),
+ // APP_PATHS
+ absolutePath.c_str(),
+ // APP_NI_PATHS
+ absolutePath.c_str(),
+ // NATIVE_DLL_SEARCH_DIRECTORIES
+ absolutePath.c_str(),
+ // AppDomainCompatSwitch
+ "UseLatestBehaviorWhenTFMNotSpecified"};
+
+ std::string entryPointExecutablePath;
+ if (!GetEntrypointExecutableAbsolutePath(entryPointExecutablePath))
{
- return S_OK;
+ ExtErr("Could not get full path to current executable");
+ return E_FAIL;
}
- else
+
+ Status = initializeCoreCLR(entryPointExecutablePath.c_str(), "sos",
+ sizeof(propertyKeys) / sizeof(propertyKeys[0]), propertyKeys, propertyValues, &hostHandle, &domainId);
+
+ if (FAILED(Status))
+ {
+ ExtErr("Error: Fail to initialize CoreCLR %08x\n", Status);
+ return Status;
+ }
+
+ coreclr_create_delegate_ptr createDelegate = (coreclr_create_delegate_ptr)GetProcAddress(coreclrLib, "coreclr_create_delegate");
+ if (createDelegate == nullptr)
{
- *ppValue = NULL;
+ ExtErr("Error: coreclr_create_delegate not found\n");
return E_FAIL;
}
-#else
+ IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "LoadSymbolsForModule", (void **)&loadSymbolsForModuleDelegate));
+ IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "Dispose", (void **)&disposeDelegate));
+ IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "ResolveSequencePoint", (void **)&resolveSequencePointDelegate));
+ IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "GetLocalVariableName", (void **)&getLocalVariableNameDelegate));
+ IfFailRet(createDelegate(hostHandle, domainId, SymbolReaderDllName, SymbolReaderClassName, "GetLineByILOffset", (void **)&getLineByILOffsetDelegate));
+
+ return Status;
+}
+
+HRESULT SymbolReader::GetLineByILOffset(___in mdMethodDef methodToken, ___in ULONG64 ilOffset,
+ ___out ULONG *pLinenum, __out_ecount(cchFileName) WCHAR* pwszFileName, ___in ULONG cchFileName)
+{
+ HRESULT Status = S_OK;
+
+ if (m_symbolReaderHandle != 0)
+ {
+ _ASSERTE(getLineByILOffsetDelegate != nullptr);
+
+ BSTR bstrFileName = SysAllocStringLen(0, MAX_LONGPATH);
+ if (bstrFileName == nullptr)
+ {
+ return E_OUTOFMEMORY;
+ }
+ // Source lines with 0xFEEFEE markers are filtered out on the managed side.
+ if ((getLineByILOffsetDelegate(m_symbolReaderHandle, methodToken, ilOffset, pLinenum, &bstrFileName) == FALSE) || (*pLinenum == 0))
+ {
+ SysFreeString(bstrFileName);
+ return E_FAIL;
+ }
+ wcscpy_s(pwszFileName, cchFileName, bstrFileName);
+ SysFreeString(bstrFileName);
+ return S_OK;
+ }
+
+#ifndef FEATURE_PAL
+ if (m_pSymReader == NULL)
+ return E_FAIL;
+
+ ToRelease<ISymUnmanagedMethod> pSymMethod(NULL);
+ IfFailRet(m_pSymReader->GetMethod(methodToken, &pSymMethod));
+
+ ULONG32 seqPointCount = 0;
+ IfFailRet(pSymMethod->GetSequencePointCount(&seqPointCount));
+
+ if (seqPointCount == 0)
+ return E_FAIL;
+
+ // allocate memory for the objects to be fetched
+ ArrayHolder<ULONG32> offsets(new ULONG32[seqPointCount]);
+ ArrayHolder<ULONG32> lines(new ULONG32[seqPointCount]);
+ ArrayHolder<ULONG32> columns(new ULONG32[seqPointCount]);
+ ArrayHolder<ULONG32> endlines(new ULONG32[seqPointCount]);
+ ArrayHolder<ULONG32> endcolumns(new ULONG32[seqPointCount]);
+ ArrayHolder<ToRelease<ISymUnmanagedDocument>> documents(new ToRelease<ISymUnmanagedDocument>[seqPointCount]);
+
+ ULONG32 realSeqPointCount = 0;
+ IfFailRet(pSymMethod->GetSequencePoints(seqPointCount, &realSeqPointCount, offsets, &(documents[0]), lines, columns, endlines, endcolumns));
+
+ const ULONG32 HiddenLine = 0x00feefee;
+ int bestSoFar = -1;
+
+ for (int i = 0; i < (int)realSeqPointCount; i++)
+ {
+ if (offsets[i] > ilOffset)
+ break;
+
+ if (lines[i] != HiddenLine)
+ bestSoFar = i;
+ }
+
+ if (bestSoFar != -1)
+ {
+ ULONG32 cchNeeded = 0;
+ IfFailRet(documents[bestSoFar]->GetURL(cchFileName, &cchNeeded, pwszFileName));
+
+ *pLinenum = lines[bestSoFar];
+ return S_OK;
+ }
+#endif // FEATURE_PAL
+
+ return E_FAIL;
+}
+
+HRESULT SymbolReader::GetNamedLocalVariable(___in ISymUnmanagedScope * pScope, ___in ICorDebugILFrame * pILFrame, ___in mdMethodDef methodToken,
+ ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ICorDebugValue** ppValue)
+{
+ HRESULT Status = S_OK;
+
+ if (m_symbolReaderHandle != 0)
+ {
+ _ASSERTE(getLocalVariableNameDelegate != nullptr);
+
+ BSTR wszParamName = SysAllocStringLen(0, mdNameLen);
+ if (wszParamName == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (getLocalVariableNameDelegate(m_symbolReaderHandle, methodToken, localIndex, &wszParamName) == FALSE)
+ {
+ SysFreeString(wszParamName);
+ return E_FAIL;
+ }
+
+ wcscpy_s(paramName, paramNameLen, wszParamName);
+ SysFreeString(wszParamName);
+
+ if (FAILED(pILFrame->GetLocalVariable(localIndex, ppValue)) || (*ppValue == NULL))
+ {
+ *ppValue = NULL;
+ return E_FAIL;
+ }
+ return S_OK;
+ }
+
+#ifndef FEATURE_PAL
+ if (m_pSymReader == NULL)
+ return E_FAIL;
+
if (pScope == NULL)
{
ToRelease<ISymUnmanagedMethod> pSymMethod;
}
for (ULONG j = 0; j < numChildren; j++) pChildren[j]->Release();
-
}
+#endif // FEATURE_PAL
return E_FAIL;
-#endif // FEATURE_PAL
}
-HRESULT SymbolReader::GetNamedLocalVariable(ICorDebugFrame * pFrame, ULONG localIndex, __inout_ecount(paramNameLen) WCHAR* paramName, ULONG paramNameLen, ICorDebugValue** ppValue)
+HRESULT SymbolReader::GetNamedLocalVariable(___in ICorDebugFrame * pFrame, ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName,
+ ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue)
{
HRESULT Status = S_OK;
return GetNamedLocalVariable(NULL, pILFrame, methodDef, localIndex, paramName, paramNameLen, ppValue);
}
-HRESULT SymbolReader::ResolveSequencePoint(__in_z WCHAR* pFilename, ULONG32 lineNumber, TADDR mod, mdMethodDef* pToken, ULONG32* pIlOffset)
+HRESULT SymbolReader::ResolveSequencePoint(__in_z WCHAR* pFilename, ___in ULONG32 lineNumber, ___in TADDR mod, ___out mdMethodDef* pToken, ___out ULONG32* pIlOffset)
{
HRESULT Status = S_OK;
+ if (m_symbolReaderHandle != 0)
+ {
+ _ASSERTE(resolveSequencePointDelegate != nullptr);
+
+ char szName[mdNameLen];
+ if (WideCharToMultiByte(CP_ACP, 0, pFilename, (int)(_wcslen(pFilename) + 1), szName, mdNameLen, NULL, NULL) == 0)
+ {
+ return E_FAIL;
+ }
+ if (resolveSequencePointDelegate(m_symbolReaderHandle, szName, lineNumber, pToken, pIlOffset) == FALSE)
+ {
+ return E_FAIL;
+ }
+ return S_OK;
+ }
+
#ifndef FEATURE_PAL
+ if (m_pSymReader == NULL)
+ return E_FAIL;
+
ULONG32 cDocs = 0;
ULONG32 cDocsNeeded = 0;
ArrayHolder<ToRelease<ISymUnmanagedDocument>> pDocs = NULL;
ULONG32 filenameLen = (ULONG32) _wcslen(pFilename);
- for(ULONG32 i = 0; i < cDocs; i++)
+ for (ULONG32 i = 0; i < cDocs; i++)
{
ULONG32 cchUrl = 0;
ULONG32 cchUrlNeeded = 0;
IfFailRet(pDocs[i]->GetURL(cchUrl, &cchUrlNeeded, pUrl));
// If the URL is exactly as long as the filename then compare the two names directly
- if(cchUrl-1 == filenameLen)
+ if (cchUrl-1 == filenameLen)
{
- if(0!=_wcsicmp(pUrl, pFilename))
+ if (0!=_wcsicmp(pUrl, pFilename))
continue;
}
// does the URL suffix match [back]slash + filename?
- else if(cchUrl-1 > filenameLen)
+ else if (cchUrl-1 > filenameLen)
{
WCHAR* slashLocation = pUrl + (cchUrl - filenameLen - 2);
- if(*slashLocation != L'\\' && *slashLocation != L'/')
+ if (*slashLocation != L'\\' && *slashLocation != L'/')
continue;
- if(0 != _wcsicmp(slashLocation+1, pFilename))
+ if (0 != _wcsicmp(slashLocation+1, pFilename))
continue;
}
// URL is too short to match
continue;
ULONG32 closestLine = 0;
- if(FAILED(pDocs[i]->FindClosestLine(lineNumber, &closestLine)))
+ if (FAILED(pDocs[i]->FindClosestLine(lineNumber, &closestLine)))
continue;
ToRelease<ISymUnmanagedMethod> pSymUnmanagedMethod;
}
return S_OK;
}
- return E_FAIL;
-
-#else
- if (loadSymbolsForModuleDelegate == nullptr)
- {
- IfFailRet(PrepareSymbolReader());
- }
-
- char szName[mdNameLen];
- WideCharToMultiByte(CP_ACP, 0, pFilename, (int) (_wcslen(pFilename) + 1), szName, mdNameLen, NULL, NULL);
-
- WCHAR FileNameW[MAX_LONGPATH];
- char FileName[MAX_LONGPATH];
- FileNameForModule(mod, FileNameW);
-
- WideCharToMultiByte(CP_ACP, 0, FileNameW, (int) (_wcslen(FileNameW) + 1), FileName, MAX_LONGPATH, NULL, NULL);
- if (resolveSequencePointDelegate(FileName, szName, lineNumber, pToken, pIlOffset) == FALSE)
- {
- return E_FAIL;
- }
-
- return S_OK;
#endif // FEATURE_PAL
+
+ return E_FAIL;
}
static void AddAssemblyName(WString& methodOutput, CLRDATA_ADDRESS mdesc)
DacpModuleData dmd;
if (SUCCEEDED(dmd.Request(g_sos, mdescData.ModulePtr)))
{
- ArrayHolder<WCHAR> wszFileName = new WCHAR[MAX_LONGPATH+1];
- if (SUCCEEDED(g_sos->GetPEFileName(dmd.File, MAX_LONGPATH, wszFileName, NULL)))
+ ToRelease<IXCLRDataModule> pModule;
+ if (SUCCEEDED(g_sos->GetModule(mdescData.ModulePtr, &pModule)))
{
- if (wszFileName[0] != W('\0'))
+ ArrayHolder<WCHAR> wszFileName = new WCHAR[MAX_LONGPATH + 1];
+ ULONG32 nameLen = 0;
+ if (SUCCEEDED(pModule->GetFileName(MAX_LONGPATH, &nameLen, wszFileName)))
{
- WCHAR *pJustName = _wcsrchr(wszFileName, DIRECTORY_SEPARATOR_CHAR_W);
- if (pJustName == NULL)
- pJustName = wszFileName - 1;
-
- methodOutput += (pJustName + 1);
- methodOutput += W("!");
+ if (wszFileName[0] != W('\0'))
+ {
+ WCHAR *pJustName = _wcsrchr(wszFileName, DIRECTORY_SEPARATOR_CHAR_W);
+ if (pJustName == NULL)
+ pJustName = wszFileName - 1;
+ methodOutput += (pJustName + 1);
+ methodOutput += W("!");
+ }
}
}
}
return frameOutput;
}
-String ModuleNameFromIP(CLRDATA_ADDRESS ip)
-{
- CLRDATA_ADDRESS mdesc = 0;
- if (SUCCEEDED(g_sos->GetMethodDescPtrFromIP(ip, &mdesc)))
- {
- DacpMethodDescData mdescData;
- DacpModuleData moduleData;
- if (SUCCEEDED(mdescData.Request(g_sos, mdesc)))
- {
- if (SUCCEEDED(moduleData.Request(g_sos, mdescData.ModulePtr)))
- {
- ArrayHolder<WCHAR> wszModuleName = new WCHAR[MAX_LONGPATH+1];
- if (SUCCEEDED(g_sos->GetPEFileName(moduleData.File, MAX_LONGPATH, wszModuleName, NULL)))
- {
- ArrayHolder<char> szModuleName = new char[MAX_LONGPATH+1];
- WideCharToMultiByte(CP_ACP, 0, wszModuleName, (int) (_wcslen(wszModuleName) + 1), szModuleName, mdNameLen, NULL, NULL);
- return szModuleName.GetPtr();
- }
- }
- }
- }
- return "";
-}
-
WString MethodNameFromIP(CLRDATA_ADDRESS ip, BOOL bSuppressLines, BOOL bAssemblyName, BOOL bDisplacement)
{
ULONG linenum;
methodOutput = W("<unknown>");
}
- ArrayHolder<char> szFileName = new char[MAX_LONGPATH+1];
-
+ ArrayHolder<WCHAR> wszFileName = new WCHAR[MAX_LONGPATH];
if (!bSuppressLines &&
- SUCCEEDED(GetLineByOffset(TO_CDADDR(ip), &linenum, szFileName, MAX_LONGPATH)))
+ SUCCEEDED(GetLineByOffset(TO_CDADDR(ip), &linenum, wszFileName, MAX_LONGPATH)))
{
- int len = MultiByteToWideChar(CP_ACP, 0, szFileName, MAX_LONGPATH, NULL, 0);
- ArrayHolder<WCHAR> wszFileName = new WCHAR[len];
- MultiByteToWideChar(CP_ACP, 0, szFileName, MAX_LONGPATH, wszFileName, len);
-
methodOutput += WString(W(" [")) + wszFileName + W(" @ ") + Decimal(linenum) + W("]");
}
}
#endif // !FEATURE_PAL
-#ifdef FEATURE_PAL
-typedef BOOL (*ResolveSequencePointDelegate)(const char*, const char*, unsigned int, unsigned int*, unsigned int*);
-typedef BOOL (*LoadSymbolsForModuleDelegate)(const char*);
-typedef BOOL (*GetLocalVariableName)(const char*, int, int, BSTR*);
-typedef BOOL (*GetLineByILOffsetDelegate)(const char*, mdMethodDef, ULONG64, ULONG *, BSTR*);
-static const char *SymbolReaderDllName = "System.Diagnostics.Debug.SymbolReader";
-static const char *SymbolReaderClassName = "System.Diagnostics.Debug.SymbolReader.SymbolReader";
-#endif //FEATURE_PAL
+static const char *SymbolReaderDllName = "SOS.NETCore";
+static const char *SymbolReaderClassName = "SOS.SymbolReader";
+
+typedef int (*ReadMemoryDelegate)(ULONG64, char *, int);
+typedef ULONG64 (*LoadSymbolsForModuleDelegate)(const char*, BOOL, ULONG64, int, ULONG64, int, ReadMemoryDelegate);
+typedef void (*DisposeDelegate)(ULONG64);
+typedef BOOL (*ResolveSequencePointDelegate)(ULONG64, const char*, unsigned int, unsigned int*, unsigned int*);
+typedef BOOL (*GetLocalVariableName)(ULONG64, int, int, BSTR*);
+typedef BOOL (*GetLineByILOffsetDelegate)(ULONG64, mdMethodDef, ULONG64, ULONG *, BSTR*);
class SymbolReader
{
private:
+#ifndef FEATURE_PAL
ISymUnmanagedReader* m_pSymReader;
-#ifdef FEATURE_PAL
- static void *coreclrLib;
- char m_szModuleName[mdNameLen];
- static ResolveSequencePointDelegate resolveSequencePointDelegate;
+#endif
+ ULONG64 m_symbolReaderHandle;
+
static LoadSymbolsForModuleDelegate loadSymbolsForModuleDelegate;
+ static DisposeDelegate disposeDelegate;
+ static ResolveSequencePointDelegate resolveSequencePointDelegate;
static GetLocalVariableName getLocalVariableNameDelegate;
static GetLineByILOffsetDelegate getLineByILOffsetDelegate;
+ static HRESULT PrepareSymbolReader();
-#endif
-
-private:
- HRESULT GetNamedLocalVariable(ISymUnmanagedScope * pScope, ICorDebugILFrame * pILFrame, mdMethodDef methodToken, ULONG localIndex, __inout_ecount(paramNameLen) WCHAR* paramName, ULONG paramNameLen, ICorDebugValue** ppValue);
+ HRESULT GetNamedLocalVariable(___in ISymUnmanagedScope* pScope, ___in ICorDebugILFrame* pILFrame, ___in mdMethodDef methodToken, ___in ULONG localIndex,
+ __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue);
+ HRESULT LoadSymbolsForWindowsPDB(___in IMetaDataImport* pMD, ___in ULONG64 peAddress, __in_z WCHAR* pModuleName, ___in BOOL isFileLayout);
+ HRESULT LoadSymbolsForPortablePDB(__in_z WCHAR* pModuleName, ___in BOOL isInMemory, ___in BOOL isFileLayout, ___in ULONG64 peAddress, ___in ULONG64 peSize,
+ ___in ULONG64 inMemoryPdbAddress, ___in ULONG64 inMemoryPdbSize);
public:
- SymbolReader() : m_pSymReader (NULL) {
+ SymbolReader()
+ {
+#ifndef FEATURE_PAL
+ m_pSymReader = NULL;
+#endif
+ m_symbolReaderHandle = 0;
}
+
~SymbolReader()
{
+#ifndef FEATURE_PAL
if(m_pSymReader != NULL)
{
m_pSymReader->Release();
m_pSymReader = NULL;
}
+#endif
+ if (m_symbolReaderHandle != 0)
+ {
+ disposeDelegate(m_symbolReaderHandle);
+ m_symbolReaderHandle = 0;
+ }
}
-#ifdef FEATURE_PAL
- static HRESULT PrepareSymbolReader();
- static bool SymbolReaderDllExists();
- static HRESULT GetLineByILOffset(__in_z const char* szModuleName, mdMethodDef MethodToken, ULONG64 IlOffset, ___out ULONG *pLinenum,
- __out_ecount(cbFileName) LPSTR lpszFileName, ___in ULONG cbFileName);
-#endif //FEATURE_PAL
- HRESULT LoadSymbols(IMetaDataImport * pMD, ICorDebugModule * pModule);
- HRESULT LoadSymbols(IMetaDataImport * pMD, ULONG64 baseAddress, __in_z WCHAR* pModuleName, BOOL isInMemory);
- HRESULT GetNamedLocalVariable(ICorDebugFrame * pFrame, ULONG localIndex, __inout_ecount(paramNameLen) WCHAR* paramName, ULONG paramNameLen, ICorDebugValue** ppValue);
- HRESULT SymbolReader::ResolveSequencePoint(__in_z WCHAR* pFilename, ULONG32 lineNumber, TADDR mod, mdMethodDef* pToken, ULONG32* pIlOffset);
+ HRESULT LoadSymbols(___in IMetaDataImport* pMD, ___in ICorDebugModule* pModule);
+ HRESULT LoadSymbols(___in IMetaDataImport* pMD, ___in IXCLRDataModule* pModule);
+ HRESULT GetLineByILOffset(___in mdMethodDef MethodToken, ___in ULONG64 IlOffset, ___out ULONG *pLinenum, __out_ecount(cchFileName) WCHAR* pwszFileName, ___in ULONG cchFileName);
+ HRESULT GetNamedLocalVariable(___in ICorDebugFrame * pFrame, ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue);
+ HRESULT ResolveSequencePoint(__in_z WCHAR* pFilename, ___in ULONG32 lineNumber, ___in TADDR mod, ___out mdMethodDef* ___out pToken, ___out ULONG32* pIlOffset);
};
-
HRESULT
GetLineByOffset(
___in ULONG64 IP,
___out ULONG *pLinenum,
- __out_ecount(cbFileName) LPSTR lpszFileName,
- ___in ULONG cbFileName);
+ __out_ecount(cchFileName) WCHAR* pwszFileName,
+ ___in ULONG cchFileName);
/// X86 Context
#define X86_SIZE_OF_80387_REGISTERS 80
WString BuildRegisterOutput(const SOSStackRefData &ref, bool printObj = true);
WString MethodNameFromIP(CLRDATA_ADDRESS methodDesc, BOOL bSuppressLines = FALSE, BOOL bAssemblyName = FALSE, BOOL bDisplacement = FALSE);
-String ModuleNameFromIP(CLRDATA_ADDRESS ip);
HRESULT GetGCRefs(ULONG osID, SOSStackRefData **ppRefs, unsigned int *pRefCnt, SOSStackRefError **ppErrors, unsigned int *pErrCount);
WString GetFrameFromAddress(TADDR frameAddr, IXCLRDataStackWalk *pStackwalk = NULL, BOOL bAssemblyName = FALSE);
ULONG64 modBase;
};
-HRESULT
-GetClrModuleImages(
- ___in IXCLRDataModule* Module,
- ___in CLRDataModuleExtentType DesiredType,
- ___out ImageInfo* FirstAdd);
-
// Helper class used in ClrStackFromPublicInterface() to keep track of explicit EE Frames
// (i.e., "internal frames") on the stack. Call Init() with the appropriate
// ICorDebugThread3, and this class will initialize itself with the set of internal
#define DEBUG_CLASS_USER_WINDOWS 2
#define DEBUG_CLASS_IMAGE_FILE 3
+// Generic dump types. These can be used
+// with either user or kernel sessions.
+// Session-type-specific aliases are also
+// provided.
+#define DEBUG_DUMP_SMALL 1024
+#define DEBUG_DUMP_DEFAULT 1025
+#define DEBUG_DUMP_FULL 1026
+#define DEBUG_DUMP_IMAGE_FILE 1027
+#define DEBUG_DUMP_TRACE_LOG 1028
+#define DEBUG_DUMP_WINDOWS_CE 1029
+
#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386.
#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
<!-- List the projects that need to be built -->
<ItemGroup>
+ <Project Include="ToolBox\SOS\NETCore\SOS.NETCore.csproj" />
<Project Include="mscorlib\System.Private.CoreLib.csproj" />
<Project Include="mscorlib\facade\mscorlib.csproj" />
<Project Include="mscorlib\ref\mscorlib.csproj" />
DestinationFolder="$(BinDir)PDB" />
</Target>
-</Project>
\ No newline at end of file
+</Project>
/* [size_is][out] */ BYTE *outBuffer);
HRESULT RequestGetModulePtr(IN ULONG32 inBufferSize,
- IN BYTE* inBuffer,
- IN ULONG32 outBufferSize,
- OUT BYTE* outBuffer);
+ IN BYTE* inBuffer,
+ IN ULONG32 outBufferSize,
+ OUT BYTE* outBuffer);
+
+ HRESULT RequestGetModuleData(IN ULONG32 inBufferSize,
+ IN BYTE* inBuffer,
+ IN ULONG32 outBufferSize,
+ OUT BYTE* outBuffer);
Module* GetModule(void)
{
outGMA->ModulePtr = TO_CDADDR(PTR_HOST_TO_TADDR(m_module));
return S_OK;
}
+
+HRESULT
+ClrDataModule::RequestGetModuleData(
+ /* [in] */ ULONG32 inBufferSize,
+ /* [size_is][in] */ BYTE *inBuffer,
+ /* [in] */ ULONG32 outBufferSize,
+ /* [size_is][out] */ BYTE *outBuffer)
+{
+ // Validate params.
+ // Input: Nothing.
+ // Output: a DacpGetModuleData structure.
+ if ((inBufferSize != 0) ||
+ (inBuffer != NULL) ||
+ (outBufferSize != sizeof(DacpGetModuleData)) ||
+ (outBuffer == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ DacpGetModuleData * outGMD = reinterpret_cast<DacpGetModuleData *>(outBuffer);
+ ZeroMemory(outGMD, sizeof(DacpGetModuleData));
+
+ Module* pModule = GetModule();
+ PEFile *pPEFile = pModule->GetFile();
+
+ outGMD->PEFile = TO_CDADDR(PTR_HOST_TO_TADDR(pPEFile));
+ outGMD->IsDynamic = pModule->IsReflection();
+
+ if (pPEFile != NULL)
+ {
+ outGMD->IsInMemory = pPEFile->GetPath().IsEmpty();
+
+ COUNT_T peSize;
+ outGMD->LoadedPEAddress = TO_CDADDR(PTR_TO_TADDR(pPEFile->GetLoadedImageContents(&peSize)));
+ outGMD->LoadedPESize = (ULONG64)peSize;
+ outGMD->IsFileLayout = pPEFile->GetLoaded()->IsFlat();
+ }
+
+ // If there is a in memory symbol stream
+ CGrowableStream* stream = pModule->GetInMemorySymbolStream();
+ if (stream != NULL)
+ {
+ // Save the in-memory PDB address and size
+ MemoryRange range = stream->GetRawBuffer();
+ outGMD->InMemoryPdbAddress = TO_CDADDR(PTR_TO_TADDR(range.StartAddress()));
+ outGMD->InMemoryPdbSize = range.Size();
+ }
+
+ return S_OK;
+}
HRESULT STDMETHODCALLTYPE
ClrDataModule::Request(
break;
case DACDATAMODULEPRIV_REQUEST_GET_MODULEPTR:
- status = RequestGetModulePtr(inBufferSize, inBuffer,
- outBufferSize, outBuffer);
+ status = RequestGetModulePtr(inBufferSize, inBuffer, outBufferSize, outBuffer);
+ break;
+ case DACDATAMODULEPRIV_REQUEST_GET_MODULEDATA:
+ status = RequestGetModuleData(inBufferSize, inBuffer, outBufferSize, outBuffer);
break;
default:
hr = coreclr_create_delegate(*hostHandle,
*domainId,
- "System.Diagnostics.Debug.SymbolReader",
- "System.Diagnostics.Debug.SymbolReader.SymbolReader",
+ "SOS.NETCore",
+ "SOS.SymbolReader",
"GetInfoForMethod",
(void**)&getInfoForMethodDelegate);
// Private requests for DataModules
enum
{
- DACDATAMODULEPRIV_REQUEST_GET_MODULEPTR = 0xf0000000
+ DACDATAMODULEPRIV_REQUEST_GET_MODULEPTR = 0xf0000000,
+ DACDATAMODULEPRIV_REQUEST_GET_MODULEDATA = 0xf0000001
};
CLRDATA_ADDRESS ModulePtr;
HRESULT Request(IXCLRDataModule* pDataModule)
{
- return pDataModule->Request(DACDATAMODULEPRIV_REQUEST_GET_MODULEPTR,
- 0, NULL,
- sizeof(*this), (PBYTE) this);
+ return pDataModule->Request(DACDATAMODULEPRIV_REQUEST_GET_MODULEPTR, 0, NULL, sizeof(*this), (PBYTE) this);
+ }
+};
+
+struct DacpGetModuleData : ZeroInit<DacpGetModuleData>
+{
+ BOOL IsDynamic;
+ BOOL IsInMemory;
+ BOOL IsFileLayout;
+ CLRDATA_ADDRESS PEFile;
+ CLRDATA_ADDRESS LoadedPEAddress;
+ ULONG64 LoadedPESize;
+ CLRDATA_ADDRESS InMemoryPdbAddress;
+ ULONG64 InMemoryPdbSize;
+
+ HRESULT Request(IXCLRDataModule* pDataModule)
+ {
+ return pDataModule->Request(DACDATAMODULEPRIV_REQUEST_GET_MODULEDATA, 0, NULL, sizeof(*this), (PBYTE) this);
}
};
#endif
#define DIRECTORY_SEPARATOR_CHAR_A '\\'
+#define DIRECTORY_SEPARATOR_STR_A "\\"
#define DIRECTORY_SEPARATOR_CHAR_W W('\\')
#define DIRECTORY_SEPARATOR_STR_W W("\\")
if (s_symbolsMethodInfo == null)
{
s_symbolsType = Type.GetType(
- "System.Diagnostics.StackTraceSymbols, System.Diagnostics.StackTrace, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
+ "System.Diagnostics.StackTraceSymbols, System.Diagnostics.StackTrace, Version=4.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
throwOnError: false);
if (s_symbolsType == null)
return;
- s_symbolsMethodInfo = s_symbolsType.GetMethod("GetSourceLineInfo");
+ s_symbolsMethodInfo = s_symbolsType.GetMethod("GetSourceLineInfo", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
if (s_symbolsMethodInfo == null)
return;
}
for (int index = 0; index < iFrameCount; index++)
{
- // If there was some reason not to try get get the symbols from the portable PDB reader like the module was
+ // If there was some reason not to try get the symbols from the portable PDB reader like the module was
// ENC or the source/line info was already retrieved, the method token is 0.
if (rgiMethodToken[index] != 0)
{
const Module* mod = MethodDescPtr->GetMethodTable()->GetModule();
SString modName = mod->GetFile()->GetPath();
+ if (modName.IsEmpty())
+ return E_FAIL;
+
StackScratchBuffer scratch;
const char* szModName = modName.GetUTF8(scratch);
MethodDebugInfo* methodDebugInfo = new (nothrow) MethodDebugInfo();
if (methodDebugInfo == nullptr)
return E_OUTOFMEMORY;
+
methodDebugInfo->points = (SequencePointInfo*) CoTaskMemAlloc(sizeof(SequencePointInfo) * numMap);
if (methodDebugInfo->points == nullptr)
return E_OUTOFMEMORY;
+
methodDebugInfo->size = numMap;
if (getInfoForMethodDelegate(szModName, MethodDescPtr->GetMemberDef(), *methodDebugInfo) == FALSE)