using System; using System.Collections.Generic; using System.Text; using System.Xml.Serialization; // // This file contains type definitions for storing interesting information from a symbol file (corresponding // to the ISymbolReader APIs). // // These types intended to be written/read using XmlSerializer, which restricts us to using public // read/write fields and default constructors. These types are all dumb containers. // // These data structures capture all the information available from diasymreader.dll v2.0.50727 (Whidbey RTM), // except for: // - Differentiation of multiple documents with the same URL // Diasymreader allows multiple documents to be created with the same URL, and this has an affect on semantics // of the various reader APIs. However, there doesn't appear to be any good way to match a document and so // differentiate between two documents that have the same URL (one heuristic may be to see what FindClosestLine(0) // returns for each). // - All Symbol custom attributes: // There are no APIs to enumerate these (you must know the name to look for). Pdb2Xml supports reading only // attribute names that are known to be used (eg. by C#), but it will write any name given. // Several additional APIs (globals, parameters, fields, namespace definitions) are known to not be // implemented, and probably never will be. // namespace Pdb2Xml { // TODO: can I have the Xml serializer auto-convert hex numbers? Eg. XmlElement(datatype="hexBinary") /// /// Represents all the data we want to read out of an ISymbolReader /// public class SymbolData { /// /// Path of the assembly for which these symbols correspond. /// For informational purposes only - not written to the symbol store /// [XmlAttribute] public string assembly; /// /// MethodDef token (in hex) of the user entrypoint method, or null if none (eg. a DLL) /// public string entryPointToken; [XmlArray] [XmlArrayItem(ElementName= "document")] public List sourceFiles; [XmlElement(ElementName = "method")] public List methods; } /// /// Data from an ISymbolDocument /// public class Document { [XmlAttribute] public int id; [XmlAttribute] public string url; [XmlAttribute] public Guid language; [XmlAttribute] public Guid languageVendor; [XmlAttribute] public Guid documentType; } /// /// Data from an ISymbolMethod /// public class Method { /// /// The type and name of this method. /// For informational purposes only - not written to the symbol store /// [XmlAttribute] public string name; /// /// MethodDef token (in hex) of the method /// [XmlAttribute] public string token; /// /// Signature token (in hex) of the local variable signature for this method (or null if no locals) /// This can't be read from the symbol store directly (it's read by reflection), but can be used when writing locals. /// [XmlAttribute] public string localSigMetadataToken; /// /// This is set to true by the reader when it can't read the method body from the method because the CLR /// claims it is invalid. There is at least one known C# compiler bug that can cause this. The /// reader will not be able to provide a localSigMetadataToken in this case, and so the writer may not /// be able to write out the locals. /// [System.ComponentModel.DefaultValue(false)] [XmlAttribute] public bool hasInvalidMethodBody; [XmlArray] [XmlArrayItem(ElementName="seqPoint")] public List sequencePoints; public Scope rootScope; [XmlArray] [XmlArrayItem(ElementName = "attribute")] public List symAttributes; /// /// Optionally, C# custom debug information /// [XmlElement(ElementName="csharpCustomDebugInfo")] public CSharpCDI csharpCDI; } /// /// Data from ISymbolMethod.GetSequencePoints /// public class SequencePoint { [XmlAttribute] public int ilOffset; [XmlAttribute] public int sourceId; /// /// If true, this sequence point corresponds to a hidden (0xfeefee) sequence point. /// This property isn't directly read or written from the symbols, but inferred by the line number value. /// [XmlAttribute] [System.ComponentModel.DefaultValue(false)] public bool hidden; [XmlAttribute] public int startRow; [XmlAttribute] public int startColumn; [XmlAttribute] public int endRow; [XmlAttribute] public int endColumn; } /// /// Data from an ISymbolScope /// public class Scope { /// /// True if this scope is created implicity by the symbol library and so should not explicitly /// written out. /// [XmlAttribute(AttributeName="implicit")] [System.ComponentModel.DefaultValue(false)] public bool isImplicit; [XmlAttribute] public int startOffset; [XmlAttribute] public int endOffset; [XmlElement(ElementName = "local")] public List locals; [XmlElement(ElementName = "constant")] public List constants; [XmlElement(ElementName="scope")] public List scopes; [XmlElement(ElementName = "usingNamespace")] public List usedNamespaces; /// /// True if this scope didn't actually show up while reading a PDB, but we know it must have /// been written there. See SymbolDataReader.WorkAroundDiasymreaderScopeBug for details. /// [XmlAttribute] [System.ComponentModel.DefaultValue(false)] public bool isReconstructedDueToDiasymreaderBug; } /// /// Data from an ISymbolVariable /// public class Variable { [XmlAttribute] public string name; [XmlAttribute] public int ilIndex; [XmlAttribute] public int attributes; [XmlAttribute] public string signature; } /// /// Data from an ISymbolConstant /// public class Constant { [XmlAttribute] public string name; [XmlAttribute] public string value; [XmlAttribute] public string signature; } /// /// Data from an ISymbolNamespace /// Note that most uses of namespaces in the symbol APIs aren't actually implemented. /// Namespaces are just a name (no children or variables) and are used only in scopes to /// say which namespaces are being "used". /// public class Namespace { [XmlAttribute] public string name; } /// /// Data returned by ISymUnmanagedReader::GetSymAttribute /// public class SymAttribute { [XmlAttribute] public string name; [XmlAttribute] public string value; // hex bytes } // // The following are used to represent C# custom debug information // Theese structures are undocumented implementation details of the C# // compiler and debugger interaction. // public class CSharpCDI { [XmlAttribute] public int version; [XmlArrayItem(Type = typeof(CDIUsing), ElementName="using"), XmlArrayItem(Type = typeof(CDIForward), ElementName="usingForward"), XmlArrayItem(Type = typeof(CDIForwardModule), ElementName="usingForwardToModule"), XmlArrayItem(Type = typeof(CDIIteratorLocals), ElementName="iteratorLocals"), XmlArrayItem(Type = typeof(CDIForwardIterator), ElementName="forwardIterator"), XmlArrayItem(Type = typeof(CDIUnknown), ElementName="unknown")] public CDIItem[] entries; } public abstract class CDIItem { [XmlAttribute] public int version; } public class CDIUsing : CDIItem { /// /// This appears to be used as follows: /// There is one entry for each level of declaration nesting for the containing method. /// The value indicates the number of using-namespace declarations (from the PDB scope using /// data) that should be imported, and applied to the indicated nesting level. /// Presumably this impacts what symbols are available when doing expression evaluation in the /// context of a method. /// [XmlElement] public int[] countOfUsing; } public class CDIForward : CDIItem { /// /// This data indicates that CDIUsing information should be imported from the specified /// method. Presumably this is an optimizaton to avoid duplicating all the using namespace /// names in each method. /// [XmlAttribute] public string tokenToForwardTo; } public class CDIForwardModule : CDIItem { /// /// This is similar to tokenToForwardTo, but appears to be treated slightly differently somehow. /// [XmlAttribute] public string tokenOfModuleInfo; } public class CDIIteratorLocalBucket { [XmlAttribute] public int ilOffsetStart; [XmlAttribute] public int ilOffsetEnd; } public class CDIIteratorLocals : CDIItem { /// /// This causes local variables in iterator methods (which are actually implemented /// as fields on the generated class) to be visible in the debugger. /// [XmlElement(ElementName="bucket")] public CDIIteratorLocalBucket[] buckets; } public class CDIForwardIterator : CDIItem { /// /// This indicates that the current method is an iterator method and is implemented /// by the specified generated class. This causes callstacks to display the name of /// this method when they're actually inside a method on the generated iterator class. /// [XmlAttribute] public string iteratorClassName; } public class CDIUnknown : CDIItem { [XmlAttribute] public int kind; [XmlAttribute] public string bytes; } }