2014-08-13 10:39:27 +01:00
|
|
|
//
|
|
|
|
// Author:
|
|
|
|
// Jb Evain (jbevain@gmail.com)
|
|
|
|
//
|
2016-11-10 13:04:39 +00:00
|
|
|
// Copyright (c) 2008 - 2015 Jb Evain
|
|
|
|
// Copyright (c) 2008 - 2011 Novell, Inc.
|
2014-08-13 10:39:27 +01:00
|
|
|
//
|
2016-11-10 13:04:39 +00:00
|
|
|
// Licensed under the MIT/X11 license.
|
2014-08-13 10:39:27 +01:00
|
|
|
//
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.IO;
|
|
|
|
|
|
|
|
using Mono.Cecil.Cil;
|
|
|
|
using Mono.Collections.Generic;
|
|
|
|
using Mono.CompilerServices.SymbolWriter;
|
|
|
|
|
|
|
|
namespace Mono.Cecil.Mdb {
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
public sealed class MdbReaderProvider : ISymbolReaderProvider {
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
public ISymbolReader GetSymbolReader (ModuleDefinition module, string fileName)
|
|
|
|
{
|
2016-11-10 13:04:39 +00:00
|
|
|
Mixin.CheckModule (module);
|
|
|
|
Mixin.CheckFileName (fileName);
|
|
|
|
|
|
|
|
return new MdbReader (module, MonoSymbolFile.ReadSymbolFile (Mixin.GetMdbFileName (fileName), module.Mvid));
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public ISymbolReader GetSymbolReader (ModuleDefinition module, Stream symbolStream)
|
|
|
|
{
|
2016-11-10 13:04:39 +00:00
|
|
|
Mixin.CheckModule (module);
|
|
|
|
Mixin.CheckStream (symbolStream);
|
|
|
|
|
|
|
|
var file = MonoSymbolFile.ReadSymbolFile (symbolStream);
|
|
|
|
if (module.Mvid != file.Guid) {
|
|
|
|
var file_stream = symbolStream as FileStream;
|
|
|
|
if (file_stream != null)
|
|
|
|
throw new MonoSymbolFileException ("Symbol file `{0}' does not match assembly", file_stream.Name);
|
|
|
|
|
|
|
|
throw new MonoSymbolFileException ("Symbol file from stream does not match assembly");
|
|
|
|
}
|
|
|
|
return new MdbReader (module, file);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
public sealed class MdbReader : ISymbolReader {
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
readonly ModuleDefinition module;
|
|
|
|
readonly MonoSymbolFile symbol_file;
|
|
|
|
readonly Dictionary<string, Document> documents;
|
|
|
|
|
|
|
|
public MdbReader (ModuleDefinition module, MonoSymbolFile symFile)
|
|
|
|
{
|
|
|
|
this.module = module;
|
|
|
|
this.symbol_file = symFile;
|
|
|
|
this.documents = new Dictionary<string, Document> ();
|
|
|
|
}
|
|
|
|
|
2017-06-07 13:16:24 +00:00
|
|
|
#if !READ_ONLY
|
|
|
|
public ISymbolWriterProvider GetWriterProvider ()
|
|
|
|
{
|
|
|
|
return new MdbWriterProvider ();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
public bool ProcessDebugHeader (ImageDebugHeader header)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
|
|
|
return symbol_file.Guid == module.Mvid;
|
|
|
|
}
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
public MethodDebugInformation Read (MethodDefinition method)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2016-11-10 13:04:39 +00:00
|
|
|
var method_token = method.MetadataToken;
|
2014-08-13 10:39:27 +01:00
|
|
|
var entry = symbol_file.GetMethodByToken (method_token.ToInt32 ());
|
|
|
|
if (entry == null)
|
2016-11-10 13:04:39 +00:00
|
|
|
return null;
|
|
|
|
|
|
|
|
var info = new MethodDebugInformation (method);
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
var scopes = ReadScopes (entry, info);
|
|
|
|
ReadLineNumbers (entry, info);
|
|
|
|
ReadLocalVariables (entry, scopes);
|
|
|
|
|
|
|
|
return info;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
static void ReadLocalVariables (MethodEntry entry, ScopeDebugInformation [] scopes)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
|
|
|
var locals = entry.GetLocals ();
|
|
|
|
|
|
|
|
foreach (var local in locals) {
|
2016-11-10 13:04:39 +00:00
|
|
|
var variable = new VariableDebugInformation (local.Index, local.Name);
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
var index = local.BlockIndex;
|
|
|
|
if (index < 0 || index >= scopes.Length)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
var scope = scopes [index];
|
|
|
|
if (scope == null)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
scope.Variables.Add (variable);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
void ReadLineNumbers (MethodEntry entry, MethodDebugInformation info)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
|
|
|
var table = entry.GetLineNumberTable ();
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
info.sequence_points = new Collection<SequencePoint> (table.LineNumbers.Length);
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
for (var i = 0; i < table.LineNumbers.Length; i++) {
|
|
|
|
var line = table.LineNumbers [i];
|
|
|
|
if (i > 0 && table.LineNumbers [i - 1].Offset == line.Offset)
|
|
|
|
continue;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
info.sequence_points.Add (LineToSequencePoint (line));
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Document GetDocument (SourceFileEntry file)
|
|
|
|
{
|
|
|
|
var file_name = file.FileName;
|
|
|
|
|
|
|
|
Document document;
|
|
|
|
if (documents.TryGetValue (file_name, out document))
|
|
|
|
return document;
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
document = new Document (file_name) {
|
|
|
|
Hash = file.Checksum,
|
|
|
|
};
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
documents.Add (file_name, document);
|
|
|
|
|
|
|
|
return document;
|
|
|
|
}
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
static ScopeDebugInformation [] ReadScopes (MethodEntry entry, MethodDebugInformation info)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
|
|
|
var blocks = entry.GetCodeBlocks ();
|
2016-11-10 13:04:39 +00:00
|
|
|
var scopes = new ScopeDebugInformation [blocks.Length + 1];
|
|
|
|
|
|
|
|
info.scope = scopes [0] = new ScopeDebugInformation {
|
|
|
|
Start = new InstructionOffset (0),
|
|
|
|
End = new InstructionOffset (info.code_size),
|
|
|
|
};
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
foreach (var block in blocks) {
|
2016-11-10 13:04:39 +00:00
|
|
|
if (block.BlockType != CodeBlockEntry.Type.Lexical && block.BlockType != CodeBlockEntry.Type.CompilerGenerated)
|
2014-08-13 10:39:27 +01:00
|
|
|
continue;
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
var scope = new ScopeDebugInformation ();
|
|
|
|
scope.Start = new InstructionOffset (block.StartOffset);
|
|
|
|
scope.End = new InstructionOffset (block.EndOffset);
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
scopes [block.Index + 1] = scope;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
if (!AddScope (info.scope.Scopes, scope))
|
|
|
|
info.scope.Scopes.Add (scope);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return scopes;
|
|
|
|
}
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
static bool AddScope (Collection<ScopeDebugInformation> scopes, ScopeDebugInformation scope)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2016-11-10 13:04:39 +00:00
|
|
|
foreach (var sub_scope in scopes) {
|
|
|
|
if (sub_scope.HasScopes && AddScope (sub_scope.Scopes, scope))
|
2014-08-13 10:39:27 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
if (scope.Start.Offset >= sub_scope.Start.Offset && scope.End.Offset <= sub_scope.End.Offset) {
|
|
|
|
sub_scope.Scopes.Add (scope);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
SequencePoint LineToSequencePoint (LineNumberEntry line)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2016-11-10 13:04:39 +00:00
|
|
|
var source = symbol_file.GetSourceFile (line.File);
|
|
|
|
return new SequencePoint (line.Offset, GetDocument (source)) {
|
|
|
|
StartLine = line.Row,
|
|
|
|
EndLine = line.EndRow,
|
|
|
|
StartColumn = line.Column,
|
|
|
|
EndColumn = line.EndColumn,
|
|
|
|
};
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
public void Dispose ()
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2016-11-10 13:04:39 +00:00
|
|
|
symbol_file.Dispose ();
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
2016-11-10 13:04:39 +00:00
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
static class MethodEntryExtensions {
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
public static bool HasColumnInfo (this MethodEntry entry)
|
|
|
|
{
|
|
|
|
return (entry.MethodFlags & MethodEntry.Flags.ColumnsInfoIncluded) != 0;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
public static bool HasEndInfo (this MethodEntry entry)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2016-11-10 13:04:39 +00:00
|
|
|
return (entry.MethodFlags & MethodEntry.Flags.EndInfoIncluded) != 0;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|