in src/PerfView/memory/ClrProfilerParser.cs [226:852]
private void ReadFile(FastStream stream)
{
StringBuilder sb = new StringBuilder();
List<Address> addressList = new List<Address>();
int lineNum = 1;
int gcNumber = 0;
stream.MoveNext();
for (; ; )
{
// Check whether we've been asked to abort this parsing early
if (abort)
{
return;
}
byte c = stream.Current;
stream.MoveNext();
// Because the stats are highly skewed, and if-then-else tree is better than a switch.
if (c == '\r')
{
// Do nothing
}
else if (c == '\n')
{
lineNum++;
if (lineNum % 4096 == 0) // Every 4K events allow Thread.Interrupt.
{
System.Threading.Thread.Sleep(0);
}
}
else if (c == 'c')
{
// Call was made
if (Call == null)
{
goto SKIP_LINE;
}
uint threadId = stream.ReadUInt();
uint fileStackId = stream.ReadUInt();
if (!stream.EndOfStream)
{
ProfilerStackTraceID stackId = GetStackIdForFileId(fileStackId);
Call(stackId, threadId);
}
}
else if (c == '!')
{
// Allocation was made
if (Allocation == null)
{
goto SKIP_LINE;
}
uint threadId = stream.ReadUInt();
Address address = (Address)stream.ReadULong();
uint fileStackId = stream.ReadUInt();
ProfilerAllocID allocId = GetAllocIdForFileId(fileStackId);
if (!stream.EndOfStream)
{
Allocation(allocId, address, threadId);
}
}
else if (c == 'n')
{
// Announce a stack
uint fileStackId = stream.ReadUInt();
uint flag = stream.ReadUInt();
uint hadTypeId = (flag & 2);
uint tailCount = flag >> 2;
uint hasTypeId = (flag & 1);
ProfilerTypeID typeId = 0;
uint size = 0;
if (hasTypeId != 0)
{
typeId = (ProfilerTypeID)stream.ReadUInt();
size = stream.ReadUInt();
}
uint fileTailStackId = 0;
if (tailCount > 0)
{
fileTailStackId = stream.ReadUInt();
}
if (!stream.EndOfStream)
{
// The rest of the line are a series of function Ids (going from closest to top of stack
// to closest to execution.
ProfilerStackTraceID stackId = GetStackIdForFileId(fileTailStackId);
if (tailCount > 0)
{
stackId = GetTopOfStack(stackId, tailCount);
}
for (; ; )
{
ProfilerMethodID methodId = (ProfilerMethodID)stream.ReadUInt();
if ((uint)methodId == 0xFFFFFFFF)
{
break;
}
// TODO decide how to fix this.
// Debug.Assert(methodIds[(int)methodId].name != null);
uint newStackId = stackIdLimit++;
stackIds = ArrayUtilities<StackInfo>.InsureCapacity(stackIds, newStackId);
stackIds[newStackId].methodId = methodId;
stackIds[newStackId].stackId = stackId;
stackId = (ProfilerStackTraceID)newStackId;
if (methodIds[(int)methodId] != null)
{
VerboseDebug("Frame stackId=S" + newStackId + " name=" + methodIds[(int)methodId].name);
}
}
if (hasTypeId != 0)
{
uint allocId = allocIdLimit++;
allocIds = ArrayUtilities<AllocInfo>.InsureCapacity(allocIds, allocId);
allocIds[allocId].Set(typeId, size, stackId);
if (methodIds[(int)stackIds[(int)stackId].methodId] != null) // TODO
{
VerboseDebug("FileAlloc s" + fileStackId + " maps to allocId=" + allocId +
" type=" + typeIds[(int)typeId].name + " size=" + size + " stackId=" + stackId +
((stackId == 0) ? "" : " (" + methodIds[(int)stackIds[(int)stackId].methodId].name + ")"));
}
fileIdStackInfoId = ArrayUtilities<uint>.InsureCapacity(fileIdStackInfoId, fileStackId);
fileIdStackInfoId[fileStackId] = SetAllocId(allocId);
}
else
{
if (methodIds[(int)stackIds[(int)stackId].methodId] != null) // TODO
{
VerboseDebug("FileStack s" + fileStackId + " maps to stackId=" + stackId +
((stackId == 0) ? "" : " (" + methodIds[(int)stackIds[(int)stackId].methodId].name + ")"));
}
fileIdStackInfoId = ArrayUtilities<uint>.InsureCapacity(fileIdStackInfoId, fileStackId);
fileIdStackInfoId[fileStackId] = SetStackId(stackId);
}
}
}
else if (c == 'f')
{
// Announce a function (used in stacks)
ProfilerMethodID methodId = (ProfilerMethodID)stream.ReadUInt();
stream.SkipSpace();
sb.Length = 0;
// name may contain spaces if they are in angle brackets.
// Example: <Module>::std_less<unsigned void>.()
// The name may be truncated at 255 chars by profilerOBJ.dll
int angleBracketsScope = 0;
c = stream.Current;
while (c > ' ' || angleBracketsScope != 0 && sb.Length < 255)
{
if (c == '<')
{
angleBracketsScope++;
}
sb.Append((char)c);
c = stream.ReadByte();
if (c == '>' && angleBracketsScope > 0)
{
angleBracketsScope--;
}
}
string name = sb.ToString();
stream.SkipSpace();
sb.Length = 0;
c = stream.Current;
while (c > '\r')
{
sb.Append((char)c);
if (c == ')')
{
c = stream.ReadByte();
break;
}
c = stream.ReadByte();
}
string signature = sb.ToString();
Address address = (Address)stream.ReadULong();
uint size = stream.ReadUInt();
ProfilerMethodID moduleId = (ProfilerMethodID)stream.ReadUInt();
uint fileFirstStackId = stream.ReadUInt();
if (!stream.EndOfStream)
{
ProfilerModule module = null;
if (methodId == 0) // Hack for first method, it does not have a module ID or a stack
{
moduleId = 0;
fileFirstStackId = 0;
module = new ProfilerModule(0, "UnknownModule", 0, 0);
}
else
{
module = moduleIds[(int)moduleId];
}
ProfilerStackTraceID firstStackId = GetStackIdForFileId(fileFirstStackId);
VerboseDebug("Method " + methodId + " name=" + name + " sig=" + signature +
" moduleId=" + moduleId + " size=" + size + " stack=S" + firstStackId);
methodIds = ArrayUtilities<ProfilerMethod>.InsureCapacity(methodIds, (uint)methodId);
methodIds[(int)methodId] = new ProfilerMethod(methodId, name, signature, address, size, module, firstStackId);
if ((uint)methodId >= methodIdLimit)
{
methodIdLimit = (uint)methodId + 1;
}
}
}
else if (c == 't')
{
// Announce a nodeId
ProfilerTypeID typeId = (ProfilerTypeID)stream.ReadUInt();
bool isFinalizable = (stream.ReadInt() == 1);
stream.SkipSpace();
sb.Length = 0;
stream.ReadAsciiStringUpTo('\r', sb);
string name = sb.ToString();
if (!stream.EndOfStream)
{
VerboseDebug("Type " + typeId + " name=" + name + " isFinalizable=" + isFinalizable);
CreateType(typeId, name, isFinalizable);
}
}
else if (c == 'h')
{
// A handle was created
if (CreateHandle == null)
{
goto SKIP_LINE;
}
uint threadId = stream.ReadUInt();
Address handle = (Address)stream.ReadULong();
Address objectInHandle = (Address)stream.ReadULong();
uint fileStackId = stream.ReadUInt();
if (!stream.EndOfStream && CreateHandle != null)
{
ProfilerStackTraceID stackId = GetStackIdForFileId(fileStackId);
CreateHandle(handle, objectInHandle, stackId, threadId);
}
}
else if (c == 'i')
{
// Time has gone by (do this every msec to 10 msec
if (Tick == null)
{
goto SKIP_LINE;
}
int millisecondsSinceStart = stream.ReadInt();
if (!stream.EndOfStream)
{
Tick(millisecondsSinceStart);
}
}
else if (c == 'j')
{
// Destroy a handle
if (DestroyHandle == null)
{
goto SKIP_LINE;
}
uint threadId = stream.ReadUInt();
Address handle = (Address)stream.ReadULong();
uint fileStackId = stream.ReadUInt();
if (!stream.EndOfStream)
{
ProfilerStackTraceID stackId = GetStackIdForFileId(fileStackId);
DestroyHandle(handle, stackId, threadId);
}
}
else if (c == 'l')
{
// Finalizer called
if (Finalizer == null)
{
goto SKIP_LINE;
}
Address objectAddress = (Address)stream.ReadULong();
bool isCritical = (stream.ReadInt() == 1);
if (!stream.EndOfStream)
{
Finalizer(objectAddress, isCritical);
}
}
else if (c == 'b')
{
// GC boundary (first args indicates if it is start or end)
if (GCStart == null && GCEnd == null)
{
goto SKIP_LINE;
}
bool gcStart = (stream.ReadInt() == 1);
bool induced = (stream.ReadInt() == 1);
int condemnedGeneration = stream.ReadInt();
if (gcStart)
{
gcNumber++;
}
List<ProfilerGCSegment> gcMemoryRanges = new List<ProfilerGCSegment>(5);
for (; ; )
{
ProfilerGCSegment range = new ProfilerGCSegment((Address)stream.ReadULong(), stream.ReadUInt(), stream.ReadUInt(), stream.ReadInt());
if (range.rangeGeneration < 0)
{
break;
}
gcMemoryRanges.Add(range);
}
if (!stream.EndOfStream)
{
if (gcStart && GCStart != null)
{
GCStart(gcNumber, induced, condemnedGeneration, gcMemoryRanges);
}
if (!gcStart && GCEnd != null)
{
GCEnd(gcNumber, induced, condemnedGeneration, gcMemoryRanges);
}
}
}
else if (c == 'g')
{
// Generation count
int gen0Count = stream.ReadInt();
int gen1Count = stream.ReadInt();
int gen2Count = stream.ReadInt();
}
else if (c == 'r')
{
// Heap roots (for heap dump)
if (HeapDump == null && ObjectDescription == null)
{
goto SKIP_LINE;
}
addressList.Clear();
for (; ; )
{
Address root = (Address)stream.ReadULong();
if (root == BadAddress)
{
break;
}
addressList.Add(root);
}
if (!stream.EndOfStream && HeapDump != null)
{
HeapDump(addressList);
}
}
else if (c == 'o')
{
// Object in heap (heap dump)
if (ObjectDescription == null && HeapDump == null)
{
goto SKIP_LINE;
}
Address objectAddress = (Address)stream.ReadULong();
ProfilerTypeID typeId = (ProfilerTypeID)stream.ReadUInt();
uint size = stream.ReadUInt();
addressList.Clear();
for (; ; )
{
Address reference = (Address)stream.ReadULong();
if (reference == BadAddress)
{
break;
}
addressList.Add(reference);
}
if (!stream.EndOfStream)
{
ObjectDescription(objectAddress, typeId, size, addressList);
}
}
else if (c == 'u')
{
// Object relocation
if (ObjectRangeRelocation == null)
{
goto SKIP_LINE;
}
Address oldBase = (Address)stream.ReadULong();
Address newBase = (Address)stream.ReadULong();
uint size = stream.ReadUInt();
if (!stream.EndOfStream)
{
ObjectRangeRelocation(oldBase, newBase, size);
}
}
else if (c == 'v')
{
// Object live range
if (ObjectRangeLive == null)
{
goto SKIP_LINE;
}
Address startAddress = (Address)stream.ReadULong();
uint size = stream.ReadUInt();
if (!stream.EndOfStream)
{
ObjectRangeLive(startAddress, size);
}
}
else if (c == 'm')
{
// Module load
uint moduleId = stream.ReadUInt();
sb.Length = 0;
stream.ReadAsciiStringUpTo(" 0x", sb);
string name = sb.ToString();
Address address = (Address)stream.ReadULong();
uint fileFirstStackId = stream.ReadUInt();
if (!stream.EndOfStream)
{
ProfilerStackTraceID firstStackId = GetStackIdForFileId(fileFirstStackId);
VerboseDebug("Module " + moduleId + " Addr=" + address + " stack=S" + firstStackId + " name=" + name);
ProfilerModule newModule = CreateModule((ProfilerModuleID)moduleId, name, address, firstStackId);
ModuleLoad?.Invoke(newModule);
}
}
else if (c == 'y')
{
// Assembly load
if (AssemblyLoad == null)
{
goto SKIP_LINE;
}
uint threadId = stream.ReadUInt();
Address assemblyAddress = (Address)stream.ReadULong();
sb.Length = 0;
stream.SkipSpace();
stream.ReadAsciiStringUpTo('\r', sb);
string assemblyName = sb.ToString();
if (!stream.EndOfStream && AssemblyLoad != null)
{
AssemblyLoad(assemblyName, assemblyAddress, threadId);
}
}
else if (c == 'e')
{
// GC root (handle) (heap dump)
if (GCRoot == null)
{
goto SKIP_LINE;
}
Address objectAddress = (Address)stream.ReadULong();
GcRootKind rootKind = (GcRootKind)stream.ReadInt();
GcRootFlags rootFlags = (GcRootFlags)stream.ReadInt();
Address rootID = (Address)stream.ReadULong();
if (!stream.EndOfStream && GCRoot != null)
{
GCRoot(objectAddress, rootKind, rootFlags, rootID);
}
}
else if (c == 's') // NEW! static variable root.
{
if (StaticVar == null)
{
goto SKIP_LINE;
}
Address objectAddress = (Address)stream.ReadULong();
stream.SkipSpace();
sb.Length = 0;
stream.ReadAsciiStringUpTo(' ', sb);
string fieldName = sb.ToString();
stream.SkipSpace();
sb.Length = 0;
stream.ReadAsciiStringUpTo(' ', sb);
string typeName = sb.ToString();
var threadId = stream.ReadUInt();
stream.SkipSpace();
sb.Length = 0;
stream.ReadAsciiStringUpTo(' ', sb);
string appDomainName = sb.ToString();
// Find the typeId for this type name, and when you find it, call the callback.
// Use the TypesNeedingModule dictionary to avoid searching most of the list
var typeIds = GetTypesNeedingModule(typeName, true);
foreach (var typeId in typeIds)
{
var type = GetTypeById(typeId);
if (type.name == typeName)
{
StaticVar(objectAddress, fieldName, typeId, threadId, appDomainName);
break;
}
}
}
else if (c == 'L') // NEW! local variable root.
{
if (LocalVar == null)
{
goto SKIP_LINE;
}
Address objectAddress = (Address)stream.ReadULong();
stream.SkipSpace();
sb.Length = 0;
stream.ReadAsciiStringUpTo(' ', sb);
string localName = sb.ToString();
stream.SkipSpace();
sb.Length = 0;
stream.ReadAsciiStringUpTo(' ', sb);
string methodName = sb.ToString();
stream.SkipSpace();
sb.Length = 0;
stream.ReadAsciiStringUpTo(' ', sb);
string typeName = sb.ToString();
var threadId = stream.ReadUInt();
stream.SkipSpace();
sb.Length = 0;
stream.ReadAsciiStringUpTo(' ', sb);
string appDomainName = sb.ToString();
// Find the typeId for this type name, and when you find it, call the callback.
// Use the TypesNeedingModule dictionary to avoid searching most of the list
var typeIds = GetTypesNeedingModule(typeName, true);
foreach (var typeId in typeIds)
{
var type = GetTypeById(typeId);
if (type.name == typeName)
{
LocalVar(objectAddress, localName, methodName, typeId, threadId, appDomainName);
break;
}
}
}
else if (c == 'M') // NEW! module information for types
{
stream.SkipSpace();
sb.Length = 0;
stream.ReadAsciiStringUpTo(' ', sb);
string typeName = sb.ToString();
stream.SkipSpace();
sb.Length = 0;
stream.ReadAsciiStringUpTo('\r', sb);
string moduleName = sb.ToString();
// Only add module names to type names that we already care about.
var typeIds = GetTypesNeedingModule(typeName, false);
if (typeIds != null)
{
var moduleId = GetModuleIdForName(moduleName);
foreach (var typeId in typeIds)
{
GetTypeById(typeId).ModuleId = moduleId;
}
}
}
else if (c == 'z')
{
// User event
if (UserEvent == null)
{
goto SKIP_LINE;
}
sb.Length = 0;
stream.SkipSpace();
stream.ReadAsciiStringUpTo('\r', sb);
string comment = sb.ToString();
if (!stream.EndOfStream && UserEvent != null)
{
UserEvent(comment);
}
}
else
{
// We matched none of the expected chars
if (stream.EndOfStream)
{
break;
}
Debug.WriteLine("Warning unknown CLRProfiler entry line '" + (char)c + "' on line " + lineNum);
goto SKIP_LINE;
}
continue;
SKIP_LINE:
stream.SkipUpTo('\r');
}
moduleNameToId = null;
typesNeedingModule = null;
}