using System; using System.Collections.Generic; using System.Data; using System.Data.SQLite; using System.Diagnostics.SymbolStore; using System.IO; using System.Linq; using System.Reflection; using Dapper; using DapperExtensions; using DapperExtensions.Mapper; using DapperExtensions.Sql; using Microsoft.Samples.Debugging.CorSymbolStore; using SQLinq; using SQLinq.Dapper; namespace Cosmos.Debug.Common { public class DebugInfo : IDisposable { /// /// Current id of the generation. /// private static long mLastGuid = 0; /// /// Range for the id generation process. /// private static long mPrefix = 0; /// /// Specifies range which is used by the assembler during compilation phase. /// public const long AssemblerDebugSymbolsRange = 0x0L; /// /// Specifies range which is used by the Elf map extraction process. /// public const long ElfFileMapExtractionRange = 0x1000000000000000L; /// /// Specifies range which is used by the Nasm map extraction process. /// public const long NAsmMapExtractionRange = 0x4000000000000000L; // Please beware this field, it may cause issues if used incorrectly. public static DebugInfo CurrentInstance { get; private set; } public class Field_Map { public string TypeName { get; set; } public List FieldNames = new List(); } protected SQLiteConnection mConnection; protected string mDbName; // Dont use DbConnectionStringBuilder class, it doesnt work with LocalDB properly. //protected mDataSouce = @".\SQLEXPRESS"; protected string mConnStr; public void DeleteDB(string aDbName, string aPathname) { File.Delete(aDbName); } public DebugInfo(string aPathname, bool aCreate = false, bool aCreateIndexes = false) { InitializeCache(); CurrentInstance = this; if (aCreate) { File.Delete(aPathname); } aCreate = !File.Exists(aPathname); // Manually register the data provider. Do not remove this otherwise the data provider doesn't register properly. mConnStr = String.Format("data source={0};journal mode=Memory;synchronous=Off;foreign keys=True;BinaryGuid=false", aPathname); // Use the SQLiteConnectionFactory as the default database connection // Do not open mConnection before mEntities.CreateDatabase mConnection = new SQLiteConnection(mConnStr); DapperExtensions.DapperExtensions.DefaultMapper = typeof(PluralizedAutoClassMapper<>); DapperExtensions.DapperExtensions.SqlDialect = new SqliteDialect(); if (aCreate) { // DatabaseExists checks if the DBName exists, not physical files. if (aCreate) { mConnection.Open(); var xSQL = new SQL(mConnection); xSQL.CreateDB(); // Be careful with indexes, they slow down inserts. So on tables that we have a // lot of inserts, but limited look ups, dont add them. // if (aCreateIndexes) { this.CreateIndexes(); } } } if (mConnection.State == ConnectionState.Closed) { mConnection.Open(); } } /// /// Create indexes inside the database. /// public void CreateIndexes() { var xSQL = new SQL(mConnection); xSQL.MakeIndex("Labels", "Address", false); xSQL.MakeIndex("Labels", "Name", true); xSQL.MakeIndex("Methods", "DocumentID", false); xSQL.ExecuteAssemblyResource("SQLiteIndexes.sql"); } // The GUIDs etc are populated by the MSBuild task, so they wont be loaded when the debugger runs. // Because of this, we also allow manual loading. public void LoadLookups() { foreach (var xDoc in mConnection.Query(new SQLinq().ToSQL().ToQuery())) { DocumentGUIDs.Add(xDoc.Pathname.ToLower(), xDoc.ID); } } private void InitializeCache() { mSourceInfosCache = new CacheHelper(a => DoGetSourceInfos(a)); mLabelsCache = new CacheHelper(a => DoGetLabels(a)); mFirstMethodIlOpByLabelNameCache = new CacheHelper(n => mConnection.Query(new SQLinq().Where(q => q.LabelName == n)).FirstOrDefault()); mMethodCache = new CacheHelper(i => mConnection.Get(i)); mAllLocalsAndArgumentsInfosByMethodLabelNameCache = new CacheHelper(a => mConnection.Query(new SQLinq().Where(q => q.METHODLABELNAME == a)).ToArray()); mDocumentIdByNameCache = new CacheHelper(n => { long xId; var xHasResult = DocumentGUIDs.TryGetValue(n, out xId); if (xHasResult) { return xId; } else { return null; } }); mAssemblyFileByIdCache = new CacheHelper(i => mConnection.Get(i)); mAddressOfLabelCache = new CacheHelper(l => DoGetAddressOfLabel(l)); mFieldMapCache = new CacheHelper(t => DoGetFieldMap(t)); mFieldInfoByNameCache = new CacheHelper(n => mConnection.Query(new SQLinq().Where(q => q.NAME == n)).First()); } private CacheHelper mSourceInfosCache; public SourceInfos GetSourceInfos(uint aAddress) { return mSourceInfosCache.GetValue(aAddress); } private CacheHelper mLabelsCache; public string[] GetLabels(uint aAddress) { return mLabelsCache.GetValue(aAddress); } private CacheHelper mFirstMethodIlOpByLabelNameCache; public MethodIlOp TryGetFirstMethodIlOpByLabelName(string aLabelName) { return mFirstMethodIlOpByLabelNameCache.GetValue(aLabelName); } private CacheHelper mMethodCache; public Method GetMethod(long aMethodId) { return mMethodCache.GetValue(aMethodId); } private CacheHelper mAllLocalsAndArgumentsInfosByMethodLabelNameCache; public LOCAL_ARGUMENT_INFO[] GetAllLocalsAndArgumentsInfosByMethodLabelName(string aLabelName) { return mAllLocalsAndArgumentsInfosByMethodLabelNameCache.GetValue(aLabelName); } private CacheHelper mDocumentIdByNameCache; public bool TryGetDocumentIdByName(string aDocumentName, out long oDocumentId) { var xValue = mDocumentIdByNameCache.GetValue(aDocumentName); oDocumentId = xValue.GetValueOrDefault(); return xValue != null; } private CacheHelper mAssemblyFileByIdCache; public AssemblyFile GetAssemblyFileById(long aId) { return mAssemblyFileByIdCache.GetValue(aId); } private CacheHelper mAddressOfLabelCache; public uint GetAddressOfLabel(string aLabelName) { return mAddressOfLabelCache.GetValue(aLabelName); } private CacheHelper mFieldMapCache; public DebugInfo.Field_Map GetFieldMap(string aTypeName) { return mFieldMapCache.GetValue(aTypeName); } private CacheHelper mFieldInfoByNameCache; public FIELD_INFO GetFieldInfoByName(string aName) { return mFieldInfoByNameCache.GetValue(aName); } private UInt32 DoGetAddressOfLabel(string aLabel) { var xRow = mConnection.Query