diff --git a/src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs b/src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs index ccba9a824c..38b4f1b31a 100644 --- a/src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs +++ b/src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs @@ -94,6 +94,7 @@ namespace ICSharpCode.Profiler.Controller.Data sealed class TempFileDatabaseWriter : IProfilingDataWriter { TempFileDatabase database; + bool isClosed; public TempFileDatabaseWriter(TempFileDatabase database) { @@ -121,7 +122,9 @@ namespace ICSharpCode.Profiler.Controller.Data public void Close() { - database.NotifyFinish(); + if (!isClosed) + database.NotifyFinish(); + isClosed = true; } public void WriteMappings(IEnumerable mappings) diff --git a/src/AddIns/Misc/Profiler/Controller/ExtensionMethods.cs b/src/AddIns/Misc/Profiler/Controller/ExtensionMethods.cs index 34944161ee..12c16dbdbd 100644 --- a/src/AddIns/Misc/Profiler/Controller/ExtensionMethods.cs +++ b/src/AddIns/Misc/Profiler/Controller/ExtensionMethods.cs @@ -1,9 +1,12 @@ -using ICSharpCode.Profiler.Controller.Data; -using System; +using System; using System.CodeDom.Compiler; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; +using System.Text.RegularExpressions; + +using ICSharpCode.Profiler.Controller.Data; namespace ICSharpCode.Profiler.Controller { @@ -35,5 +38,100 @@ namespace ICSharpCode.Profiler.Controller { return items.First().Merge(items); } + + /// + /// Creates a comma separated string. The string is encoded so that it can be split + /// into the original parts even if the inputs contains commas. + /// + public static string CreateSeparatedString(this IEnumerable inputs) + { + return CreateSeparatedString(inputs, ','); + } + + /// + /// Creates a separated string using the specified separator. The string is encoded + /// so that it can be split into the original parts even if the inputs contain the separator. + /// + public static string CreateSeparatedString(this IEnumerable inputs, char separator) + { + if (inputs == null) + throw new ArgumentNullException("inputs"); + if (separator == '"') + throw new ArgumentException("Invalid separator"); + + StringBuilder b = new StringBuilder(); + bool first = true; + foreach (string input in inputs) { + if (input == null) + throw new ArgumentNullException("inputs", "An element in inputs is null"); + + if (first) + first = false; + else + b.Append(separator); + + if (input.Length > 0 && input.IndexOf(separator) < 0 && input.IndexOf('"') < 0) { + b.Append(input); + } else { + b.Append('"'); + foreach (char c in input) { + if (c == '"') + b.Append("\"\""); + else + b.Append(c); + } + b.Append('"'); + } + } + return b.ToString(); + } + + /// + /// Splits a comma-separated string. + /// + public static IList SplitSeparatedString(this string input) + { + return SplitSeparatedString(input, ','); + } + + /// + /// Splits a separated string using the specified separator. + /// + public static IList SplitSeparatedString(this string input, char separator) + { + if (input == null) + throw new ArgumentNullException("input"); + if (separator == '"') + throw new ArgumentException("Invalid separator"); + + List result = new List(); + for (int i = 0; i < input.Length; i++) { + Debug.Assert(i == 0 || input[i - 1] == separator); + if (input[i] == '"') { + i++; + StringBuilder b = new StringBuilder(); + for (; i < input.Length; i++) { + char c = input[i]; + if (c == '"') { + i++; + if (i < input.Length && input[i] == '"') + b.Append('"'); + else + break; + } else { + b.Append(c); + } + } + result.Add(b.ToString()); + // i is now positioned before separator + } else { + int end = input.IndexOf(separator, i); + if (end < 0) end = input.Length; + result.Add(input.Substring(i, end - i)); + i = end; // position i before separator + } + } + return result; + } } } diff --git a/src/AddIns/Misc/Profiler/Controller/Profiler.cs b/src/AddIns/Misc/Profiler/Controller/Profiler.cs index f8dd2ee478..036eef7b85 100644 --- a/src/AddIns/Misc/Profiler/Controller/Profiler.cs +++ b/src/AddIns/Misc/Profiler/Controller/Profiler.cs @@ -6,6 +6,7 @@ // using System; +using System.Linq; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -273,6 +274,8 @@ namespace ICSharpCode.Profiler.Controller memHeader32->HeapOffset = Align(memHeader32->ThreadDataOffset + threadDataSize); memHeader32->HeapLength = profilerOptions.SharedMemorySize - memHeader32->HeapOffset; memHeader32->ProcessorFrequency = GetProcessorFrequency(); + memHeader32->DoNotProfileDotnetInternals = !profilerOptions.ProfileDotNetInternals; + memHeader32->CombineRecursiveFunction = profilerOptions.CombineRecursiveFunction; if ((Int32)(fullView.Pointer + memHeader32->HeapOffset) % 8 != 0) { throw new DataMisalignedException("Heap is not aligned properly: " + ((Int32)(fullView.Pointer + memHeader32->HeapOffset)).ToString(CultureInfo.InvariantCulture) + "!"); @@ -575,6 +578,8 @@ namespace ICSharpCode.Profiler.Controller isRunning = false; + this.dataWriter.Close(); + OnSessionEnded(EventArgs.Empty); } @@ -631,27 +636,34 @@ namespace ICSharpCode.Profiler.Controller return false; if (readString.StartsWith("map ", StringComparison.Ordinal)) { - string[] parts = readString.Split('-'); - string[] args = parts[0].Split(' '); + IList parts = readString.SplitSeparatedString(' '); - int id = int.Parse(args[1], CultureInfo.InvariantCulture.NumberFormat); - string name = null; - string returnType = null; - IList parameters = null; - - if (parts[1].StartsWith("Thread#", StringComparison.Ordinal)) { - name = parts[1] + ((parts.Length > 2 && !string.IsNullOrEmpty(parts[2])) ? " - " + parts[2] : ""); - } else { - returnType = parts[1].Trim(); - name = parts[2].Trim(); - if (parts.Length > 3) - parameters = parts[3].Trim().Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries); - } + if (parts.Count < 3) + return false; + + int id = int.Parse(parts[1], CultureInfo.InvariantCulture); + string name = parts[4]; + string returnType = parts[3]; + IList parameters = parts.Skip(5).ToList(); lock (this.dataWriter) { this.dataWriter.WriteMappings(new NameMapping[] {new NameMapping(id, returnType, name, parameters)}); } + return true; + } else if (readString.StartsWith("mapthread ", StringComparison.Ordinal)) { + IList parts = readString.SplitSeparatedString(' '); + + if (parts.Count < 5) + return false; + + int id = int.Parse(parts[1], CultureInfo.InvariantCulture); + string name = parts[3] + ((string.IsNullOrEmpty(parts[4])) ? "" : " - " + parts[4]); + + lock (this.dataWriter) { + this.dataWriter.WriteMappings(new NameMapping[] {new NameMapping(id, null, name, null)}); + } + return true; } else { if (readString.StartsWith("error-", StringComparison.Ordinal)) { diff --git a/src/AddIns/Misc/Profiler/Controller/ProfilerOptions.cs b/src/AddIns/Misc/Profiler/Controller/ProfilerOptions.cs index 5fd0f153d7..fca1124630 100644 --- a/src/AddIns/Misc/Profiler/Controller/ProfilerOptions.cs +++ b/src/AddIns/Misc/Profiler/Controller/ProfilerOptions.cs @@ -20,8 +20,24 @@ namespace ICSharpCode.Profiler.Controller public const int SHARED_MEMORY_SIZE = 64 * 1024 * 1024; // 64 mb bool enableDC; + bool profileDotNetInternals; + bool combineRecursiveFunction; int sharedMemorySize; + /// + /// Gets whether .NET internal calls are profiled or not. + /// + public bool ProfileDotNetInternals { + get { return profileDotNetInternals; } + } + + /// + /// Gets whether recursive functions calls are combined or not. + /// + public bool CombineRecursiveFunction { + get { return combineRecursiveFunction; } + } + /// /// Gets whether data collection is enabled during profiling sessions. /// @@ -39,17 +55,19 @@ namespace ICSharpCode.Profiler.Controller /// /// Creates new ProfilerOptions using the selected settings. /// - public ProfilerOptions(bool enableDC, int sharedMemorySize) + public ProfilerOptions(bool enableDC, int sharedMemorySize, bool profileDotNetInternals, bool combineRecursiveFunction) { this.enableDC = enableDC; this.sharedMemorySize = sharedMemorySize; + this.profileDotNetInternals = profileDotNetInternals; + this.combineRecursiveFunction = combineRecursiveFunction; } /// /// Creates default ProfilerOptions. /// public ProfilerOptions() - : this(true, SHARED_MEMORY_SIZE) + : this(true, SHARED_MEMORY_SIZE, true, false) { } } diff --git a/src/AddIns/Misc/Profiler/Controller/structs.cs b/src/AddIns/Misc/Profiler/Controller/structs.cs index 50669cb4ea..393319f186 100644 --- a/src/AddIns/Misc/Profiler/Controller/structs.cs +++ b/src/AddIns/Misc/Profiler/Controller/structs.cs @@ -30,6 +30,8 @@ namespace ICSharpCode.Profiler.Controller public TargetProcessPointer32 RootFuncInfoAddress; public TargetProcessPointer32 LastThreadListItem; public int ProcessorFrequency; + public bool DoNotProfileDotnetInternals; + public bool CombineRecursiveFunction; public Allocator32 Allocator; } @@ -238,5 +240,6 @@ namespace ICSharpCode.Profiler.Controller { public TargetProcessPointer32 Function; public ulong StartTime; + public int FrameCount; } } diff --git a/src/AddIns/Misc/Profiler/Frontend/AddIn/AddIn.csproj b/src/AddIns/Misc/Profiler/Frontend/AddIn/AddIn.csproj index 5d367683ac..9ed0661dd8 100644 --- a/src/AddIns/Misc/Profiler/Frontend/AddIn/AddIn.csproj +++ b/src/AddIns/Misc/Profiler/Frontend/AddIn/AddIn.csproj @@ -99,7 +99,6 @@ ProfileExecutableForm.xaml - UserControl diff --git a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Commands/SetAsRoot.cs b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Commands/SetAsRoot.cs index 7f8d8029c9..6f0e08a166 100644 --- a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Commands/SetAsRoot.cs +++ b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Commands/SetAsRoot.cs @@ -43,10 +43,8 @@ namespace ICSharpCode.Profiler.AddIn.Commands foreach (CallTreeNode node in items) { if (nameId != null && nameId != node.NameMapping.Id) nameId = null; - NodePath p = node.GetPath().First(); - if (p != null) { - parts.Add("GetNodeByPath(" + string.Join(",", p.Select(i => i.ToString()).ToArray()) + ")"); - } + foreach (var path in node.GetPath()) + parts.Add("GetNodeByPath(" + string.Join(",", path.Select(i => i.ToString()).ToArray()) + ")"); } string header = "Merged Nodes: " + items.First().Name; diff --git a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/ExtensionMethods.cs b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/ExtensionMethods.cs deleted file mode 100644 index 0c753b6e6e..0000000000 --- a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/ExtensionMethods.cs +++ /dev/null @@ -1,112 +0,0 @@ -// -// -// -// -// $Revision: 382 $ -// - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Text; - -namespace ICSharpCode.Profiler.AddIn -{ - static class ExtensionMethods - { - /// - /// Creates a comma separated string. The string is encoded so that it can be split - /// into the original parts even if the inputs contains commas. - /// - public static string CreateSeparatedString(this IEnumerable inputs) - { - return CreateSeparatedString(inputs, ','); - } - - /// - /// Creates a separated string using the specified separator. The string is encoded - /// so that it can be split into the original parts even if the inputs contain the separator. - /// - public static string CreateSeparatedString(this IEnumerable inputs, char separator) - { - if (inputs == null) - throw new ArgumentNullException("inputs"); - if (separator == '"') - throw new ArgumentException("Invalid separator"); - - StringBuilder b = new StringBuilder(); - bool first = true; - foreach (string input in inputs) { - if (input == null) - throw new ArgumentNullException("inputs", "An element in inputs is null"); - - if (first) - first = false; - else - b.Append(separator); - - if (input.Length > 0 && input.IndexOf(separator) < 0 && input.IndexOf('"') < 0) { - b.Append(input); - } else { - b.Append('"'); - foreach (char c in input) { - if (c == '"') - b.Append("\"\""); - else - b.Append(c); - } - b.Append('"'); - } - } - return b.ToString(); - } - - /// - /// Splits a comma-separated string. - /// - public static IList SplitSeparatedString(this string input) - { - return SplitSeparatedString(input, ','); - } - - /// - /// Splits a separated string using the specified separator. - /// - public static IList SplitSeparatedString(this string input, char separator) - { - if (input == null) - throw new ArgumentNullException("input"); - if (separator == '"') - throw new ArgumentException("Invalid separator"); - - List result = new List(); - for (int i = 0; i < input.Length; i++) { - Debug.Assert(i == 0 || input[i - 1] == separator); - if (input[i] == '"') { - i++; - StringBuilder b = new StringBuilder(); - for (; i < input.Length; i++) { - char c = input[i]; - if (c == '"') { - i++; - if (i < input.Length && input[i] == '"') - b.Append('"'); - else - break; - } else { - b.Append(c); - } - } - result.Add(b.ToString()); - // i is now positioned before separator - } else { - int end = input.IndexOf(separator, i); - if (end < 0) end = input.Length; - result.Add(input.Substring(i, end - i)); - i = end; // position i before separator - } - } - return result; - } - } -} diff --git a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/OptionsPanels/General.cs b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/OptionsPanels/General.cs index 93511116be..b0ffb5e482 100644 --- a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/OptionsPanels/General.cs +++ b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/OptionsPanels/General.cs @@ -52,7 +52,10 @@ namespace ICSharpCode.Profiler.AddIn.OptionsPanels public static ProfilerOptions CreateProfilerOptions() { return new ProfilerOptions(properties.Get("EnableDC", true), - properties.Get("SharedMemorySize", ProfilerOptions.SHARED_MEMORY_SIZE)); + properties.Get("SharedMemorySize", ProfilerOptions.SHARED_MEMORY_SIZE), + !properties.Get("PorfileDotNetInternals", true), + properties.Get("CombineRecursiveFunction", false) + ); } } } diff --git a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/ProfilerRunner.cs b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/ProfilerRunner.cs index 5b5254be5e..f1ea9c1f5b 100644 --- a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/ProfilerRunner.cs +++ b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/ProfilerRunner.cs @@ -66,8 +66,6 @@ namespace ICSharpCode.Profiler.AddIn void FinishSession() { - profiler.DataWriter.Close(); - if (database != null) { database.WriteTo(writer, progress => true); // TODO : change default impl to good user interface notification writer.Close(); diff --git a/src/AddIns/Misc/Profiler/Frontend/Gui/Window1.xaml.cs b/src/AddIns/Misc/Profiler/Frontend/Gui/Window1.xaml.cs index bcf2b61edb..ad46c69763 100644 --- a/src/AddIns/Misc/Profiler/Frontend/Gui/Window1.xaml.cs +++ b/src/AddIns/Misc/Profiler/Frontend/Gui/Window1.xaml.cs @@ -88,7 +88,6 @@ namespace ICSharpCode.Profiler.Frontend void profiler_SessionEnded(object sender, EventArgs e) { string pathToDb = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(typeof(Profiler.Controller.Profiler).Assembly.Location), "output.sdps"); - profiler.DataWriter.Close(); ProfilingDataSQLiteWriter writer = new ProfilingDataSQLiteWriter(pathToDb); this.database.WriteTo(writer, progress => true); writer.Close(); @@ -109,8 +108,8 @@ namespace ICSharpCode.Profiler.Frontend MessageBox.Show(ex.ToString()); } } - )); - + ) + ); } void RefreshUI(int startIndex, int endIndex) diff --git a/src/AddIns/Misc/Profiler/Hook/Profiler.cpp b/src/AddIns/Misc/Profiler/Hook/Profiler.cpp index 0319dc57f8..82564a1ebf 100644 --- a/src/AddIns/Misc/Profiler/Hook/Profiler.cpp +++ b/src/AddIns/Misc/Profiler/Hook/Profiler.cpp @@ -81,29 +81,34 @@ ASSEMBLER_CALLBACK FunctionEnterGlobal(int functionID) // this call allows GetOrAddChild to update the value at the top of the stack // if the FunctionInfo is resized FunctionInfo *f = data->stack.top().function; - //DebugWriteLine(L"FunctionEnterGlobal %d, current stack top=%d", functionID, f->Id); - FunctionInfo *newParent = nullptr; - FunctionInfo *child = f->GetOrAddChild(functionID, newParent); - if (newParent != nullptr) { - // f was moved to newParent - // update stack: - data->stack.top().function = newParent; - // update parent of f: - if (data->stack.hasAtLeastTwoElements()) { -// DebugWriteLine("Updating parent of parent"); - data->stack.belowTop().function->AddOrUpdateChild(newParent); - data->stack.belowTop().function->Check(); - } else { - DebugWriteLine(L"Updating parent of parent (root)"); - profiler.MovedRootChild(newParent); + + if ((sharedMemoryHeader->doNotProfileDotnetInternals && functionID < 0 && f->Id < 0) || (sharedMemoryHeader->combineRecursiveFunction && functionID == f->Id)) { + data->stack.top().frameCount++; + } else { + //DebugWriteLine(L"FunctionEnterGlobal %d, current stack top=%d", functionID, f->Id); + FunctionInfo *newParent = nullptr; + FunctionInfo *child = f->GetOrAddChild(functionID, newParent); + if (newParent != nullptr) { + // f was moved to newParent + // update stack: + data->stack.top().function = newParent; + // update parent of f: + if (data->stack.hasAtLeastTwoElements()) { + //DebugWriteLine("Updating parent of parent"); + data->stack.belowTop().function->AddOrUpdateChild(newParent); + data->stack.belowTop().function->Check(); + } else { + DebugWriteLine(L"Updating parent of parent (root)"); + profiler.MovedRootChild(newParent); + } + FreeFunctionInfo(f); } - FreeFunctionInfo(f); + + // Set the stats for this function + child->CallCount++; + + data->stack.push(StackEntry(child, __rdtsc())); } - - // Set the stats for this function - child->CallCount++; - - data->stack.push(StackEntry(child, __rdtsc())); data->inLock = 0; } @@ -132,8 +137,11 @@ ASSEMBLER_CALLBACK FunctionLeaveGlobal() } else { StackEntry &stackTop = data->stack.top(); //DebugWriteLine(L"FunctionLeaveGlobal %d", stackTop.function->Id); - stackTop.function->TimeSpent += (__rdtsc() - stackTop.startTime); - data->stack.pop(); + stackTop.frameCount--; + if (stackTop.frameCount == 0) { + stackTop.function->TimeSpent += (__rdtsc() - stackTop.startTime); + data->stack.pop(); + } } data->inLock = 0; @@ -149,9 +157,9 @@ ASSEMBLER_CALLBACK FunctionTailcallGlobal() // FunctionTailcallGlobal call will be followed by FunctionEnterGlobal for new function } -volatile LONG nextFunctionID = 0; +volatile LONG nextPosFunctionID = 0; -int getNewFunctionID() { +int getNewPosFunctionID() { const int step = 5; // Simple continuous assignment of IDs leads to problems with the // linear probing in FunctionInfo, so we use multiples of 'step' as IDs. @@ -159,9 +167,9 @@ int getNewFunctionID() { LONG oldID; LONG newID; do { - oldID = nextFunctionID; - newID = oldID + step; - } while (InterlockedCompareExchange(&nextFunctionID, newID, oldID) != oldID); + oldID = nextPosFunctionID; + newID = ((oldID + step) & 0x7FFFFFFF); // x % 2^31 + } while (InterlockedCompareExchange(&nextPosFunctionID, newID, oldID) != oldID); return newID; } @@ -175,15 +183,19 @@ UINT_PTR CProfiler::MapFunction(FunctionID functionID) { mapFunctionCriticalSection.Enter(); int clientData = 0; + TFunctionIDMap::iterator it = this->functionIDMap.find(functionID); if (it == this->functionIDMap.end()) { DebugWriteLine(L"Creating new ID"); - clientData = getNewFunctionID(); + if (sigReader->IsNetInternal(functionID)) + clientData = -getNewPosFunctionID(); // negative series + else + clientData = getNewPosFunctionID(); // positive series this->functionIDMap.insert(TFunctionIDPair(functionID, clientData)); // send to host std::wstring signature = sigReader->Parse(functionID); - LogString(L"map %d %Id -%s-", clientData, functionID, signature.c_str()); + LogString(L"map %d %Id %s", clientData, functionID, signature.c_str()); } else { DebugWriteLine(L"using old ID"); clientData = it->second; @@ -197,7 +209,7 @@ FunctionInfo *CProfiler::CreateNewRoot() { rootElementCriticalSection.Enter(); FunctionInfo *oldRoot = sharedMemoryHeader->RootFuncInfo; FunctionInfo *newRoot = nullptr; - FunctionInfo *newThreadRoot = oldRoot->GetOrAddChild(getNewFunctionID(), newRoot); + FunctionInfo *newThreadRoot = oldRoot->GetOrAddChild(getNewPosFunctionID(), newRoot); if (newRoot != nullptr) { sharedMemoryHeader->RootFuncInfo = newRoot; FreeFunctionInfo(oldRoot); @@ -208,7 +220,7 @@ FunctionInfo *CProfiler::CreateNewRoot() { data->functionInfoId = newThreadRoot->Id; - LogString(L"map %d 0 -Thread#%d-%s-", newThreadRoot->Id, GetCurrentThreadId(), data->threadName.c_str()); + LogString(L"mapthread %d 0 \"Thread#%d\" \"%s\"", newThreadRoot->Id, GetCurrentThreadId(), data->threadName.c_str()); return newThreadRoot; } diff --git a/src/AddIns/Misc/Profiler/Hook/ProfilerMetaData.cpp b/src/AddIns/Misc/Profiler/Hook/ProfilerMetaData.cpp index cc2400915b..b183f9ea9c 100644 --- a/src/AddIns/Misc/Profiler/Hook/ProfilerMetaData.cpp +++ b/src/AddIns/Misc/Profiler/Hook/ProfilerMetaData.cpp @@ -156,11 +156,14 @@ bool SignatureReader::ReadMethodDefSig(byte head) // Append Name - this->output << L"-"; - this->output << className.str(); + this->output << L" "; + this->output << '"'; + std::wstring name = className.str(); + AppendEscapedString(name); this->output << L"."; - this->output << szFunction; - this->output << L"-"; + AppendEscapedString(szFunction); + this->output << '"'; + this->output << L" "; // Read Parameters // Param ::= CustomMod* ( TYPEDBYREF | [BYREF] Type ) @@ -188,6 +191,7 @@ bool SignatureReader::ReadMethodDefSig(byte head) if (!FAILED(this->metaData->GetParamForMethodIndex(this->methodDefiniton, i + 1, ¶mDef))) { if (!FAILED(this->metaData->GetParamProps(paramDef, &this->methodDefiniton, &pSequence, paramName, 256, &pchName, &pdwAttr, &pdwCPlusTypeFlag, &ppValue, &pcchValue))) { + this->output << '"'; switch (type) { case ELEMENT_TYPE_TYPEDBYREF: // ignore @@ -205,12 +209,13 @@ bool SignatureReader::ReadMethodDefSig(byte head) } this->output << L" "; - this->output << paramName; + AppendEscapedString(paramName); + this->output << '"'; } } if (i < paramCount - 1) - this->output << L", "; + this->output << L" "; } return true; @@ -297,12 +302,12 @@ bool SignatureReader::ReadType(byte type) hr = this->metaData->GetTypeRefProps(token, nullptr, zName, 1024, &length); hr = this->metaData->GetTypeDefProps(token, zName, 1024, &length, nullptr, nullptr); if (SUCCEEDED(hr) && length > 0) - this->output << zName; + AppendEscapedString(zName); else { hr = this->metaData->GetTypeRefProps(token, nullptr, zName, 1024, &length); hr = this->metaData->GetTypeDefProps(token, zName, 1024, &length, nullptr, nullptr); if (SUCCEEDED(hr) && length > 0) - this->output << zName; + AppendEscapedString(zName); else this->output << L"?"; } @@ -345,7 +350,7 @@ bool SignatureReader::ReadType(byte type) hr = this->metaData->GetTypeRefProps(token, nullptr, zName, 1024, &length); hr = this->metaData->GetTypeDefProps(token, zName, 1024, &length, nullptr, nullptr); if (SUCCEEDED(hr) && length > 0) - this->output << zName; + AppendEscapedString(zName); else this->output << L"?"; if (!this->ReadCompressedInt(&tmp2)) @@ -359,7 +364,7 @@ bool SignatureReader::ReadType(byte type) return false; this->ReadType(type); if ((i + 1) < genArgCount) - this->output << L", "; + this->output << L","; } this->output << L">"; break; @@ -520,3 +525,78 @@ bool SignatureReader::ReadCompressedInt(int *out) *out = ((byte1 & 0x1f) << 24) | (byte2 << 16) | (byte3 << 8) | byte4; return true; } + +const byte mscorlibkey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +const byte systemdrawingkey[] = { + 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x52, 0x53, 0x41, 0x31, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x07, 0xD1, + 0xFA, 0x57, 0xC4, 0xAE, 0xD9, 0xF0, 0xA3, 0x2E, 0x84, 0xAA, 0x0F, 0xAE, 0xFD, 0x0D, 0xE9, 0xE8, 0xFD, + 0x6A, 0xEC, 0x8F, 0x87, 0xFB, 0x03, 0x76, 0x6C, 0x83, 0x4C, 0x99, 0x92, 0x1E, 0xB2, 0x3B, 0xE7, 0x9A, + 0xD9, 0xD5, 0xDC, 0xC1, 0xDD, 0x9A, 0xD2, 0x36, 0x13, 0x21, 0x02, 0x90, 0x0B, 0x72, 0x3C, 0xF9, 0x80, + 0x95, 0x7F, 0xC4, 0xE1, 0x77, 0x10, 0x8F, 0xC6, 0x07, 0x77, 0x4F, 0x29, 0xE8, 0x32, 0x0E, 0x92, 0xEA, + 0x05, 0xEC, 0xE4, 0xE8, 0x21, 0xC0, 0xA5, 0xEF, 0xE8, 0xF1, 0x64, 0x5C, 0x4C, 0x0C, 0x93, 0xC1, 0xAB, + 0x99, 0x28, 0x5D, 0x62, 0x2C, 0xAA, 0x65, 0x2C, 0x1D, 0xFA, 0xD6, 0x3D, 0x74, 0x5D, 0x6F, 0x2D, 0xE5, + 0xF1, 0x7E, 0x5E, 0xAF, 0x0F, 0xC4, 0x96, 0x3D, 0x26, 0x1C, 0x8A, 0x12, 0x43, 0x65, 0x18, 0x20, 0x6D, + 0xC0, 0x93, 0x34, 0x4D, 0x5A, 0xD2, 0x93 +}; + + +bool SignatureReader::IsNetInternal(FunctionID fid) +{ + mdToken funcToken; + HRESULT hr = S_OK; + IMetaDataAssemblyImport *asmMetaData; + const void *publicKey; + ULONG pKLength; + + hr = profilerInfo->GetTokenAndMetaDataFromFunction(fid, IID_IMetaDataAssemblyImport, (LPUNKNOWN *) &asmMetaData, &funcToken); + if (SUCCEEDED(hr)) { + mdAssembly assembly; + hr = asmMetaData->GetAssemblyFromScope(&assembly); + if (SUCCEEDED(hr)) { + WCHAR assemblyName[NAME_BUFFER_SIZE]; + ULONG assemblyNameLength; + + hr = asmMetaData->GetAssemblyProps(assembly, &publicKey, &pKLength, nullptr, assemblyName, NAME_BUFFER_SIZE, &assemblyNameLength, nullptr, nullptr); + DebugWriteLine(L"assembly: %s", assemblyName); + const byte *b = (const byte *)publicKey; + WCHAR tmp[8]; + + std::wstring s(L"{ "); + + for (ULONG i = 0; i < pKLength; i++) { + memset(tmp, 0, 16); + swprintf_s(tmp, L"0x%02X, ", (int) b[i]); + s.append(tmp); + } + + s.append(L" }"); + + DebugWriteLine(L"assembly: PK: %s", s.c_str()); + + if (pKLength == sizeof(mscorlibkey) && memcmp(mscorlibkey, b, sizeof(mscorlibkey)) == 0) + return true; + if (pKLength == sizeof(systemdrawingkey) && memcmp(systemdrawingkey, b, sizeof(systemdrawingkey)) == 0) + return true; + } + } + + return false; +} + +void SignatureReader::AppendEscapedString(const WCHAR *input) +{ + for (const WCHAR *ptr = input; *ptr != 0; ptr++) { + WCHAR c = *ptr; + if (c == '"') + this->output << "\"\""; + else + this->output << c; + } +} + +void SignatureReader::AppendEscapedString(const std::wstring &input_string) +{ + AppendEscapedString(input_string.c_str()); +} \ No newline at end of file diff --git a/src/AddIns/Misc/Profiler/Hook/ProfilerMetaData.h b/src/AddIns/Misc/Profiler/Hook/ProfilerMetaData.h index 5a54e7d5f3..e1454eea56 100644 --- a/src/AddIns/Misc/Profiler/Hook/ProfilerMetaData.h +++ b/src/AddIns/Misc/Profiler/Hook/ProfilerMetaData.h @@ -99,8 +99,16 @@ class SignatureReader { bool ReadShapedArray(byte); bool ReadSzArray(byte); bool ReadArrayType(byte); - + + /// + /// Creates a separated string using the specified separator. The string is encoded + /// so that it can be split into the original parts even if the inputs contain the separator. + /// + void AppendEscapedString(const WCHAR *input); + void AppendEscapedString(const std::wstring &input); public: - std::wstring Parse(FunctionID); + std::wstring Parse(FunctionID); + bool IsNetInternal(FunctionID); SignatureReader(ICorProfilerInfo *); }; + diff --git a/src/AddIns/Misc/Profiler/Hook/SharedMemory.cpp b/src/AddIns/Misc/Profiler/Hook/SharedMemory.cpp index a8971ba2a0..ab3036e615 100644 --- a/src/AddIns/Misc/Profiler/Hook/SharedMemory.cpp +++ b/src/AddIns/Misc/Profiler/Hook/SharedMemory.cpp @@ -19,11 +19,11 @@ CSharedMemory::CSharedMemory(char *name) DebugWriteLine(L"MapViewOfFile returned nullptr"); MessageBox(nullptr, TEXT("Could not open Shared Memory, please restart the profiler!"), TEXT("Profiler Error"), MB_OK); } - SharedMemoryHeader* header = (SharedMemoryHeader*)this->startPtr; - if (header->Magic != '~SM1') { + this->header = (SharedMemoryHeader*)this->startPtr; + if (this->header->Magic != '~SM1') { DebugWriteLine(L"Corrupted shared memory header"); } - this->length = header->TotalLength; + this->length = this->header->TotalLength; UnmapViewOfFile(this->startPtr); this->startPtr = MapViewOfFile(this->fileHandle, FILE_MAP_ALL_ACCESS, 0, 0, this->length); if (startPtr == nullptr) { diff --git a/src/AddIns/Misc/Profiler/Hook/SharedMemory.h b/src/AddIns/Misc/Profiler/Hook/SharedMemory.h index 9699e1f6b2..d0d30b882d 100644 --- a/src/AddIns/Misc/Profiler/Hook/SharedMemory.h +++ b/src/AddIns/Misc/Profiler/Hook/SharedMemory.h @@ -26,6 +26,8 @@ struct SharedMemoryHeader FunctionInfo* RootFuncInfo; ThreadLocalData* LastThreadListItem; int ProcFrequency; + bool doNotProfileDotnetInternals; + bool combineRecursiveFunction; freeListAllocator mallocator; }; @@ -35,6 +37,7 @@ public: CSharedMemory(TCHAR *name); ~CSharedMemory(); void* GetStartPtr(); + SharedMemoryHeader *header; private: HANDLE fileHandle; void* startPtr; diff --git a/src/AddIns/Misc/Profiler/Hook/global.h b/src/AddIns/Misc/Profiler/Hook/global.h index 3a20373fb8..12de75a593 100644 --- a/src/AddIns/Misc/Profiler/Hook/global.h +++ b/src/AddIns/Misc/Profiler/Hook/global.h @@ -23,9 +23,10 @@ extern DWORD tls_index; struct StackEntry { FunctionInfo *function; ULONGLONG startTime; + int frameCount; StackEntry() {} - StackEntry(FunctionInfo * function, ULONGLONG startTime) : function(function), startTime(startTime) {} + StackEntry(FunctionInfo * function, ULONGLONG startTime) : function(function), startTime(startTime), frameCount(1) {} }; struct ThreadLocalData {