Move SymbolReader.cs near SymbolReader.csproj
authorIgor Kulaychuk <i.kulaychuk@samsung.com>
Mon, 13 Nov 2017 19:28:16 +0000 (22:28 +0300)
committerIgor Kulaychuk <i.kulaychuk@samsung.com>
Mon, 13 Nov 2017 19:28:16 +0000 (22:28 +0300)
src/ToolBox/SOS/NETCore/SymbolReader.cs [deleted file]
src/debug/netcoredbg/SymbolReader.cs [new file with mode: 0644]

diff --git a/src/ToolBox/SOS/NETCore/SymbolReader.cs b/src/ToolBox/SOS/NETCore/SymbolReader.cs
deleted file mode 100644 (file)
index c7b71e9..0000000
+++ /dev/null
@@ -1,983 +0,0 @@
-// 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, CharSet = CharSet.Unicode)]
-        internal struct LocalVarInfo
-        {
-            public int startOffset;
-            public int endOffset;
-            public string name;
-        }
-
-        [StructLayout(LayoutKind.Sequential)]
-        internal struct MethodDebugInfo
-        {
-            public IntPtr points;
-            public int size;
-            public IntPtr locals;
-            public int localsSize;
-
-        }
-
-        [StructLayout(LayoutKind.Sequential)]
-        internal struct DbgSequencePoint
-        {
-            public int startLine;
-            public int startColumn;
-            public int endLine;
-            public int endColumn;
-            public int offset;
-        }
-
-        /// <summary>
-        /// Read memory callback
-        /// </summary>
-        /// <returns>number of bytes read or 0 for error</returns>
-        internal unsafe delegate int ReadMemoryDelegate(ulong 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 ulong _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(ulong 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(_address + (ulong)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>
-        /// Quick fix for Path.GetFileName which incorrectly handles Windows-style paths on Linux
-        /// </summary>
-        /// <param name="pathName"> File path to be processed </param>
-        /// <returns>Last component of path</returns>
-        private static string GetFileName(string pathName)
-        {
-            int pos = pathName.LastIndexOfAny(new char[] { '/', '\\'});
-            if (pos < 0)
-                return pathName;
-            return pathName.Substring(pos + 1);
-        }
-
-        /// <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, ulong loadedPeAddress, int loadedPeSize, 
-            ulong inMemoryPdbAddress, int inMemoryPdbSize, ReadMemoryDelegate readMemory)
-        {
-            try
-            {
-                TargetStream peStream = null;
-                if (assemblyPath == null && loadedPeAddress != 0)
-                {
-                    peStream = new TargetStream(loadedPeAddress, loadedPeSize, readMemory);
-                }
-                TargetStream pdbStream = null;
-                if (inMemoryPdbAddress != 0)
-                {
-                    pdbStream = new TargetStream(inMemoryPdbAddress, inMemoryPdbSize, readMemory);
-                }
-                OpenedReader openedReader = GetReader(assemblyPath, isFileLayout, peStream, pdbStream);
-                if (openedReader != null)
-                {
-                    GCHandle gch = GCHandle.Alloc(openedReader);
-                    return GCHandle.ToIntPtr(gch);
-                }
-            }
-            catch
-            {
-            }
-            return IntPtr.Zero;
-        }
-
-        /// <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 = 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 && 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();
-                if (methodDebugHandle.IsNil)
-                    return false;
-
-                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;
-        }
-
-        internal static bool GetSequencePoints(IntPtr symbolReaderHandle, int methodToken, out IntPtr points, out int pointsCount)
-        {
-            Debug.Assert(symbolReaderHandle != IntPtr.Zero);
-            pointsCount = 0;
-            points = IntPtr.Zero;
-
-            Debug.Assert(symbolReaderHandle != IntPtr.Zero);
-
-            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();
-                if (methodDebugHandle.IsNil)
-                    return false;
-
-                MethodDebugInformation methodDebugInfo = reader.GetMethodDebugInformation(methodDebugHandle);
-                SequencePointCollection sequencePoints = methodDebugInfo.GetSequencePoints();
-
-                var list = new List<DbgSequencePoint>();
-                foreach (SequencePoint p in sequencePoints)
-                {
-                    list.Add(new DbgSequencePoint() {
-                        startLine = p.StartLine,
-                        endLine = p.EndLine,
-                        startColumn = p.StartColumn,
-                        endColumn = p.EndColumn,
-                        offset = p.Offset
-                    });
-                }
-
-                if (list.Count == 0)
-                    return true;
-
-                var structSize = Marshal.SizeOf<DbgSequencePoint>();
-                IntPtr allPoints = Marshal.AllocCoTaskMem(list.Count * structSize);
-                var currentPtr = allPoints;
-
-                foreach (var p in list)
-                {
-                    Marshal.StructureToPtr(p, currentPtr, false);
-                    currentPtr = (IntPtr)(currentPtr.ToInt64() + structSize);
-                }
-
-                points = allPoints;
-                pointsCount = list.Count;
-                return true;
-            }
-            catch
-            {
-            }
-            return false;
-        }
-
-        internal static bool GetStepRangesFromIP(IntPtr symbolReaderHandle, int ip, int methodToken, out uint ilStartOffset, out uint ilEndOffset)
-        {
-            Debug.Assert(symbolReaderHandle != IntPtr.Zero);
-            ilStartOffset = 0;
-            ilEndOffset = 0;
-
-            Debug.Assert(symbolReaderHandle != IntPtr.Zero);
-
-            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();
-                if (methodDebugHandle.IsNil)
-                    return false;
-
-                MethodDebugInformation methodDebugInfo = reader.GetMethodDebugInformation(methodDebugHandle);
-                SequencePointCollection sequencePoints = methodDebugInfo.GetSequencePoints();
-
-                var list = new List<SequencePoint>();
-                foreach (SequencePoint p in sequencePoints)
-                    list.Add(p);
-
-                var pointsArray = list.ToArray();
-
-                for (int i = 1; i < pointsArray.Length; i++)
-                {
-                    SequencePoint p = pointsArray[i];
-
-                    if (p.Offset > ip && p.StartLine != 0 && p.StartLine != SequencePoint.HiddenLine)
-                    {
-                        ilStartOffset = (uint)pointsArray[0].Offset;
-                        for (int j = i - 1; j > 0; j--)
-                        {
-                            if (pointsArray[j].Offset <= ip)
-                            {
-                                ilStartOffset = (uint)pointsArray[j].Offset;
-                                break;
-                            }
-                        }
-                        ilEndOffset = (uint)p.Offset;
-                        return true;
-                    }
-                }
-
-                // let's handle correctly last step range from last sequence point till
-                // end of the method.
-                if (pointsArray.Length > 0)
-                {
-                    ilStartOffset = (uint)pointsArray[0].Offset;
-                    for (int j = pointsArray.Length - 1; j > 0; j--)
-                    {
-                        if (pointsArray[j].Offset <= ip)
-                        {
-                            ilStartOffset = (uint)pointsArray[j].Offset;
-                            break;
-                        }
-                    }
-                    ilEndOffset = ilStartOffset; // Should set this to IL code size in calling code
-                    return true;
-                }
-            }
-            catch
-            {
-            }
-            return false;
-        }
-
-        internal static bool GetLocalVariableNameAndScope(IntPtr symbolReaderHandle, int methodToken, int localIndex, out IntPtr localVarName, out int ilStartOffset, out int ilEndOffset)
-        {
-            localVarName = IntPtr.Zero;
-            ilStartOffset = 0;
-            ilEndOffset = 0;
-
-            string localVar = null;
-            if (!GetLocalVariableAndScopeByIndex(symbolReaderHandle, methodToken, localIndex, out localVar, out ilStartOffset, out ilEndOffset))
-                return false;
-
-            localVarName = Marshal.StringToBSTR(localVar);
-            localVar = null;
-            return true;
-        }
-
-        internal static bool GetLocalVariableAndScopeByIndex(IntPtr symbolReaderHandle, int methodToken, int localIndex, out string localVarName, out int ilStartOffset, out int ilEndOffset)
-        {
-            Debug.Assert(symbolReaderHandle != IntPtr.Zero);
-            localVarName = null;
-            ilStartOffset = 0;
-            ilEndOffset = 0;
-
-            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);
-                            ilStartOffset = scope.StartOffset;
-                            ilEndOffset = scope.EndOffset;
-                            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;
-        }
-        internal static bool GetLocalsInfoForMethod(string assemblyPath, int methodToken, out List<LocalVarInfo> locals)
-        {
-            locals = 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;
-
-                    locals = new List<LocalVarInfo>();
-
-                    MethodDebugInformationHandle methodDebugHandle =
-                        ((MethodDefinitionHandle)handle).ToDebugInformationHandle();
-                    LocalScopeHandleCollection localScopes = openedReader.Reader.GetLocalScopes(methodDebugHandle);
-                    foreach (LocalScopeHandle scopeHandle in localScopes)
-                    {
-                        LocalScope scope = openedReader.Reader.GetLocalScope(scopeHandle);
-                        LocalVariableHandleCollection localVars = scope.GetLocalVariables();
-                        foreach (LocalVariableHandle varHandle in localVars)
-                        {
-                            LocalVariable localVar = openedReader.Reader.GetLocalVariable(varHandle);
-                            if (localVar.Attributes == LocalVariableAttributes.DebuggerHidden)
-                                continue;
-                            LocalVarInfo info = new LocalVarInfo();
-                            info.startOffset = scope.StartOffset;
-                            info.endOffset = scope.EndOffset;
-                            info.name = openedReader.Reader.GetString(localVar.Name);
-                            locals.Add(info);
-                        }
-                    }
-                }
-                catch
-                {
-                    return false;
-                }
-            }
-            return true;
-
-        }
-        /// <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)
-        {
-            try
-            {
-                List<DebugInfo> points = null;
-                List<LocalVarInfo> locals = null;
-
-                if (!GetDebugInfoForMethod(assemblyPath, methodToken, out points))
-                {
-                    return false;
-                }
-
-                if (!GetLocalsInfoForMethod(assemblyPath, methodToken, out locals))
-                {
-                    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);
-                }
-
-                structSize = Marshal.SizeOf<LocalVarInfo>();
-
-                debugInfo.localsSize = locals.Count;
-                ptr = debugInfo.locals;
-
-                foreach (var info in locals)
-                {
-                    Marshal.StructureToPtr(info, ptr, false);
-                    ptr = (IntPtr)(ptr.ToInt64() + structSize);
-                }
-
-                return true;
-            }
-            catch
-            {
-            }
-            return false;
-        }
-
-        /// <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)
-                    {
-
-                        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 (Exception e) when (e is BadImageFormatException || e is IOException)
-            {
-                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), 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;
-            }
-        }
-    }
-}
diff --git a/src/debug/netcoredbg/SymbolReader.cs b/src/debug/netcoredbg/SymbolReader.cs
new file mode 100644 (file)
index 0000000..c7b71e9
--- /dev/null
@@ -0,0 +1,983 @@
+// 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, CharSet = CharSet.Unicode)]
+        internal struct LocalVarInfo
+        {
+            public int startOffset;
+            public int endOffset;
+            public string name;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct MethodDebugInfo
+        {
+            public IntPtr points;
+            public int size;
+            public IntPtr locals;
+            public int localsSize;
+
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct DbgSequencePoint
+        {
+            public int startLine;
+            public int startColumn;
+            public int endLine;
+            public int endColumn;
+            public int offset;
+        }
+
+        /// <summary>
+        /// Read memory callback
+        /// </summary>
+        /// <returns>number of bytes read or 0 for error</returns>
+        internal unsafe delegate int ReadMemoryDelegate(ulong 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 ulong _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(ulong 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(_address + (ulong)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>
+        /// Quick fix for Path.GetFileName which incorrectly handles Windows-style paths on Linux
+        /// </summary>
+        /// <param name="pathName"> File path to be processed </param>
+        /// <returns>Last component of path</returns>
+        private static string GetFileName(string pathName)
+        {
+            int pos = pathName.LastIndexOfAny(new char[] { '/', '\\'});
+            if (pos < 0)
+                return pathName;
+            return pathName.Substring(pos + 1);
+        }
+
+        /// <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, ulong loadedPeAddress, int loadedPeSize, 
+            ulong inMemoryPdbAddress, int inMemoryPdbSize, ReadMemoryDelegate readMemory)
+        {
+            try
+            {
+                TargetStream peStream = null;
+                if (assemblyPath == null && loadedPeAddress != 0)
+                {
+                    peStream = new TargetStream(loadedPeAddress, loadedPeSize, readMemory);
+                }
+                TargetStream pdbStream = null;
+                if (inMemoryPdbAddress != 0)
+                {
+                    pdbStream = new TargetStream(inMemoryPdbAddress, inMemoryPdbSize, readMemory);
+                }
+                OpenedReader openedReader = GetReader(assemblyPath, isFileLayout, peStream, pdbStream);
+                if (openedReader != null)
+                {
+                    GCHandle gch = GCHandle.Alloc(openedReader);
+                    return GCHandle.ToIntPtr(gch);
+                }
+            }
+            catch
+            {
+            }
+            return IntPtr.Zero;
+        }
+
+        /// <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 = 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 && 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();
+                if (methodDebugHandle.IsNil)
+                    return false;
+
+                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;
+        }
+
+        internal static bool GetSequencePoints(IntPtr symbolReaderHandle, int methodToken, out IntPtr points, out int pointsCount)
+        {
+            Debug.Assert(symbolReaderHandle != IntPtr.Zero);
+            pointsCount = 0;
+            points = IntPtr.Zero;
+
+            Debug.Assert(symbolReaderHandle != IntPtr.Zero);
+
+            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();
+                if (methodDebugHandle.IsNil)
+                    return false;
+
+                MethodDebugInformation methodDebugInfo = reader.GetMethodDebugInformation(methodDebugHandle);
+                SequencePointCollection sequencePoints = methodDebugInfo.GetSequencePoints();
+
+                var list = new List<DbgSequencePoint>();
+                foreach (SequencePoint p in sequencePoints)
+                {
+                    list.Add(new DbgSequencePoint() {
+                        startLine = p.StartLine,
+                        endLine = p.EndLine,
+                        startColumn = p.StartColumn,
+                        endColumn = p.EndColumn,
+                        offset = p.Offset
+                    });
+                }
+
+                if (list.Count == 0)
+                    return true;
+
+                var structSize = Marshal.SizeOf<DbgSequencePoint>();
+                IntPtr allPoints = Marshal.AllocCoTaskMem(list.Count * structSize);
+                var currentPtr = allPoints;
+
+                foreach (var p in list)
+                {
+                    Marshal.StructureToPtr(p, currentPtr, false);
+                    currentPtr = (IntPtr)(currentPtr.ToInt64() + structSize);
+                }
+
+                points = allPoints;
+                pointsCount = list.Count;
+                return true;
+            }
+            catch
+            {
+            }
+            return false;
+        }
+
+        internal static bool GetStepRangesFromIP(IntPtr symbolReaderHandle, int ip, int methodToken, out uint ilStartOffset, out uint ilEndOffset)
+        {
+            Debug.Assert(symbolReaderHandle != IntPtr.Zero);
+            ilStartOffset = 0;
+            ilEndOffset = 0;
+
+            Debug.Assert(symbolReaderHandle != IntPtr.Zero);
+
+            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();
+                if (methodDebugHandle.IsNil)
+                    return false;
+
+                MethodDebugInformation methodDebugInfo = reader.GetMethodDebugInformation(methodDebugHandle);
+                SequencePointCollection sequencePoints = methodDebugInfo.GetSequencePoints();
+
+                var list = new List<SequencePoint>();
+                foreach (SequencePoint p in sequencePoints)
+                    list.Add(p);
+
+                var pointsArray = list.ToArray();
+
+                for (int i = 1; i < pointsArray.Length; i++)
+                {
+                    SequencePoint p = pointsArray[i];
+
+                    if (p.Offset > ip && p.StartLine != 0 && p.StartLine != SequencePoint.HiddenLine)
+                    {
+                        ilStartOffset = (uint)pointsArray[0].Offset;
+                        for (int j = i - 1; j > 0; j--)
+                        {
+                            if (pointsArray[j].Offset <= ip)
+                            {
+                                ilStartOffset = (uint)pointsArray[j].Offset;
+                                break;
+                            }
+                        }
+                        ilEndOffset = (uint)p.Offset;
+                        return true;
+                    }
+                }
+
+                // let's handle correctly last step range from last sequence point till
+                // end of the method.
+                if (pointsArray.Length > 0)
+                {
+                    ilStartOffset = (uint)pointsArray[0].Offset;
+                    for (int j = pointsArray.Length - 1; j > 0; j--)
+                    {
+                        if (pointsArray[j].Offset <= ip)
+                        {
+                            ilStartOffset = (uint)pointsArray[j].Offset;
+                            break;
+                        }
+                    }
+                    ilEndOffset = ilStartOffset; // Should set this to IL code size in calling code
+                    return true;
+                }
+            }
+            catch
+            {
+            }
+            return false;
+        }
+
+        internal static bool GetLocalVariableNameAndScope(IntPtr symbolReaderHandle, int methodToken, int localIndex, out IntPtr localVarName, out int ilStartOffset, out int ilEndOffset)
+        {
+            localVarName = IntPtr.Zero;
+            ilStartOffset = 0;
+            ilEndOffset = 0;
+
+            string localVar = null;
+            if (!GetLocalVariableAndScopeByIndex(symbolReaderHandle, methodToken, localIndex, out localVar, out ilStartOffset, out ilEndOffset))
+                return false;
+
+            localVarName = Marshal.StringToBSTR(localVar);
+            localVar = null;
+            return true;
+        }
+
+        internal static bool GetLocalVariableAndScopeByIndex(IntPtr symbolReaderHandle, int methodToken, int localIndex, out string localVarName, out int ilStartOffset, out int ilEndOffset)
+        {
+            Debug.Assert(symbolReaderHandle != IntPtr.Zero);
+            localVarName = null;
+            ilStartOffset = 0;
+            ilEndOffset = 0;
+
+            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);
+                            ilStartOffset = scope.StartOffset;
+                            ilEndOffset = scope.EndOffset;
+                            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;
+        }
+        internal static bool GetLocalsInfoForMethod(string assemblyPath, int methodToken, out List<LocalVarInfo> locals)
+        {
+            locals = 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;
+
+                    locals = new List<LocalVarInfo>();
+
+                    MethodDebugInformationHandle methodDebugHandle =
+                        ((MethodDefinitionHandle)handle).ToDebugInformationHandle();
+                    LocalScopeHandleCollection localScopes = openedReader.Reader.GetLocalScopes(methodDebugHandle);
+                    foreach (LocalScopeHandle scopeHandle in localScopes)
+                    {
+                        LocalScope scope = openedReader.Reader.GetLocalScope(scopeHandle);
+                        LocalVariableHandleCollection localVars = scope.GetLocalVariables();
+                        foreach (LocalVariableHandle varHandle in localVars)
+                        {
+                            LocalVariable localVar = openedReader.Reader.GetLocalVariable(varHandle);
+                            if (localVar.Attributes == LocalVariableAttributes.DebuggerHidden)
+                                continue;
+                            LocalVarInfo info = new LocalVarInfo();
+                            info.startOffset = scope.StartOffset;
+                            info.endOffset = scope.EndOffset;
+                            info.name = openedReader.Reader.GetString(localVar.Name);
+                            locals.Add(info);
+                        }
+                    }
+                }
+                catch
+                {
+                    return false;
+                }
+            }
+            return true;
+
+        }
+        /// <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)
+        {
+            try
+            {
+                List<DebugInfo> points = null;
+                List<LocalVarInfo> locals = null;
+
+                if (!GetDebugInfoForMethod(assemblyPath, methodToken, out points))
+                {
+                    return false;
+                }
+
+                if (!GetLocalsInfoForMethod(assemblyPath, methodToken, out locals))
+                {
+                    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);
+                }
+
+                structSize = Marshal.SizeOf<LocalVarInfo>();
+
+                debugInfo.localsSize = locals.Count;
+                ptr = debugInfo.locals;
+
+                foreach (var info in locals)
+                {
+                    Marshal.StructureToPtr(info, ptr, false);
+                    ptr = (IntPtr)(ptr.ToInt64() + structSize);
+                }
+
+                return true;
+            }
+            catch
+            {
+            }
+            return false;
+        }
+
+        /// <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)
+                    {
+
+                        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 (Exception e) when (e is BadImageFormatException || e is IOException)
+            {
+                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), 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;
+            }
+        }
+    }
+}