in src/TraceEvent/Symbols/NativeSymbolModule.cs [316:1559]
public override SourceLocation SourceLocationForManagedCode(uint methodMetadataToken, int ilOffset)
{
ThrowIfDisposed();
m_reader.m_log.WriteLine("SourceLocationForManaged: Looking up method token {0:x} ilOffset {1:x}", methodMetadataToken, ilOffset);
IDiaSymbol methodSym;
m_session.findSymbolByToken(methodMetadataToken, SymTagEnum.SymTagFunction, out methodSym);
if (methodSym == null)
{
m_reader.m_log.WriteLine("SourceLocationForManaged: No symbol for token {0:x} ilOffset {1:x}", methodMetadataToken, ilOffset);
return null;
}
uint fetchCount;
IDiaEnumLineNumbers sourceLocs;
IDiaLineNumber sourceLoc;
// TODO FIX NOW, this code here is for debugging only turn if off when we are happy.
//m_session.findLinesByRVA(methodSym.relativeVirtualAddress, (uint)(ilOffset + 256), out sourceLocs);
//for (int i = 0; ; i++)
//{
// sourceLocs.Next(1, out sourceLoc, out fetchCount);
// if (fetchCount == 0)
// break;
// if (i == 0)
// m_reader.m_log.WriteLine("SourceLocationForManaged source file: {0}", sourceLoc.sourceFile.fileName);
// m_reader.m_log.WriteLine("SourceLocationForManaged ILOffset {0:x} -> line {1}",
// sourceLoc.relativeVirtualAddress - methodSym.relativeVirtualAddress, sourceLoc.lineNumber);
//} // End TODO FIX NOW debugging code
// For managed code, the 'RVA' is a 'cumulative IL offset' (amount of IL bytes before this in the module)
// Thus you find the line number of a particular IL offset by adding the offset within the method to
// the cumulative IL offset of the start of the method.
m_session.findLinesByRVA(methodSym.relativeVirtualAddress + (uint)ilOffset, 256, out sourceLocs);
sourceLocs.Next(1, out sourceLoc, out fetchCount);
if (fetchCount == 0)
{
m_reader.m_log.WriteLine("SourceLocationForManaged: No lines for token {0:x} ilOffset {1:x}", methodMetadataToken, ilOffset);
return null;
}
var sourceFile = new MicrosoftPdbSourceFile(this, sourceLoc.sourceFile);
IDiaLineNumber lineNum = null;
// FEEFEE is some sort of illegal line number that is returned some time, It is better to ignore it.
// and take the next valid line
for (; ; )
{
lineNum = sourceLoc;
if (sourceLoc.lineNumber != 0xFEEFEE)
{
break;
}
lineNum = null;
sourceLocs.Next(1, out sourceLoc, out fetchCount);
if (fetchCount == 0)
{
break;
}
}
int lineBegin = lineNum != null ? (int)lineNum.lineNumber : 0;
int lineEnd = lineNum != null ? (int)lineNum.lineNumberEnd : 0;
int columnBegin = lineNum != null ? (int)lineNum.columnNumber : 0;
int columnEnd = lineNum != null ? (int)lineNum.columnNumberEnd : 0;
var sourceLocation = new SourceLocation(sourceFile, lineBegin, lineEnd, columnBegin, columnEnd);
m_reader.m_log.WriteLine("SourceLocationForManaged: found source linenum ({0},{1}):({2},{3}) file {4}", sourceLocation.LineNumber, sourceLocation.ColumnNumber, sourceLocation.LineNumberEnd, sourceLocation.ColumnNumberEnd, sourceFile.BuildTimeFilePath);
return sourceLocation;
}
/// <summary>
/// The symbol representing the module as a whole. All global symbols are children of this symbol
/// </summary>
public Symbol GlobalSymbol
{
get
{
ThrowIfDisposed();
return new Symbol(this, m_session.globalScope);
}
}
#if TEST_FIRST
/// <summary>
/// Returns a list of all source files referenced in the PDB
/// </summary>
public IEnumerable<SourceFile> AllSourceFiles()
{
ThrowIfDisposed();
IDiaEnumTables tables;
m_session.getEnumTables(out tables);
IDiaEnumSourceFiles sourceFiles;
IDiaTable table = null;
uint fetchCount = 0;
for (; ; )
{
tables.Next(1, ref table, ref fetchCount);
if (fetchCount == 0)
return null;
sourceFiles = table as IDiaEnumSourceFiles;
if (sourceFiles != null)
break;
}
var ret = new List<SourceFile>();
IDiaSourceFile sourceFile = null;
for (; ; )
{
sourceFiles.Next(1, out sourceFile, out fetchCount);
if (fetchCount == 0)
break;
ret.Add(new SourceFile(this, sourceFile));
}
return ret;
}
#endif
/// <summary>
/// The a unique identifier that is used to relate the DLL and its PDB.
/// </summary>
public override Guid PdbGuid
{
get
{
ThrowIfDisposed();
return m_session.globalScope.guid;
}
}
/// <summary>
/// Along with the PdbGuid, there is a small integer
/// call the age is also used to find the PDB (it represents the different
/// post link transformations the DLL has undergone).
/// </summary>
public override int PdbAge
{
get
{
ThrowIfDisposed();
return (int)m_session.globalScope.age;
}
}
#region private
/// <summary>
/// A source file represents a source file from a PDB. This is not just a string
/// because the file has a build time path, a checksum, and it needs to be 'smart'
/// to copy down the file if requested.
///
/// TODO We don't need this subclass. We can have SourceFile simply a container
/// that holds the BuildTimePath, hashType and hashValue. The lookup of the
/// source can then be put on NativeSymbolModule and called from SourceFile generically.
/// This makes the different symbol files more similar and is a nice simplification.
/// </summary>
public class MicrosoftPdbSourceFile : SourceFile
{
private const string OldSourceServerUrl = "http://vstfdevdiv.redmond.corp.microsoft.com:8080";
private const string NewSourceServerUrl = "https://vstfdevdiv";
/// <inheritdoc/>
public override bool GetSourceLinkInfo(out string url, out string relativePath)
{
// See if it is in sourceLink information.
if (base.GetSourceLinkInfo(out url, out relativePath))
{
return true;
}
else
{
// Try to convert srcsrv information
GetSourceServerTargetAndCommand(out string target, out _);
if (!string.IsNullOrEmpty(target) && Uri.IsWellFormedUriString(target, UriKind.Absolute))
{
url = target;
relativePath = Path.GetFileName(this.BuildTimeFilePath);
return true;
}
return false;
}
}
/// <summary>
/// Try to fetch the source file associated with 'buildTimeFilePath' from the symbol server
/// information from the PDB from 'pdbPath'. Will return a path to the returned file (uses
/// SourceCacheDirectory associated symbol reader for context where to put the file),
/// or null if unsuccessful.
///
/// There is a tool called pdbstr associated with srcsrv that basically does this.
/// pdbstr -r -s:srcsrv -p:PDBPATH
/// will dump it.
///
/// The basic flow is
///
/// There is a variables section and a files section
///
/// The file section is a list of items separated by *. The first is the path, the rest are up to you
///
/// You form a command by using the SRCSRVTRG variable and substituting variables %var1 where var1 is the first item in the * separated list
/// There are special operators %fnfile%(XXX), etc that manipulate the string XXX (get file name, translate \ to / ...
///
/// If what is at the end is a valid URL it is looked up.
/// </summary>
protected override string GetSourceFromSrcServer()
{
// Try getting the source from the source server using SourceLink information.
var ret = base.GetSourceFromSrcServer();
if (ret != null)
{
return ret;
}
var cacheDir = _symbolModule.SymbolReader.SourceCacheDirectory;
string target, fetchCmdStr;
GetSourceServerTargetAndCommand(out target, out fetchCmdStr, cacheDir);
if (target != null)
{
if (!target.StartsWith(cacheDir, StringComparison.OrdinalIgnoreCase))
{
// if target is not in cache dir, it means it's from a remote server.
Uri uri = null;
if (Uri.TryCreate(target, UriKind.Absolute, out uri))
{
target = null;
var newTarget = Path.Combine(cacheDir, uri.AbsolutePath.TrimStart('/').Replace('/', '\\'));
if (_symbolModule.SymbolReader.GetPhysicalFileFromServer(uri.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped), uri.AbsolutePath, newTarget))
{
target = newTarget;
}
if (target == null)
{
_log.WriteLine("Could not fetch {0} from web", uri.AbsoluteUri);
return null;
}
}
else
{
_log.WriteLine("Source Server string {0} is targeting an unsafe location. Giving up.", target);
return null;
}
}
if (!File.Exists(target) && fetchCmdStr != null)
{
_log.WriteLine("Trying to generate the file {0}.", target);
var toolsDir = Path.GetDirectoryName(typeof(SourceFile).GetTypeInfo().Assembly.ManifestModule.FullyQualifiedName);
var archToolsDir = Path.Combine(toolsDir, NativeDlls.ProcessArchitectureDirectory);
// Find the EXE to do the source server fetch. We only support SD.exe and TF.exe.
string addToPath = null;
if (fetchCmdStr.StartsWith("sd.exe ", StringComparison.OrdinalIgnoreCase))
{
if (!File.Exists(Path.Combine(archToolsDir, "sd.exe")))
{
_log.WriteLine("WARNING: Could not find sd.exe that should have been deployed at {0}", archToolsDir);
}
addToPath = archToolsDir;
}
else
if (fetchCmdStr.StartsWith("tf.exe ", StringComparison.OrdinalIgnoreCase))
{
var tfExe = Command.FindOnPath("tf.exe");
if (tfExe == null)
{
tfExe = FindTfExe();
if (tfExe == null)
{
_log.WriteLine("Could not find TF.exe, place it on the PATH environment variable to fix this.");
return null;
}
addToPath = Path.GetDirectoryName(tfExe);
}
}
else
{
_log.WriteLine("Source Server command {0} is not recognized as safe (sd.exe or tf.exe), failing.", fetchCmdStr);
return null;
}
// Update the source server URL if necessary.
fetchCmdStr = fetchCmdStr.Replace(OldSourceServerUrl, NewSourceServerUrl);
Directory.CreateDirectory(Path.GetDirectoryName(target));
fetchCmdStr = "cmd /c " + fetchCmdStr;
var options = new CommandOptions().AddOutputStream(_log).AddNoThrow();
if (addToPath != null)
{
options = options.AddEnvironmentVariable("PATH", addToPath + ";%PATH%");
}
_log.WriteLine("Source Server command {0}", fetchCmdStr);
var fetchCmd = Command.Run(fetchCmdStr, options);
if (fetchCmd.ExitCode != 0)
{
_log.WriteLine("Source Server command failed with exit code {0}", fetchCmd.ExitCode);
}
if (File.Exists(target))
{
// If TF.exe command files it might still create an empty output file. Fix that
if (new FileInfo(target).Length == 0)
{
File.Delete(target);
target = null;
}
}
else
{
target = null;
}
if (target == null)
{
_log.WriteLine("Source Server command failed to produce the output file.");
}
else
{
_log.WriteLine("Source Server command succeeded creating {0}", target);
}
}
else
{
_log.WriteLine("Found an existing source server file {0}.", target);
}
return target;
}
_log.WriteLine("Did not find source file in the set of source files in the PDB.");
return null;
}
#region private
internal unsafe MicrosoftPdbSourceFile(NativeSymbolModule module, IDiaSourceFile sourceFile) : base(module)
{
BuildTimeFilePath = sourceFile.fileName;
if (sourceFile.checksumType == 0)
{
// If the checksum type is zero, this means either this is a non-C++ PDB, or there is no checksum info
TryInitializeManagedChecksum(module);
}
else
{
// Otherwise this is a C++ style PDB
TryInitializeCppChecksum(sourceFile);
}
}
private void TryInitializeCppChecksum(IDiaSourceFile sourceFile)
{
// 1 CALG_MD5 checksum generated with the MD5 hashing algorithm.
// 2 CALG_SHA1 checksum generated with the SHA1 hashing algorithm.
// 3 checksum generated with the SHA256 hashing algorithm.
if (sourceFile.checksumType == 1)
{
_hashAlgorithm = System.Security.Cryptography.MD5.Create();
}
else if (sourceFile.checksumType == 2)
{
_hashAlgorithm = System.Security.Cryptography.SHA1.Create();
}
else if (sourceFile.checksumType == 3)
{
_hashAlgorithm = System.Security.Cryptography.SHA256.Create();
}
if (_hashAlgorithm != null)
{
uint hashSizeInBytes;
byte* dummy = null;
sourceFile.get_checksum(0, out hashSizeInBytes, out *dummy);
// MD5 is 16 bytes
// SHA1 is 20 bytes
// SHA-256 is 32 bytes
_hash = new byte[hashSizeInBytes];
uint bytesFetched;
fixed (byte* bufferPtr = _hash)
{
sourceFile.get_checksum((uint)_hash.Length, out bytesFetched, out *bufferPtr);
}
Debug.Assert(bytesFetched == _hash.Length);
}
}
private void TryInitializeManagedChecksum(NativeSymbolModule module)
{
try
{
module.m_session.findInjectedSource(this.BuildTimeFilePath, out IDiaEnumInjectedSources injectedSources);
if (injectedSources == null)
{
return;
}
injectedSources.Next(1, out IDiaInjectedSource injectedSource, out uint count);
if (count != 1)
{
return;
}
SrcFormat srcFormat = new SrcFormat();
int srcFormatSize = Marshal.SizeOf(typeof(SrcFormat));
int srcFormatHeaderSize = Marshal.SizeOf(typeof(SrcFormatHeader));
byte* pSrcFormat = (byte*)&srcFormat;
injectedSource.get_source((uint)srcFormatSize, out uint sizeAvailable, out *pSrcFormat);
if (sizeAvailable < srcFormatHeaderSize || sizeAvailable < srcFormat.Header.checkSumSize + srcFormatHeaderSize || srcFormatSize < srcFormat.Header.checkSumSize + srcFormatHeaderSize)
{
return;
}
if (srcFormat.Header.algorithmId == guidMD5)
{
_hashAlgorithm = System.Security.Cryptography.MD5.Create();
}
else if (srcFormat.Header.algorithmId == guidSHA1)
{
_hashAlgorithm = System.Security.Cryptography.SHA1.Create();
}
else if (srcFormat.Header.algorithmId == guidSHA256)
{
_hashAlgorithm = System.Security.Cryptography.SHA256.Create();
}
if (_hashAlgorithm != null)
{
_hash = new byte[srcFormat.Header.checkSumSize];
Marshal.Copy((IntPtr)srcFormat.checksumBytes, _hash, startIndex: 0, length: _hash.Length);
}
}
catch (COMException)
{
// DIA API failed. Ignore.
}
}
/// <summary>
/// Parse the 'srcsrv' stream in a PDB file and return the target for SourceFile
/// represented by the 'this' pointer. This target is iether a ULR or a local file
/// path.
///
/// You can dump the srcsrv stream using a tool called pdbstr
/// pdbstr -r -s:srcsrv -p:PDBPATH
///
/// The target in this stream is called SRCSRVTRG and there is another variable SRCSRVCMD
/// which represents the command to run to fetch the soruce into SRCSRVTRG
///
/// To form the target, the stream expect you to private a %targ% variable which is a directory
/// prefix to tell where to put the source file being fetched. If the source file is
/// available via a URL this variable is not needed.
///
/// ********* This is a typical example of what is in a PDB with source server information.
/// SRCSRV: ini ------------------------------------------------
/// VERSION=3
/// INDEXVERSION=2
/// VERCTRL=Team Foundation Server
/// DATETIME=Thu Mar 10 16:15:55 2016
/// SRCSRV: variables ------------------------------------------
/// TFS_EXTRACT_CMD=tf.exe view /version:%var4% /noprompt "$%var3%" /server:%fnvar%(%var2%) /output:%srcsrvtrg%
/// TFS_EXTRACT_TARGET=%targ%\%var2%%fnbksl%(%var3%)\%var4%\%fnfile%(%var1%)
/// VSTFDEVDIV_DEVDIV2=http://vstfdevdiv.redmond.corp.microsoft.com:8080/DevDiv2
/// SRCSRVVERCTRL=tfs
/// SRCSRVERRDESC=access
/// SRCSRVERRVAR=var2
/// SRCSRVTRG=%TFS_extract_target%
/// SRCSRVCMD=%TFS_extract_cmd%
/// SRCSRV: source files --------------------------------- ------
/// f:\dd\externalapis\legacy\vctools\vc12\inc\cvconst.h*VSTFDEVDIV_DEVDIV2*/DevDiv/Fx/Rel/NetFxRel3Stage/externalapis/legacy/vctools/vc12/inc/cvconst.h*1363200
/// f:\dd\externalapis\legacy\vctools\vc12\inc\cvinfo.h*VSTFDEVDIV_DEVDIV2*/DevDiv/Fx/Rel/NetFxRel3Stage/externalapis/legacy/vctools/vc12/inc/cvinfo.h*1363200
/// f:\dd\externalapis\legacy\vctools\vc12\inc\vc\ammintrin.h*VSTFDEVDIV_DEVDIV2*/DevDiv/Fx/Rel/NetFxRel3Stage/externalapis/legacy/vctools/vc12/inc/vc/ammintrin.h*1363200
/// SRCSRV: end ------------------------------------------------
///
/// ********* And here is a more modern one where the source code is available via a URL.
/// SRCSRV: ini ------------------------------------------------
/// VERSION=2
/// INDEXVERSION=2
/// VERCTRL=http
/// SRCSRV: variables ------------------------------------------
/// SRCSRVTRG=https://nuget.smbsrc.net/src/%fnfile%(%var1%)/%var2%/%fnfile%(%var1%)
/// SRCSRVCMD=
/// SRCSRVVERCTRL=http
/// SRCSRV: source files ---------------------------------------
/// c:\Users\rafalkrynski\Documents\Visual Studio 2012\Projects\DavidSymbolSourceTest\DavidSymbolSourceTest\Demo.cs*SQPvxWBMtvANyCp8Pd3OjoZEUgpKvjDVIY1WbaiFPMw=
/// SRCSRV: end ------------------------------------------------
///
/// </summary>
/// <param name="target">returns the target source file path</param>
/// <param name="command">returns the command to fetch the target source file</param>
/// <param name="localDirectoryToPlaceSourceFiles">Specify the value for %targ% variable. This is the
/// directory where source files can be fetched to. Typically the returned file is under this directory
/// If the value is null, %targ% variable be emtpy. This assumes that the resulting file is something
/// that does not need to be copied to the machine (either a URL or a file that already exists)</param>
private void GetSourceServerTargetAndCommand(out string target, out string command, string localDirectoryToPlaceSourceFiles = null)
{
target = null;
command = null;
_log.WriteLine("*** Looking up {0} using source server", BuildTimeFilePath);
NativeSymbolModule srcServerPdb = (_symbolModule as NativeSymbolModule).PdbForSourceServer as NativeSymbolModule;
if (srcServerPdb == null)
{
_log.WriteLine("*** Could not find PDB to look up source server information");
return;
}
string srcsvcStream = srcServerPdb.GetSrcSrvStream();
if (srcsvcStream == null)
{
_log.WriteLine("*** Could not find srcsrv stream in PDB file");
return;
}
_log.WriteLine("*** Found srcsrv stream in PDB file. of size {0}", srcsvcStream.Length);
StringReader reader = new StringReader(srcsvcStream);
bool inSrc = false;
bool inVars = false;
var vars = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
if (localDirectoryToPlaceSourceFiles != null)
{
vars.Add("targ", localDirectoryToPlaceSourceFiles);
}
for (; ; )
{
var line = reader.ReadLine();
if (line == null)
{
break;
}
// log.WriteLine("Got srcsrv line {0}", line);
if (line.StartsWith("SRCSRV: "))
{
inSrc = line.StartsWith("SRCSRV: source files");
inVars = line.StartsWith("SRCSRV: variables");
continue;
}
if (inSrc)
{
var pieces = line.Split('*');
if (pieces.Length >= 2)
{
var buildTimePath = pieces[0];
// log.WriteLine("Found source {0} in the PDB", buildTimePath);
if (string.Compare(BuildTimeFilePath, buildTimePath, StringComparison.OrdinalIgnoreCase) == 0)
{
// Create variables for each of the pieces.
for (int i = 0; i < pieces.Length; i++)
{
vars.Add("var" + (i + 1).ToString(), pieces[i]);
}
target = SourceServerFetchVar("SRCSRVTRG", vars);
command = SourceServerFetchVar("SRCSRVCMD", vars);
return;
}
}
}
else if (inVars)
{
// Gather up the KEY=VALUE pairs into a dictionary.
var m = Regex.Match(line, @"^(\w+)=(.*?)\s*$");
if (m.Success)
{
vars[m.Groups[1].Value] = m.Groups[2].Value;
}
}
}
}
/// <summary>
/// Returns the location of the tf.exe executable or
/// </summary>
/// <returns></returns>
private static string FindTfExe()
{
// If you have VS installed used that TF.exe associated with that.
var progFiles = Environment.GetEnvironmentVariable("ProgramFiles (x86)");
if (progFiles == null)
{
progFiles = Environment.GetEnvironmentVariable("ProgramFiles");
}
if (progFiles != null)
{
// Find the oldest Visual Studio directory;
var dirs = Directory.GetDirectories(progFiles, "Microsoft Visual Studio*");
Array.Sort(dirs);
if (dirs.Length > 0)
{
var VSDir = Path.Combine(dirs[dirs.Length - 1], @"Common7\IDE");
var tfexe = Path.Combine(VSDir, "tf.exe");
if (File.Exists(tfexe))
{
return tfexe;
}
}
}
return null;
}
private string SourceServerFetchVar(string variable, Dictionary<string, string> vars)
{
string result = "";
if (vars.TryGetValue(variable, out result))
{
if (0 <= result.IndexOf('%'))
{
_log.WriteLine("SourceServerFetchVar: Before Evaluation {0} = '{1}'", variable, result);
}
result = SourceServerEvaluate(result, vars);
}
_log.WriteLine("SourceServerFetchVar: {0} = '{1}'", variable, result);
return result;
}
private string SourceServerEvaluate(string result, Dictionary<string, string> vars)
{
if (0 <= result.IndexOf('%'))
{
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms680641(v=vs.85).aspx for details on the %fn* variables
result = Regex.Replace(result, @"%fnvar%\((.*?)\)", delegate (Match m)
{
return SourceServerFetchVar(SourceServerEvaluate(m.Groups[1].Value, vars), vars);
});
result = Regex.Replace(result, @"%fnbksl%\((.*?)\)", delegate (Match m)
{
return SourceServerEvaluate(m.Groups[1].Value, vars).Replace('/', '\\');
});
result = Regex.Replace(result, @"%fnfile%\((.*?)\)", delegate (Match m)
{
return Path.GetFileName(SourceServerEvaluate(m.Groups[1].Value, vars));
});
// Normal variable substitution
result = Regex.Replace(result, @"%(\w+)%", delegate (Match m)
{
return SourceServerFetchVar(m.Groups[1].Value, vars);
});
}
return result;
}
// Here is an example of the srcsrv stream.
#if false
SRCSRV: ini ------------------------------------------------
VERSION=3
INDEXVERSION=2
VERCTRL=Team Foundation Server
DATETIME=Wed Nov 28 03:47:14 2012
SRCSRV: variables ------------------------------------------
TFS_EXTRACT_CMD=tf.exe view /version:%var4% /noprompt "$%var3%" /server:%fnvar%(%var2%) /console >%srcsrvtrg%
TFS_EXTRACT_TARGET=%targ%\%var2%%fnbksl%(%var3%)\%var4%\%fnfile%(%var1%)
SRCSRVVERCTRL=tfs
SRCSRVERRDESC=access
SRCSRVERRVAR=var2
DEVDIV_TFS2=http://vstfdevdiv.redmond.corp.microsoft.com:8080/devdiv2
SRCSRVTRG=%TFS_extract_target%
SRCSRVCMD=%TFS_extract_cmd%
SRCSRV: source files ---------------------------------------
f:\dd\ndp\clr\src\vm\i386\gmsasm.asm*DEVDIV_TFS2*/DevDiv/D11RelS/FX45RTMGDR/ndp/clr/src/VM/i386/gmsasm.asm*592925
f:\dd\ndp\clr\src\vm\i386\jithelp.asm*DEVDIV_TFS2*/DevDiv/D11RelS/FX45RTMGDR/ndp/clr/src/VM/i386/jithelp.asm*592925
f:\dd\ndp\clr\src\vm\i386\RedirectedHandledJITCase.asm*DEVDIV_TFS2*/DevDiv/D11RelS/FX45RTMGDR/ndp/clr/src/VM/i386/RedirectedHandledJITCase.asm*592925
f:\dd\public\devdiv\inc\ddbanned.h*DEVDIV_TFS2*/DevDiv/D11RelS/FX45RTMGDR/public/devdiv/inc/ddbanned.h*592925
f:\dd\ndp\clr\src\debug\ee\i386\dbghelpers.asm*DEVDIV_TFS2*/DevDiv/D11RelS/FX45RTMGDR/ndp/clr/src/Debug/EE/i386/dbghelpers.asm*592925
SRCSRV: end ------------------------------------------------
// Here is one for SD.
SRCSRV: ini ------------------------------------------------
VERSION=1
VERCTRL=Source Depot
SRCSRV: variables ------------------------------------------
SRCSRVTRG=%targ%\%var2%\%fnbksl%(%var3%)\%var4%\%fnfile%(%var1%)
SRCSRVCMD=sd.exe -p %fnvar%(%var2%) print -o %srcsrvtrg% -q %depot%/%var3%#%var4%
DEPOT=//depot
SRCSRVVERCTRL=sd
SRCSRVERRDESC=Connect to server failed
SRCSRVERRVAR=var2
WIN_MINKERNEL=minkerneldepot.sys-ntgroup.ntdev.microsoft.com:2020
WIN_PUBLIC=publicdepot.sys-ntgroup.ntdev.microsoft.com:2017
WIN_PUBLICINT=publicintdepot.sys-ntgroup.ntdev.microsoft.com:2018
SRCSRV: source files ---------------------------------------
d:\win7sp1_gdr.public.amd64fre\sdk\inc\pshpack4.h*WIN_PUBLIC*win7sp1_gdr/public/sdk/inc/pshpack4.h*1
d:\win7sp1_gdr.public.amd64fre\internal\minwin\priv_sdk\inc\ntos\pnp.h*WIN_PUBLICINT*win7sp1_gdr/publicint/minwin/priv_sdk/inc/ntos/pnp.h*1
d:\win7sp1_gdr.public.amd64fre\internal\minwin\priv_sdk\inc\ntos\cm.h*WIN_PUBLICINT*win7sp1_gdr/publicint/minwin/priv_sdk/inc/ntos/cm.h*1
d:\win7sp1_gdr.public.amd64fre\internal\minwin\priv_sdk\inc\ntos\pnp_x.h*WIN_PUBLICINT*win7sp1_gdr/publicint/minwin/priv_sdk/inc/ntos/pnp_x.h*2
SRCSRV: end ------------------------------------------------
#endif
#if false
// Here is ana example of the stream in use for the jithlp.asm file.
f:\dd\ndp\clr\src\vm\i386\jithelp.asm*DEVDIV_TFS2*/DevDiv/D11RelS/FX45RTMGDR/ndp/clr/src/VM/i386/jithelp.asm*592925
// Here is the command that it issues.
tf.exe view /version:592925 /noprompt "$/DevDiv/D11RelS/FX45RTMGDR/ndp/clr/src/VM/i386/jithelp.asm" /server:http://vstfdevdiv.redmond.corp.microsoft.com:8080/devdiv2 /console >"C:\Users\vancem\AppData\Local\Temp\PerfView\src\DEVDIV_TFS2\DevDiv\D11RelS\FX45RTMGDR\ndp\clr\src\VM\i386\jithelp.asm\592925\jithelp.asm"
sd.exe -p minkerneldepot.sys-ntgroup.ntdev.microsoft.com:2020 print -o "C:\Users\vancem\AppData\Local\Temp\PerfView\src\WIN_MINKERNEL\win8_gdr\minkernel\ntdll\rtlstrt.c\1\rtlstrt.c" -q //depot/win8_gdr/minkernel/ntdll/rtlstrt.c#1
#endif
#endregion
}
private NativeSymbolModule(SymbolReader reader, string pdbFilePath, Action<IDiaDataSource3> loadData) : base(reader, pdbFilePath)
{
m_reader = reader;
m_source = DiaLoader.GetDiaSourceObject();
loadData(m_source);
m_source.openSession(out m_session);
m_session.getSymbolsByAddr(out m_symbolsByAddr);
m_heapAllocationSites = new Lazy<IReadOnlyDictionary<uint, string>>(() =>
{
// Retrieves the S_HEAPALLOCSITE information from the pdb as described here:
// https://docs.microsoft.com/visualstudio/profiling/custom-native-etw-heap-events
Dictionary<uint, string> result = null;
m_session.getHeapAllocationSites(out var diaEnumSymbols);
for (; ; )
{
diaEnumSymbols.Next(1, out var sym, out var fetchCount);
if (fetchCount == 0)
{
return (IReadOnlyDictionary<uint, string>)result ?? System.Collections.Immutable.ImmutableDictionary<uint, string>.Empty;
}
result = result ?? new Dictionary<uint, string>();
m_session.symbolById(sym.typeId, out var typeSym);
result[sym.relativeVirtualAddress + (uint)sym.length] = HeapAllocationTypeInfo.GetTypeName(typeSym);
}
});
m_reader.m_log.WriteLine("Opening PDB {0} with signature GUID {1} Age {2}", pdbFilePath, PdbGuid, PdbAge);
}
internal NativeSymbolModule(SymbolReader reader, string pdbFilePath)
: this(reader, pdbFilePath, s => s.loadDataFromPdb(pdbFilePath))
{
}
internal NativeSymbolModule(SymbolReader reader, string pdbFilePath, Stream pdbStream)
: this(reader, pdbFilePath, s => s.loadDataFromIStream(new ComStreamWrapper(pdbStream)))
{
}
internal void LogManagedInfo(string pdbName, Guid pdbGuid, int pdbAge)
{
// Simply remember this if we decide we need it for source server support
m_managedPdbName = pdbName;
m_managedPdbGuid = pdbGuid;
m_managedPdbAge = pdbAge;
}
/// <summary>
/// Gets the 'srcsvc' data stream from the PDB and return it in as a string. Returns null if it is not present.
///
/// There is a tool called pdbstr associated with srcsrv that basically does this.
/// pdbstr -r -s:srcsrv -p:PDBPATH
/// will dump it.
/// </summary>
internal string GetSrcSrvStream()
{
ThrowIfDisposed();
// In order to get the IDiaDataSource3 which includes'getStreamSize' API, you need to use the
// dia2_internal.idl file from devdiv to produce the Interop.Dia2Lib.dll
// see class DiaLoader for more
var log = m_reader.m_log;
log.WriteLine("Getting source server stream for PDB {0}", SymbolFilePath);
uint len = 0;
m_source.getStreamSize("srcsrv", out len);
if (len == 0)
{
if (0 <= SymbolFilePath.IndexOf(".ni.", StringComparison.OrdinalIgnoreCase))
{
log.WriteLine("Error, trying to look up source information on an NGEN file, giving up");
}
else
{
log.WriteLine("Pdb {0} does not have source server information (srcsrv stream) in it", SymbolFilePath);
}
return null;
}
return GetUTF8PDBStream("srcsrv", len);
}
private string GetUTF8PDBStream(string name, uint len)
{
byte[] buffer = new byte[len];
fixed (byte* bufferPtr = buffer)
{
m_source.getStreamRawData(name, len, out *bufferPtr);
var ret = new UTF8Encoding().GetString(buffer);
return ret;
}
}
protected override IEnumerable<string> GetSourceLinkJson()
{
ThrowIfDisposed();
// Source Link is stored in windows pdb in *EITHER* the 'sourcelink' stream *OR* 1 or more 'sourcelink$n' streams where n starts at 1.
// For multi stream format, we read the streams starting at 1 until we receive a stream size of 0.
const string singleStreamName = "sourcelink";
const string multiStreamNameFormat = "sourcelink${0}";
// first check the single stream
m_source.getStreamSize(singleStreamName, out uint streamSize);
if (streamSize > 0)
{
string content = GetUTF8PDBStream(singleStreamName, streamSize);
return new string[] { content };
}
else
{
List<string> result = new List<string>();
// if there was no single stream, check the multi stream
for (int cStream = 1; cStream < int.MaxValue; cStream++)
{
string streamName = string.Format(CultureInfo.InvariantCulture, multiStreamNameFormat, cStream);
m_source.getStreamSize(streamName, out streamSize);
if (streamSize == 0)
{
break;
}
string content = GetUTF8PDBStream(streamName, streamSize);
result.Add(content);
}
return result;
}
}
// returns the path of the PDB that has source server information in it (which for NGEN images is the PDB for the managed image)
internal ManagedSymbolModule PdbForSourceServer
{
get
{
if (m_managedPdbName == null)
{
return this;
}
if (!m_managedPdbAttempted)
{
m_reader.m_log.WriteLine("We have a NGEN image with an IL PDB {0}, looking it up", m_managedPdbName);
m_managedPdbAttempted = true;
var managedPdbPath = m_reader.FindSymbolFilePath(m_managedPdbName, m_managedPdbGuid, m_managedPdbAge);
if (managedPdbPath != null)
{
m_reader.m_log.WriteLine("Found managed PDB path {0}", managedPdbPath);
m_managedPdb = m_reader.OpenSymbolFile(managedPdbPath);
}
else
{
m_reader.m_log.WriteLine("Could not find managed PDB {0}", m_managedPdbName);
}
}
return m_managedPdb;
}
}
/// <summary>
/// For Project N modules it returns the list of pre merged IL assemblies and the corresponding mapping.
/// </summary>
public Dictionary<int, string> GetMergedAssembliesMap()
{
ThrowIfDisposed();
if (m_mergedAssemblies == null && !m_checkedForMergedAssemblies)
{
IDiaEnumInputAssemblyFiles diaMergedAssemblyRecords;
m_session.findInputAssemblyFiles(out diaMergedAssemblyRecords);
for (; ; )
{
IDiaInputAssemblyFile inputAssembly;
uint fetchCount;
diaMergedAssemblyRecords.Next(1, out inputAssembly, out fetchCount);
if (fetchCount != 1)
{
break;
}
int index = (int)inputAssembly.index;
string assemblyName = inputAssembly.fileName;
if (m_mergedAssemblies == null)
{
m_mergedAssemblies = new Dictionary<int, string>();
}
m_mergedAssemblies.Add(index, assemblyName);
}
m_checkedForMergedAssemblies = true;
}
return m_mergedAssemblies;
}
/// <summary>
/// For ProjectN modules, gets the merged IL image embedded in the .PDB (only valid for single-file compilation)
/// </summary>
public MemoryStream GetEmbeddedILImage()
{
ThrowIfDisposed();
try
{
uint ilimageSize;
m_source.getStreamSize("ilimage", out ilimageSize);
if (ilimageSize > 0)
{
byte[] ilImage = new byte[ilimageSize];
m_source.getStreamRawData("ilimage", ilimageSize, out ilImage[0]);
return new MemoryStream(ilImage);
}
}
catch (COMException)
{
}
return null;
}
/// <summary>
/// For ProjectN modules, gets the pseudo-assembly embedded in the .PDB, if there is one.
/// </summary>
/// <returns></returns>
public MemoryStream GetPseudoAssembly()
{
ThrowIfDisposed();
try
{
uint ilimageSize;
m_source.getStreamSize("pseudoil", out ilimageSize);
if (ilimageSize > 0)
{
byte[] ilImage = new byte[ilimageSize];
m_source.getStreamRawData("pseudoil", ilimageSize, out ilImage[0]);
return new MemoryStream(ilImage, writable: false);
}
}
catch (COMException)
{
}
return null;
}
/// <summary>
/// For ProjectN modules, gets the binary blob that describes the mapping from RVAs to methods.
/// </summary>
public byte[] GetFuncMDTokenMap()
{
ThrowIfDisposed();
uint mapSize;
m_session.getFuncMDTokenMapSize(out mapSize);
byte[] buf = new byte[mapSize];
fixed (byte* pBuf = buf)
{
m_session.getFuncMDTokenMap((uint)buf.Length, out mapSize, out buf[0]);
Debug.Assert(mapSize == buf.Length);
}
return buf;
}
/// <summary>
/// For ProjectN modules, gets the binary blob that describes the mapping from RVAs to types.
/// </summary>
/// <returns></returns>
public byte[] GetTypeMDTokenMap()
{
ThrowIfDisposed();
uint mapSize;
m_session.getTypeMDTokenMapSize(out mapSize);
byte[] buf = new byte[mapSize];
fixed (byte* pBuf = buf)
{
m_session.getTypeMDTokenMap((uint)buf.Length, out mapSize, out buf[0]);
Debug.Assert(mapSize == buf.Length);
}
return buf;
}
public void Dispose()
{
if (!m_isDisposed)
{
m_isDisposed = true;
if (m_session is IDiaSession3 diaSession3)
{
int hr = diaSession3.dispose();
Debug.Assert(hr == 0, "IDiaSession3.dispose failed");
}
}
}
/// <summary>
/// This function checks if the SymbolModule is disposed before proceeding with the call.
/// This is important because DIA doesn't provide any guarantees as to what will happen if
/// one attempts to call after the session is disposed, so this at least ensure that we
/// fail cleanly in non-concurrent cases.
/// </summary>
private void ThrowIfDisposed()
{
if (m_isDisposed)
{
throw new ObjectDisposedException(nameof(NativeSymbolModule));
}
}
/// <summary>
/// This static class contains the GetTypeName method for retrieving the type name of
/// a heap allocation site.
///
/// See https://github.com/KirillOsenkov/Dia2Dump/blob/master/PrintSymbol.cpp for more details
/// </summary>
private static class HeapAllocationTypeInfo
{
internal static string GetTypeName(IDiaSymbol symbol)
{
var name = symbol.name ?? "<unknown>";
switch ((SymTagEnum)symbol.symTag)
{
case SymTagEnum.UDT:
case SymTagEnum.Enum:
case SymTagEnum.Typedef:
return name;
case SymTagEnum.FunctionType:
return "function";
case SymTagEnum.PointerType:
return $"{GetTypeName(symbol.type)} {(symbol.reference != 0 ? "&" : "*") }";
case SymTagEnum.ArrayType:
return "array";
case SymTagEnum.BaseType:
var sb = new StringBuilder();
switch ((BasicType)symbol.baseType)
{
case BasicType.btUInt:
sb.Append("unsigned ");
goto case BasicType.btInt;
case BasicType.btInt:
switch (symbol.length)
{
case 1:
sb.Append("char");
break;
case 2:
sb.Append("short");
break;
case 4:
sb.Append("int");
break;
case 8:
sb.Append("long");
break;
}
return sb.ToString();
case BasicType.btFloat:
return symbol.length == 4 ? "float" : "double";
default:
return BaseTypes[symbol.baseType];
}
}
return $"unhandled symbol tag {symbol.symTag}";
}
private enum SymTagEnum
{
Null,
Exe,
Compiland,
CompilandDetails,
CompilandEnv,
Function,
Block,
Data,
Annotation,
Label,
PublicSymbol,
UDT,
Enum,
FunctionType,
PointerType,
ArrayType,
BaseType,
Typedef,
BaseClass,
Friend,
FunctionArgType,
FuncDebugStart,
FuncDebugEnd,
UsingNamespace,
VTableShape,
VTable,
Custom,
Thunk,
CustomType,
ManagedType,
Dimension,
CallSite,
InlineSite,
BaseInterface,
VectorType,
MatrixType,
HLSLType
};
private enum BasicType
{
btNoType = 0,
btVoid = 1,
btChar = 2,
btWChar = 3,
btInt = 6,
btUInt = 7,
btFloat = 8,
btBCD = 9,
btBool = 10,
btLong = 13,
btULong = 14,
btCurrency = 25,
btDate = 26,
btVariant = 27,
btComplex = 28,
btBit = 29,
btBSTR = 30,
btHresult = 31,
btChar16 = 32, // char16_t
btChar32 = 33, // char32_t
};
private static readonly string[] BaseTypes = new[]
{
"<NoType>", // btNoType = 0,
"void", // btVoid = 1,
"char", // btChar = 2,
"wchar_t", // btWChar = 3,
"signed char",
"unsigned char",
"int", // btInt = 6,
"unsigned int", // btUInt = 7,
"float", // btFloat = 8,
"<BCD>", // btBCD = 9,
"bool", // btBool = 10,
"short",
"unsigned short",
"long", // btLong = 13,
"unsigned long", // btULong = 14,
"__int8",
"__int16",
"__int32",
"__int64",
"__int128",
"unsigned __int8",
"unsigned __int16",
"unsigned __int32",
"unsigned __int64",
"unsigned __int128",
"<currency>", // btCurrency = 25,
"<date>", // btDate = 26,
"VARIANT", // btVariant = 27,
"<complex>", // btComplex = 28,
"<bit>", // btBit = 29,
"BSTR", // btBSTR = 30,
"HRESULT" // btHresult = 31
};
}
private bool m_isDisposed;
private bool m_checkedForMergedAssemblies;
private Dictionary<int, string> m_mergedAssemblies;
private string m_managedPdbName;
private Guid m_managedPdbGuid;
private int m_managedPdbAge;
private ManagedSymbolModule m_managedPdb;
private bool m_managedPdbAttempted;
internal readonly IDiaSession m_session;
private readonly SymbolReader m_reader;
private readonly IDiaDataSource3 m_source;
private readonly IDiaEnumSymbolsByAddr m_symbolsByAddr;
private readonly Lazy<IReadOnlyDictionary<uint, string>> m_heapAllocationSites; // rva => typename
[StructLayout(LayoutKind.Sequential)]
struct SrcFormatHeader
{
public Guid language;
public Guid languageVendor;
public Guid documentType;
public Guid algorithmId;
public UInt32 checkSumSize;
public UInt32 sourceSize;
}
[StructLayout(LayoutKind.Sequential)]
struct SrcFormat
{
public SrcFormatHeader Header;
public fixed byte checksumBytes[512/8]; // this size of this may be smaller, it is controlled by the size of the `checksumSize` field
}
private static readonly Guid guidMD5 = new Guid("406ea660-64cf-4c82-b6f0-42d48172a799");
private static readonly Guid guidSHA1 = new Guid("ff1816ec-aa5e-4d10-87f7-6f4963833460");
private static readonly Guid guidSHA256 = new Guid("8829d00f-11b8-4213-878b-770e8597ac16");
#endregion
}