Skip to content

Commit

Permalink
Merge pull request #485 from ElektroKill/new-roslyn-cdi
Browse files Browse the repository at this point in the history
Add support for new Roslyn CustomDebugInfo
  • Loading branch information
wtfsck authored Nov 18, 2022
2 parents 44ee7c8 + 5ac12c3 commit e43bd76
Show file tree
Hide file tree
Showing 12 changed files with 270 additions and 8 deletions.
2 changes: 2 additions & 0 deletions src/DotNet/Pdb/CustomDebugInfoGuids.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public static class CustomDebugInfoGuids {
public static readonly Guid TupleElementNames = new Guid("ED9FDF71-8879-4747-8ED3-FE5EDE3CE710");
public static readonly Guid CompilationMetadataReferences = new Guid("7E4D4708-096E-4C5C-AEDA-CB10BA6A740D");
public static readonly Guid CompilationOptions = new Guid("B5FEEC05-8CD0-4A83-96DA-466284BB4BD8");
public static readonly Guid TypeDefinitionDocuments = new Guid("932E74BC-DBA9-4478-8D46-0F32A7BAB3D3");
public static readonly Guid EncStateMachineStateMap = new Guid("8B78CD68-2EDE-420B-980B-E15884B8AAA3");
#pragma warning restore 1591 // Missing XML comment for publicly visible type or member
}
}
2 changes: 2 additions & 0 deletions src/DotNet/Pdb/Dss/SymbolDocumentImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,7 @@ public override PdbCustomDebugInfo[] CustomDebugInfos {
}
}
PdbCustomDebugInfo[] customDebugInfos;

public override MDToken? MDToken => null;
}
}
2 changes: 2 additions & 0 deletions src/DotNet/Pdb/Managed/DbiDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public override PdbCustomDebugInfo[] CustomDebugInfos {
}
PdbCustomDebugInfo[] customDebugInfos;

public override MDToken? MDToken => null;

public DbiDocument(string url) {
this.url = url;
documentType = SymDocumentType.Text;
Expand Down
174 changes: 171 additions & 3 deletions src/DotNet/Pdb/PdbCustomDebugInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System;
using System.Collections.Generic;
using System.Threading;
using dnlib.DotNet.Emit;

namespace dnlib.DotNet.Pdb {
Expand Down Expand Up @@ -111,6 +112,16 @@ public enum PdbCustomDebugInfoKind {
/// <see cref="PdbCompilationOptionsCustomDebugInfo"/>
/// </summary>
CompilationOptions,

/// <summary>
/// <see cref="PdbTypeDefinitionDocumentsDebugInfo"/>
/// </summary>
TypeDefinitionDocuments,

/// <summary>
/// <see cref="PdbEditAndContinueStateMachineStateMapDebugInfo"/>
/// </summary>
EditAndContinueStateMachineStateMap,
}

/// <summary>
Expand Down Expand Up @@ -665,7 +676,7 @@ public sealed class PortablePdbTupleElementNamesCustomDebugInfo : PdbCustomDebug

/// <summary>
/// Async method stepping info
///
///
/// It's internal and translated to a <see cref="PdbAsyncMethodCustomDebugInfo"/>
/// </summary>
sealed class PdbAsyncMethodSteppingInformationCustomDebugInfo : PdbCustomDebugInfo {
Expand Down Expand Up @@ -777,9 +788,9 @@ public sealed class PdbEmbeddedSourceCustomDebugInfo : PdbCustomDebugInfo {

/// <summary>
/// Gets the source code blob.
///
///
/// It's not decompressed and converted to a string because the encoding isn't specified.
///
///
/// https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#embedded-source-c-and-vb-compilers
/// </summary>
public byte[] SourceCodeBlob { get; set; }
Expand Down Expand Up @@ -1101,4 +1112,161 @@ public sealed class PdbCompilationOptionsCustomDebugInfo : PdbCustomDebugInfo {
/// </summary>
public PdbCompilationOptionsCustomDebugInfo() => Options = new List<KeyValuePair<string, string>>();
}

/// <summary>
/// Links a TypeDef with no method IL with a PDB document.
/// </summary>
public class PdbTypeDefinitionDocumentsDebugInfo : PdbCustomDebugInfo {
/// <summary>
/// Returns <see cref="PdbCustomDebugInfoKind.TypeDefinitionDocuments"/>
/// </summary>
public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.TypeDefinitionDocuments;

/// <summary>
/// Gets the custom debug info guid, see <see cref="CustomDebugInfoGuids"/>
/// </summary>
public override Guid Guid => CustomDebugInfoGuids.TypeDefinitionDocuments;

/// <summary>
/// List of documents associated with the type
/// </summary>
public IList<PdbDocument> Documents {
get {
if (documents is null)
InitializeDocuments();
return documents;
}
}
/// <summary/>
protected IList<PdbDocument> documents;
/// <summary>Initializes <see cref="documents"/></summary>
protected virtual void InitializeDocuments() =>
Interlocked.CompareExchange(ref documents, new List<PdbDocument>(), null);
}

sealed class PdbTypeDefinitionDocumentsDebugInfoMD : PdbTypeDefinitionDocumentsDebugInfo {
readonly ModuleDef readerModule;
readonly IList<MDToken> documentTokens;

protected override void InitializeDocuments() {
var list = new List<PdbDocument>(documentTokens.Count);
if (readerModule.PdbState is not null) {
for (var i = 0; i < documentTokens.Count; i++) {
if (readerModule.PdbState.tokenToDocument.TryGetValue(documentTokens[i], out var document))
list.Add(document);
}
}
Interlocked.CompareExchange(ref documents, list, null);
}

public PdbTypeDefinitionDocumentsDebugInfoMD(ModuleDef readerModule, IList<MDToken> documentTokens) {
this.readerModule = readerModule;
this.documentTokens = documentTokens;
}
}

/// <summary>
/// Contains the EnC state machine state mapping
/// </summary>
public sealed class PdbEditAndContinueStateMachineStateMapDebugInfo : PdbCustomDebugInfo {
/// <summary>
/// Returns <see cref="PdbCustomDebugInfoKind.TypeDefinitionDocuments"/>
/// </summary>
public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.EditAndContinueStateMachineStateMap;

/// <summary>
/// Gets the custom debug info guid, see <see cref="CustomDebugInfoGuids"/>
/// </summary>
public override Guid Guid => CustomDebugInfoGuids.EncStateMachineStateMap;

/// <summary>
/// State machine states
/// </summary>
public List<StateMachineStateInfo> StateMachineStates { get; }

/// <summary>
/// Constructor
/// </summary>
public PdbEditAndContinueStateMachineStateMapDebugInfo() => StateMachineStates = new List<StateMachineStateInfo>();
}

/// <summary>
/// State machine state information used by debuggers
/// </summary>
public struct StateMachineStateInfo {
/// <summary>
/// Syntax offset
/// </summary>
public readonly int SyntaxOffset;

/// <summary>
/// State machine state
/// </summary>
public readonly StateMachineState State;

/// <summary>
/// Constructor
/// </summary>
/// <param name="syntaxOffset">Syntax offset</param>
/// <param name="state">State machine state</param>
public StateMachineStateInfo(int syntaxOffset, StateMachineState state) {
SyntaxOffset = syntaxOffset;
State = state;
}
}

/// <summary>
/// State machine state
/// from Roslyn: StateMachineState.cs
/// </summary>
public enum StateMachineState {
/// <summary>
/// First state of an async iterator state machine that is used to resume the machine after yield return.
/// Initial state is not used to resume state machine that yielded. State numbers decrease as the iterator makes progress.
/// </summary>
FirstResumableAsyncIteratorState = InitialAsyncIteratorState - 1,

/// <summary>
/// Initial iterator state of an async iterator.
/// Distinct from <see cref="NotStartedOrRunningState"/> so that DisposeAsync can throw in latter case.
/// </summary>
InitialAsyncIteratorState = -3,

/// <summary>
/// First state of an iterator state machine. State numbers decrease for subsequent finalize states.
/// </summary>
FirstIteratorFinalizeState = -3,

/// <summary>
/// The last state of a state machine.
/// </summary>
FinishedState = -2,

/// <summary>
/// State machine not started or is running
/// </summary>
NotStartedOrRunningState = -1,

/// <summary>
/// First unused state
/// </summary>
FirstUnusedState = 0,

/// <summary>
/// First state in async state machine that is used to resume the machine after await.
/// State numbers increase as the async computation makes progress.
/// </summary>
FirstResumableAsyncState = 0,

/// <summary>
/// Initial iterator state of an iterator.
/// </summary>
InitialIteratorState = 0,

/// <summary>
/// First state in iterator state machine that is used to resume the machine after yield return.
/// Initial state is not used to resume state machine that yielded. State numbers increase as the iterator makes progress.
/// </summary>
FirstResumableIteratorState = InitialIteratorState + 1,
}
}
6 changes: 6 additions & 0 deletions src/DotNet/Pdb/PdbDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ public sealed class PdbDocument : IHasCustomDebugInformation {
public IList<PdbCustomDebugInfo> CustomDebugInfos => customDebugInfos;
IList<PdbCustomDebugInfo> customDebugInfos;

/// <summary>
/// Gets the Metadata token of the document if available.
/// </summary>
public MDToken? MDToken { get; internal set; }

/// <summary>
/// Default constructor
/// </summary>
Expand Down Expand Up @@ -86,6 +91,7 @@ internal void Initialize(SymbolDocument symDoc) {
customDebugInfos = new List<PdbCustomDebugInfo>();
foreach (var cdi in symDoc.CustomDebugInfos)
customDebugInfos.Add(cdi);
MDToken = symDoc.MDToken;
}

/// <summary>
Expand Down
10 changes: 9 additions & 1 deletion src/DotNet/Pdb/PdbState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace dnlib.DotNet.Pdb {
public sealed class PdbState {
readonly SymbolReader reader;
readonly Dictionary<PdbDocument, PdbDocument> docDict = new Dictionary<PdbDocument, PdbDocument>();
internal readonly Dictionary<MDToken, PdbDocument> tokenToDocument = new Dictionary<MDToken, PdbDocument>();
MethodDef userEntryPoint;
readonly Compiler compiler;
readonly PdbFileKind originalPdbFileKind;
Expand Down Expand Up @@ -64,7 +65,7 @@ public bool HasDocuments {
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif

}
}

Expand Down Expand Up @@ -123,6 +124,8 @@ PdbDocument Add_NoLock(PdbDocument doc) {
if (docDict.TryGetValue(doc, out var orig))
return orig;
docDict.Add(doc, doc);
if (doc.MDToken.HasValue)
tokenToDocument.Add(doc.MDToken.Value, doc);
return doc;
}

Expand All @@ -133,6 +136,8 @@ PdbDocument Add_NoLock(SymbolDocument symDoc) {
// Expensive part, can read source code etc
doc.Initialize(symDoc);
docDict.Add(doc, doc);
if (symDoc.MDToken.HasValue)
tokenToDocument.Add(symDoc.MDToken.Value, doc);
return doc;
}

Expand All @@ -145,6 +150,8 @@ public bool Remove(PdbDocument doc) {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
if (doc.MDToken.HasValue)
tokenToDocument.Remove(doc.MDToken.Value);
return docDict.Remove(doc);
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
Expand Down Expand Up @@ -186,6 +193,7 @@ public List<PdbDocument> RemoveAllDocuments(bool returnDocs) {
theLock.EnterWriteLock(); try {
#endif
var docs = returnDocs ? new List<PdbDocument>(docDict.Values) : null;
tokenToDocument.Clear();
docDict.Clear();
return docs;
#if THREAD_SAFE
Expand Down
32 changes: 32 additions & 0 deletions src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ PdbCustomDebugInfo Read(Guid kind) {
return ReadCompilationMetadataReferences();
if (kind == CustomDebugInfoGuids.CompilationOptions)
return ReadCompilationOptions();
if (kind == CustomDebugInfoGuids.TypeDefinitionDocuments)
return ReadTypeDefinitionDocuments();
if (kind == CustomDebugInfoGuids.EncStateMachineStateMap)
return ReadEncStateMachineStateMap();
Debug.Fail("Unknown custom debug info guid: " + kind.ToString());
return new PdbUnknownCustomDebugInfo(kind, reader.ReadRemainingBytes());
}
Expand Down Expand Up @@ -228,6 +232,34 @@ PdbCustomDebugInfo ReadCompilationOptions() {
return cdi;
}

PdbCustomDebugInfo ReadTypeDefinitionDocuments() {
var docList = new List<MDToken>();
while (reader.BytesLeft > 0)
docList.Add(new MDToken(Table.Document, reader.ReadCompressedUInt32()));

return new PdbTypeDefinitionDocumentsDebugInfoMD(module, docList);
}

PdbCustomDebugInfo ReadEncStateMachineStateMap() {
var cdi = new PdbEditAndContinueStateMachineStateMapDebugInfo();

var count = reader.ReadCompressedUInt32();
if (count > 0) {
long syntaxOffsetBaseline = -reader.ReadCompressedUInt32();

while (count > 0) {
int stateNumber = reader.ReadCompressedInt32();
int syntaxOffset = (int)(syntaxOffsetBaseline + reader.ReadCompressedUInt32());

cdi.StateMachineStates.Add(new StateMachineStateInfo(syntaxOffset, (StateMachineState)stateNumber));

count--;
}
}

return cdi;
}

Instruction GetInstruction(uint offset) {
var instructions = bodyOpt.Instructions;
int lo = 0, hi = instructions.Count - 1;
Expand Down
Loading

0 comments on commit e43bd76

Please sign in to comment.