Skip to content

Commit b5f7fd9

Browse files
author
Vance Morrison
authored
Merge pull request microsoft#857 from kayle/native-allocation-type-tracking
track type of native allocations when available
2 parents 3676e90 + 61bb21b commit b5f7fd9

File tree

2 files changed

+247
-14
lines changed

2 files changed

+247
-14
lines changed

src/PerfView/PerfViewData.cs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5713,6 +5713,10 @@ protected internal override StackSource OpenStackSourceImpl(string streamName, T
57135713
var heapParser = new HeapTraceProviderTraceEventParser(eventSource);
57145714
Dictionary<Address, StackSourceSample> lastHeapAllocs = null;
57155715

5716+
var loadedModules = new Dictionary<TraceModuleFile, NativeSymbolModule>();
5717+
var allocationTypeNames = new Dictionary<CallStackIndex, string>();
5718+
var symReader = GetSymbolReader(log);
5719+
57165720
Address lastHeapHandle = 0;
57175721

57185722
float peakMetric = 0;
@@ -5731,8 +5735,9 @@ protected internal override StackSource OpenStackSourceImpl(string streamName, T
57315735

57325736
sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
57335737
sample.Metric = data.AllocSize;
5734-
var nodeIndex = stackSource.Interner.FrameIntern(GetAllocName((uint)data.AllocSize));
5735-
sample.StackIndex = stackSource.Interner.CallStackIntern(nodeIndex, stackSource.GetCallStack(data.CallStackIndex(), data));
5738+
var callStackIndex = data.CallStackIndex();
5739+
var nodeIndex = stackSource.Interner.FrameIntern(GetAllocationType(callStackIndex) ?? GetAllocName((uint)data.AllocSize));
5740+
sample.StackIndex = stackSource.Interner.CallStackIntern(nodeIndex, stackSource.GetCallStack(callStackIndex, data));
57365741
var addedSample = stackSource.AddSample(sample);
57375742
allocs[data.AllocAddress] = addedSample;
57385743

@@ -5744,6 +5749,45 @@ protected internal override StackSource OpenStackSourceImpl(string streamName, T
57445749
}
57455750
sumCumMetric += cumMetric;
57465751
cumCount++;
5752+
5753+
// Performs a stack crawl to match the best typename to this allocation.
5754+
// Returns null if no typename was found.
5755+
// This updates loadedModules and allocationTypeNames. It reads symReader/eventLog.
5756+
string GetAllocationType(CallStackIndex csi)
5757+
{
5758+
if (!allocationTypeNames.TryGetValue(csi, out var typeName))
5759+
{
5760+
const int frameLimit = 25; // we'll search the first 25 frames for the best match
5761+
5762+
int frameCount = 0;
5763+
for (var current = csi; current != CallStackIndex.Invalid && frameCount < frameLimit; current = eventLog.CallStacks.Caller(current), frameCount++)
5764+
{
5765+
var module = eventLog.CodeAddresses.ModuleFile(eventLog.CallStacks.CodeAddressIndex(current));
5766+
if (module == null)
5767+
{
5768+
continue;
5769+
}
5770+
5771+
if (!loadedModules.TryGetValue(module, out var symbolModule))
5772+
{
5773+
loadedModules[module] = symbolModule =
5774+
(module.PdbSignature != Guid.Empty
5775+
? symReader.FindSymbolFilePath(module.PdbName, module.PdbSignature, module.PdbAge, module.FilePath)
5776+
: symReader.FindSymbolFilePathForModule(module.FilePath)) is string pdb
5777+
? symReader.OpenNativeSymbolFile(pdb)
5778+
: null;
5779+
}
5780+
5781+
typeName = symbolModule?.GetTypeForHeapAllocationSite(
5782+
(uint)(eventLog.CodeAddresses.Address(eventLog.CallStacks.CodeAddressIndex(current)) - module.ImageBase)
5783+
) ?? typeName;
5784+
}
5785+
5786+
allocationTypeNames[csi] = typeName;
5787+
}
5788+
5789+
return typeName;
5790+
}
57475791
};
57485792

57495793
heapParser.HeapTraceFree += delegate (HeapFreeTraceData data)

src/TraceEvent/Symbols/NativeSymbolModule.cs

Lines changed: 201 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,20 @@ namespace Microsoft.Diagnostics.Symbols
2121
/// support other formats (e.g. Dwarf).
2222
///
2323
/// To implmente support for Windows PDBs we use the Debug Interface Access (DIA). See
24-
/// http://msdn.microsoft.com/en-us/library/x93ctkx8.aspx for more. I have only exposed what
24+
/// http://msdn.microsoft.com/library/x93ctkx8.aspx for more. I have only exposed what
2525
/// I need, and the interface is quite large (and not super pretty).
2626
/// </summary>
2727
public unsafe class NativeSymbolModule : ManagedSymbolModule
2828
{
29+
/// <summary>
30+
/// Returns the name of the type allocated for a given relative virtual address.
31+
/// Returns null if the given rva does not match a known heap allocation site.
32+
/// </summary>
33+
public string GetTypeForHeapAllocationSite(uint rva)
34+
{
35+
return m_heapAllocationSites.Value.TryGetValue(rva, out var name) ? name : null;
36+
}
37+
2938
/// <summary>
3039
/// Finds a (method) symbolic name for a given relative virtual address of some code.
3140
/// Returns an empty string if a name could not be found.
@@ -922,27 +931,46 @@ private string SourceServerEvaluate(string result, Dictionary<string, string> va
922931
#endregion
923932
}
924933

925-
private void Initialize(SymbolReader reader, string pdbFilePath, Action loadData)
934+
private NativeSymbolModule(SymbolReader reader, string pdbFilePath, Action<IDiaDataSource3> loadData) : base(reader, pdbFilePath)
926935
{
927936
m_reader = reader;
928937

929938
m_source = DiaLoader.GetDiaSourceObject();
930-
loadData();
939+
loadData(m_source);
931940
m_source.openSession(out m_session);
932941
m_session.getSymbolsByAddr(out m_symbolsByAddr);
933942

943+
m_heapAllocationSites = new Lazy<IReadOnlyDictionary<uint, string>>(() =>
944+
{
945+
// Retrieves the S_HEAPALLOCSITE information from the pdb as described here:
946+
// https://docs.microsoft.com/visualstudio/profiling/custom-native-etw-heap-events
947+
Dictionary<uint, string> result = null;
948+
m_session.getHeapAllocationSites(out var diaEnumSymbols);
949+
for (; ; )
950+
{
951+
diaEnumSymbols.Next(1, out var sym, out var fetchCount);
952+
if (fetchCount == 0)
953+
{
954+
return (IReadOnlyDictionary<uint, string>)result ?? System.Collections.Immutable.ImmutableDictionary<uint, string>.Empty;
955+
}
956+
957+
result = result ?? new Dictionary<uint, string>();
958+
m_session.symbolById(sym.typeId, out var typeSym);
959+
result[sym.relativeVirtualAddress + (uint)sym.length] = HeapAllocationTypeInfo.GetTypeName(typeSym);
960+
}
961+
});
962+
934963
m_reader.m_log.WriteLine("Opening PDB {0} with signature GUID {1} Age {2}", pdbFilePath, PdbGuid, PdbAge);
935964
}
936965

937-
internal NativeSymbolModule(SymbolReader reader, string pdbFilePath) : base(reader, pdbFilePath)
966+
internal NativeSymbolModule(SymbolReader reader, string pdbFilePath)
967+
: this(reader, pdbFilePath, s => s.loadDataFromPdb(pdbFilePath))
938968
{
939-
Initialize(reader, pdbFilePath, () => m_source.loadDataFromPdb(pdbFilePath));
940969
}
941970

942-
internal NativeSymbolModule(SymbolReader reader, string pdbFilePath, Stream pdbStream) : base(reader, pdbFilePath)
971+
internal NativeSymbolModule(SymbolReader reader, string pdbFilePath, Stream pdbStream)
972+
: this(reader, pdbFilePath, s => s.loadDataFromIStream(new ComStreamWrapper(pdbStream)))
943973
{
944-
IStream comStream = new ComStreamWrapper(pdbStream);
945-
Initialize(reader, pdbFilePath, () => m_source.loadDataFromIStream(comStream));
946974
}
947975

948976
internal void LogManagedInfo(string pdbName, Guid pdbGuid, int pdbAge)
@@ -1166,6 +1194,166 @@ public byte[] GetTypeMDTokenMap()
11661194
return buf;
11671195
}
11681196

1197+
/// <summary>
1198+
/// This static class contains the GetTypeName method for retrieving the type name of
1199+
/// a heap allocation site.
1200+
///
1201+
/// See https://github.com/KirillOsenkov/Dia2Dump/blob/master/PrintSymbol.cpp for more details
1202+
/// </summary>
1203+
private static class HeapAllocationTypeInfo
1204+
{
1205+
internal static string GetTypeName(IDiaSymbol symbol)
1206+
{
1207+
var name = symbol.name ?? "<unknown>";
1208+
1209+
switch ((SymTagEnum)symbol.symTag)
1210+
{
1211+
case SymTagEnum.UDT:
1212+
case SymTagEnum.Enum:
1213+
case SymTagEnum.Typedef:
1214+
return name;
1215+
case SymTagEnum.FunctionType:
1216+
return "function";
1217+
case SymTagEnum.PointerType:
1218+
return $"{GetTypeName(symbol.type)} {(symbol.reference != 0 ? "&" : "*") }";
1219+
case SymTagEnum.ArrayType:
1220+
return "array";
1221+
case SymTagEnum.BaseType:
1222+
var sb = new StringBuilder();
1223+
switch ((BasicType)symbol.baseType)
1224+
{
1225+
case BasicType.btUInt:
1226+
sb.Append("unsigned ");
1227+
goto case BasicType.btInt;
1228+
case BasicType.btInt:
1229+
switch (symbol.length)
1230+
{
1231+
case 1:
1232+
sb.Append("char");
1233+
break;
1234+
case 2:
1235+
sb.Append("short");
1236+
break;
1237+
case 4:
1238+
sb.Append("int");
1239+
break;
1240+
case 8:
1241+
sb.Append("long");
1242+
break;
1243+
}
1244+
return sb.ToString();
1245+
case BasicType.btFloat:
1246+
return symbol.length == 4 ? "float" : "double";
1247+
default:
1248+
return BaseTypes[symbol.baseType];
1249+
}
1250+
}
1251+
1252+
return $"unhandled symbol tag {symbol.symTag}";
1253+
}
1254+
1255+
private enum SymTagEnum
1256+
{
1257+
Null,
1258+
Exe,
1259+
Compiland,
1260+
CompilandDetails,
1261+
CompilandEnv,
1262+
Function,
1263+
Block,
1264+
Data,
1265+
Annotation,
1266+
Label,
1267+
PublicSymbol,
1268+
UDT,
1269+
Enum,
1270+
FunctionType,
1271+
PointerType,
1272+
ArrayType,
1273+
BaseType,
1274+
Typedef,
1275+
BaseClass,
1276+
Friend,
1277+
FunctionArgType,
1278+
FuncDebugStart,
1279+
FuncDebugEnd,
1280+
UsingNamespace,
1281+
VTableShape,
1282+
VTable,
1283+
Custom,
1284+
Thunk,
1285+
CustomType,
1286+
ManagedType,
1287+
Dimension,
1288+
CallSite,
1289+
InlineSite,
1290+
BaseInterface,
1291+
VectorType,
1292+
MatrixType,
1293+
HLSLType
1294+
};
1295+
1296+
private enum BasicType
1297+
{
1298+
btNoType = 0,
1299+
btVoid = 1,
1300+
btChar = 2,
1301+
btWChar = 3,
1302+
btInt = 6,
1303+
btUInt = 7,
1304+
btFloat = 8,
1305+
btBCD = 9,
1306+
btBool = 10,
1307+
btLong = 13,
1308+
btULong = 14,
1309+
btCurrency = 25,
1310+
btDate = 26,
1311+
btVariant = 27,
1312+
btComplex = 28,
1313+
btBit = 29,
1314+
btBSTR = 30,
1315+
btHresult = 31,
1316+
btChar16 = 32, // char16_t
1317+
btChar32 = 33, // char32_t
1318+
};
1319+
1320+
private static readonly string[] BaseTypes = new[]
1321+
{
1322+
"<NoType>", // btNoType = 0,
1323+
"void", // btVoid = 1,
1324+
"char", // btChar = 2,
1325+
"wchar_t", // btWChar = 3,
1326+
"signed char",
1327+
"unsigned char",
1328+
"int", // btInt = 6,
1329+
"unsigned int", // btUInt = 7,
1330+
"float", // btFloat = 8,
1331+
"<BCD>", // btBCD = 9,
1332+
"bool", // btBool = 10,
1333+
"short",
1334+
"unsigned short",
1335+
"long", // btLong = 13,
1336+
"unsigned long", // btULong = 14,
1337+
"__int8",
1338+
"__int16",
1339+
"__int32",
1340+
"__int64",
1341+
"__int128",
1342+
"unsigned __int8",
1343+
"unsigned __int16",
1344+
"unsigned __int32",
1345+
"unsigned __int64",
1346+
"unsigned __int128",
1347+
"<currency>", // btCurrency = 25,
1348+
"<date>", // btDate = 26,
1349+
"VARIANT", // btVariant = 27,
1350+
"<complex>", // btComplex = 28,
1351+
"<bit>", // btBit = 29,
1352+
"BSTR", // btBSTR = 30,
1353+
"HRESULT" // btHresult = 31
1354+
};
1355+
}
1356+
11691357
private bool m_checkedForMergedAssemblies;
11701358
private Dictionary<int, string> m_mergedAssemblies;
11711359

@@ -1175,10 +1363,11 @@ public byte[] GetTypeMDTokenMap()
11751363
private ManagedSymbolModule m_managedPdb;
11761364
private bool m_managedPdbAttempted;
11771365

1178-
internal SymbolReader m_reader;
1179-
internal IDiaSession m_session;
1180-
private IDiaDataSource3 m_source;
1181-
private IDiaEnumSymbolsByAddr m_symbolsByAddr;
1366+
internal readonly IDiaSession m_session;
1367+
private readonly SymbolReader m_reader;
1368+
private readonly IDiaDataSource3 m_source;
1369+
private readonly IDiaEnumSymbolsByAddr m_symbolsByAddr;
1370+
private readonly Lazy<IReadOnlyDictionary<uint, string>> m_heapAllocationSites; // rva => typename
11821371

11831372
#endregion
11841373
}

0 commit comments

Comments
 (0)