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 (aPathname != ":memory:")
{
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