public void InvalidAlignment_KindDoesntSupportAlignment() { // CDIs that don't support alignment: var bytes = new byte[] { 0x04, // version 0x01, // count 0x00, 0x00, 0x04, // version 0x01, // kind 0x11, // invalid data 0x14, // invalid data // body size 0x0c, 0x00, 0x00, 0x00, // payload 0x01, 0x00, 0x00, 0x06 }; var records = CustomDebugInfoReader.GetCustomDebugInfoRecords(bytes).ToArray(); Assert.Equal(1, records.Length); Assert.Equal(CustomDebugInfoKind.ForwardInfo, records[0].Kind); Assert.Equal(4, records[0].Version); AssertEx.Equal(new byte[] { 0x01, 0x00, 0x00, 0x06 }, records[0].Data); }
/// <summary> /// Given a byte array of custom debug info, parse the array and write out XML describing /// its structure and contents. /// </summary> private void WriteCustomDebugInfo(byte[] bytes) { var records = CustomDebugInfoReader.GetCustomDebugInfoRecords(bytes).ToArray(); writer.WriteStartElement("customDebugInfo"); foreach (var record in records) { if (record.Version != CDIC.CdiVersion) { WriteUnknownCustomDebugInfo(record); } else { switch (record.Kind) { case CustomDebugInfoKind.UsingInfo: WriteUsingCustomDebugInfo(record); break; case CustomDebugInfoKind.ForwardInfo: WriteForwardCustomDebugInfo(record); break; case CustomDebugInfoKind.ForwardToModuleInfo: WriteForwardToModuleCustomDebugInfo(record); break; case CustomDebugInfoKind.StateMachineHoistedLocalScopes: WriteStatemachineHoistedLocalScopesCustomDebugInfo(record); break; case CustomDebugInfoKind.ForwardIterator: WriteForwardIteratorCustomDebugInfo(record); break; case CustomDebugInfoKind.DynamicLocals: WriteDynamicLocalsCustomDebugInfo(record); break; case CustomDebugInfoKind.EditAndContinueLocalSlotMap: WriteEditAndContinueLocalSlotMap(record); break; default: WriteUnknownCustomDebugInfo(record); break; } } } writer.WriteEndElement(); //customDebugInfo }
public void InvalidAlignment2() { // CDIs that don't support alignment: var bytes = new byte[] { 0x04, // version 0x01, // count 0x00, 0x00, 0x04, // version 0x06, // kind 0x00, 0x03, // bad alignment // body size 0x02, 0x00, 0x00, 0x00, // payload 0x01, 0x00, 0x00, 0x06 }; Assert.Throws <InvalidOperationException>(() => CustomDebugInfoReader.GetCustomDebugInfoRecords(bytes).ToArray()); }
public void EncCdiAlignment() { var slots = ImmutableArray.Create( new LocalSlotDebugInfo(SynthesizedLocalKind.UserDefined, new LocalDebugId(-1, 10)), new LocalSlotDebugInfo(SynthesizedLocalKind.TryAwaitPendingCaughtException, new LocalDebugId(-20000, 10))); var closures = ImmutableArray.Create( new ClosureDebugInfo(-100, new DebugId(0, 0)), new ClosureDebugInfo(10, new DebugId(1, 0)), new ClosureDebugInfo(-200, new DebugId(2, 0))); var lambdas = ImmutableArray.Create( new LambdaDebugInfo(20, new DebugId(0, 0), 1), new LambdaDebugInfo(-50, new DebugId(1, 0), 0), new LambdaDebugInfo(-180, new DebugId(2, 0), LambdaDebugInfo.StaticClosureOrdinal)); var debugInfo = new EditAndContinueMethodDebugInformation(1, slots, closures, lambdas); var records = new ArrayBuilder <Cci.PooledBlobBuilder>(); Cci.CustomDebugInfoWriter.SerializeCustomDebugInformation(debugInfo, records); var cdi = Cci.CustomDebugInfoWriter.SerializeCustomDebugMetadata(records); Assert.Equal(2, records.Count); AssertEx.Equal(new byte[] { 0x04, // version 0x06, // record kind 0x00, 0x02, // alignment size // aligned record size 0x18, 0x00, 0x00, 0x00, // payload (4B aligned) 0xFF, 0xC0, 0x00, 0x4E, 0x20, 0x81, 0xC0, 0x00, 0x4E, 0x1F, 0x0A, 0x9A, 0x00, 0x0A, 0x00, 0x00 }, records[0].ToArray()); AssertEx.Equal(new byte[] { 0x04, // version 0x07, // record kind 0x00, 0x00, // alignment size // aligned record size 0x18, 0x00, 0x00, 0x00, // payload (4B aligned) 0x02, 0x80, 0xC8, 0x03, 0x64, 0x80, 0xD2, 0x00, 0x80, 0xDC, 0x03, 0x80, 0x96, 0x02, 0x14, 0x01 }, records[1].ToArray()); var deserialized = CustomDebugInfoReader.GetCustomDebugInfoRecords(cdi).ToArray(); Assert.Equal(CustomDebugInfoKind.EditAndContinueLocalSlotMap, deserialized[0].Kind); Assert.Equal(4, deserialized[0].Version); Assert.Equal(new byte[] { 0xFF, 0xC0, 0x00, 0x4E, 0x20, 0x81, 0xC0, 0x00, 0x4E, 0x1F, 0x0A, 0x9A, 0x00, 0x0A }, deserialized[0].Data); Assert.Equal(CustomDebugInfoKind.EditAndContinueLambdaMap, deserialized[1].Kind); Assert.Equal(4, deserialized[1].Version); Assert.Equal(new byte[] { 0x02, 0x80, 0xC8, 0x03, 0x64, 0x80, 0xD2, 0x00, 0x80, 0xDC, 0x03, 0x80, 0x96, 0x02, 0x14, 0x01 }, deserialized[1].Data); }
public static void Convert(Stream peStream, Stream sourcePdbStream, Stream targetPdbStream) { var metadataBuilder = new MetadataBuilder(); ImmutableArray <int> typeSystemRowCounts; var debugEntryPointToken = default(MethodDefinitionHandle); var pdbId = default(BlobContentId); try { using (var peReader = new PEReader(peStream)) { pdbId = ReadPdbId(peReader); var symReader = SymReaderFactory.CreateWindowsPdbReader(sourcePdbStream, peReader); var metadataReader = peReader.GetMetadataReader(); var metadataModel = new MetadataModel(metadataReader); typeSystemRowCounts = metadataModel.GetRowCounts(); debugEntryPointToken = ReadEntryPointHandle(symReader); // documents: var documentIndex = new Dictionary <string, DocumentHandle>(StringComparer.Ordinal); var documents = symReader.GetDocuments(); metadataBuilder.SetCapacity(TableIndex.Document, documents.Length); bool vbSemantics = false; foreach (var document in documents) { string name = document.GetName(); Guid language = document.GetLanguage(); // TODO: // won't work for IL-merged assmemblies vbSemantics |= language == SymReaderHelpers.VisualBasicLanguageGuid; var rid = metadataBuilder.AddDocument( name: metadataBuilder.GetOrAddDocumentName(name), hashAlgorithm: metadataBuilder.GetOrAddGuid(document.GetHashAlgorithm()), hash: metadataBuilder.GetOrAddBlob(document.GetChecksum()), language: metadataBuilder.GetOrAddGuid(language)); documentIndex.Add(name, rid); } var lastLocalVariableHandle = default(LocalVariableHandle); var lastLocalConstantHandle = default(LocalConstantHandle); var importStringsByMethod = new Dictionary <int, ImmutableArray <string> >(); var importScopesByMethod = new Dictionary <int, ImportScopeHandle>(); // Maps import scope content to import scope handles var importScopeIndex = new Dictionary <ImportScopeInfo, ImportScopeHandle>(); var importScopes = new List <ImportScopeInfo>(); // reserve slot for module import scope: importScopes.Add(default(ImportScopeInfo)); var externAliasImports = new List <ImportInfo>(); var externAliasStringSet = new HashSet <string>(StringComparer.Ordinal); string vbDefaultNamespace = null; var vbProjectLevelImports = new List <ImportInfo>(); // first pass: foreach (var methodHandle in metadataReader.MethodDefinitions) { int methodToken = MetadataTokens.GetToken(methodHandle); ImmutableArray <ImmutableArray <ImportInfo> > importGroups; if (vbSemantics) { var importStrings = CustomDebugInfoReader.GetVisualBasicImportStrings( methodToken, symReader, getMethodImportStrings: (token, sr) => GetImportStrings(token, importStringsByMethod, sr)); if (importStrings.IsEmpty) { // no debug info continue; } var vbFileLevelImports = ArrayBuilder <ImportInfo> .GetInstance(); foreach (var importString in importStrings) { if (TryParseImportString(importString, out var import, vbSemantics: true)) { if (import.Kind == ImportTargetKind.DefaultNamespace) { vbDefaultNamespace = import.Target; } else if (import.Scope == VBImportScopeKind.Project) { vbProjectLevelImports.Add(import); } else { vbFileLevelImports.Add(import); } } } importGroups = ImmutableArray.Create(vbFileLevelImports.ToImmutableAndFree()); } else { var importStringGroups = CustomDebugInfoReader.GetCSharpGroupedImportStrings( methodToken, symReader, getMethodCustomDebugInfo: (token, sr) => sr.GetCustomDebugInfo(token, methodVersion: 1), getMethodImportStrings: (token, sr) => GetImportStrings(token, importStringsByMethod, sr), externAliasStrings: out var localExternAliasStrings); if (importStringGroups.IsDefault) { // no debug info continue; } if (!localExternAliasStrings.IsDefault) { foreach (var externAlias in localExternAliasStrings) { if (externAliasStringSet.Add(externAlias) && TryParseImportString(externAlias, out var import, vbSemantics: false)) { externAliasImports.Add(import); } } } importGroups = ImmutableArray.CreateRange(importStringGroups.Select(g => ParseImportStrings(g, vbSemantics: false))); } var importScopeHandle = DefineImportScope(importGroups, importScopeIndex, importScopes); importScopesByMethod.Add(methodToken, importScopeHandle); } // import scopes: metadataBuilder.AddImportScope( parentScope: default(ImportScopeHandle), imports: SerializeModuleImportScope(metadataBuilder, externAliasImports, vbProjectLevelImports, vbDefaultNamespace, metadataModel)); for (int i = 1; i < importScopes.Count; i++) { metadataBuilder.AddImportScope( parentScope: importScopes[i].Parent, imports: SerializeImportsBlob(metadataBuilder, importScopes[i].Imports, metadataModel)); } var dynamicNames = new Dictionary <string, DynamicLocalInfo>(); var dynamicSlots = new Dictionary <int, DynamicLocalInfo>(); // methods: metadataBuilder.SetCapacity(TableIndex.MethodDebugInformation, metadataReader.MethodDefinitions.Count); foreach (var methodHandle in metadataReader.MethodDefinitions) { var methodDef = metadataReader.GetMethodDefinition(methodHandle); int methodToken = MetadataTokens.GetToken(methodHandle); var symMethod = symReader.GetMethod(methodToken); if (symMethod == null) { metadataBuilder.AddMethodDebugInformation(default(DocumentHandle), sequencePoints: default(BlobHandle)); continue; } // method debug info: int localSignatureRowId; if (methodDef.RelativeVirtualAddress != 0) { var methodBody = peReader.GetMethodBody(methodDef.RelativeVirtualAddress); localSignatureRowId = methodBody.LocalSignature.IsNil ? 0 : MetadataTokens.GetRowNumber(methodBody.LocalSignature); } else { localSignatureRowId = 0; } var symSequencePoints = symMethod.GetSequencePoints().ToImmutableArray(); DocumentHandle singleDocumentHandle; BlobHandle sequencePointsBlob = SerializeSequencePoints(metadataBuilder, localSignatureRowId, symSequencePoints, documentIndex, out singleDocumentHandle); metadataBuilder.AddMethodDebugInformation( document: singleDocumentHandle, sequencePoints: sequencePointsBlob); // state machine and async info: var symAsyncMethod = symMethod.AsAsyncMethod(); if (symAsyncMethod != null) { var kickoffToken = MetadataTokens.Handle(symAsyncMethod.GetKickoffMethod()); metadataBuilder.AddStateMachineMethod( moveNextMethod: methodHandle, kickoffMethod: (MethodDefinitionHandle)kickoffToken); metadataBuilder.AddCustomDebugInformation( parent: methodHandle, kind: metadataBuilder.GetOrAddGuid(PortableCustomDebugInfoKinds.AsyncMethodSteppingInformationBlob), value: SerializeAsyncMethodSteppingInfo(metadataBuilder, symAsyncMethod, MetadataTokens.GetRowNumber(methodHandle))); } // custom debug information: var dynamicLocals = default(ImmutableArray <DynamicLocalInfo>); byte[] customDebugInfoBytes = symReader.GetCustomDebugInfo(methodToken, methodVersion: 1); if (customDebugInfoBytes != null) { foreach (var record in CustomDebugInfoReader.GetCustomDebugInfoRecords(customDebugInfoBytes)) { switch (record.Kind) { case CustomDebugInfoKind.DynamicLocals: dynamicLocals = CustomDebugInfoReader.DecodeDynamicLocalsRecord(record.Data); break; case CustomDebugInfoKind.StateMachineHoistedLocalScopes: metadataBuilder.AddCustomDebugInformation( parent: methodHandle, kind: metadataBuilder.GetOrAddGuid(PortableCustomDebugInfoKinds.EncLocalSlotMap), value: SerializeStateMachineHoistedLocalsBlob(metadataBuilder, CustomDebugInfoReader.DecodeStateMachineHoistedLocalScopesRecord(record.Data))); break; case CustomDebugInfoKind.EditAndContinueLocalSlotMap: metadataBuilder.AddCustomDebugInformation( parent: methodHandle, kind: metadataBuilder.GetOrAddGuid(PortableCustomDebugInfoKinds.EncLocalSlotMap), value: metadataBuilder.GetOrAddBlob(record.Data)); break; case CustomDebugInfoKind.EditAndContinueLambdaMap: metadataBuilder.AddCustomDebugInformation( parent: methodHandle, kind: metadataBuilder.GetOrAddGuid(PortableCustomDebugInfoKinds.EncLambdaAndClosureMap), value: metadataBuilder.GetOrAddBlob(record.Data)); break; } } } var rootScope = symMethod.GetRootScope(); if (rootScope.GetNamespaces().Length == 0 || rootScope.GetLocals().Length == 0 || rootScope.GetConstants().Length == 0) { dynamicNames.Clear(); dynamicSlots.Clear(); foreach (var dynamicLocal in dynamicLocals) { if (dynamicLocal.SlotId == 0) { // All dynamic constants have slot id == 0, // but a variable can also have slot id == 0 if (!dynamicNames.ContainsKey(dynamicLocal.LocalName)) { dynamicNames.Add(dynamicLocal.LocalName, dynamicLocal); } else { // TODO: warning } } else if (!dynamicSlots.ContainsKey(dynamicLocal.SlotId)) { dynamicSlots.Add(dynamicLocal.SlotId, dynamicLocal); } else { // TODO: warning } } foreach (ISymUnmanagedScope scope in rootScope.GetChildren()) { SerializeScope( metadataBuilder, metadataModel, methodHandle, importScopesByMethod[methodToken], scope, dynamicSlots, dynamicNames, vbSemantics, ref lastLocalVariableHandle, ref lastLocalConstantHandle); } } else { // TODO: warning: // "Root scope must be empty (method 0x{0:x8})", MetadataTokens.GetToken(methodHandle)) } } } } catch (COMException e) { // TODO: loc throw new BadImageFormatException("Invalid PDB format: " + e.Message, e); } var serializer = new PortablePdbBuilder(metadataBuilder, typeSystemRowCounts, debugEntryPointToken, idProvider: _ => pdbId); BlobBuilder blobBuilder = new BlobBuilder(); serializer.Serialize(blobBuilder); blobBuilder.WriteContentTo(targetPdbStream); }