<p>The output of the SCC analysis is passed to the
`cross_references()` callback. It is expected to set the
`is_alive` flag on those strongly connected components that it
- wishes to be kept alive. Only bridged objects will be
- reported to the callback, i.e., non-bridged objects are
- removed from the callback graph.
+ wishes to be kept alive. The value of `is_alive` will be
+ ignored on any SCCs which lack bridges.
<p>In monodroid each bridged object has a corresponding Java
mirror object. In the bridge callback it reifies the Mono
<div class="mapi-header">
enum {
- SGEN_BRIDGE_VERSION = 4
+ SGEN_BRIDGE_VERSION = 5
};
typedef enum {
-Subproject commit ea18396c86ab3f8ba5d0fcd9ada9e066bd4f9f92
+Subproject commit f38c0f5d99b6e44fa906d14e1146fa65a3806cd3
/* pointer to a previously allocated heap */
/* object. */
+// Keep somewhat in sync with mono/metadata/profiler.h:enum MonoGCEvent
typedef enum {
GC_EVENT_START,
GC_EVENT_MARK_START,
+++ /dev/null
-//
-// Copyright (c) 2015 Xamarin Inc. (http://www.xamarin.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-using System;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-
-[assembly: AssemblyTitle ("System.Private.CoreLib.InteropServices.dll")]
-[assembly: AssemblyDescription ("System.Private.CoreLib.InteropServices.dll")]
-[assembly: AssemblyDefaultAlias ("System.Private.CoreLib.InteropServices.dll")]
-[assembly: AssemblyCompany ("Xamarin, Inc.")]
-[assembly: AssemblyProduct ("Mono Common Language Infrastructure")]
-[assembly: AssemblyCopyright ("Copyright (c) 2015 Xamarin Inc. (http://www.xamarin.com)")]
-[assembly: AssemblyVersion ("4.0.0.0")]
-[assembly: AssemblyInformationalVersion ("4.0.0.0")]
-[assembly: AssemblyFileVersion ("4.0.0.0")]
-[assembly: AssemblyDelaySign (true)]
-[assembly: AssemblyKeyFile ("../../msfinal.pub")]
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>\r
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
- <PropertyGroup>\r
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>\r
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>\r
- <ProductVersion>9.0.30729</ProductVersion>\r
- <SchemaVersion>2.0</SchemaVersion>\r
- <ProjectGuid>{5D05F5E2-7378-4A35-B1A3-76ABEB4A71C3}</ProjectGuid>\r
- <OutputType>Library</OutputType>\r
- <NoWarn>1699,1616,1699</NoWarn>\r
- <OutputPath>./../../../class/lib/net_4_x/Facades</OutputPath>\r
- <IntermediateOutputPath>obj-Facades</IntermediateOutputPath>\r
- <GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>\r
- <NoStdLib>True</NoStdLib>\r
- \r
- <NoConfig>True</NoConfig>\r
- \r
- <AppDesignerFolder>Properties</AppDesignerFolder>\r
- <RootNamespace>\r
- </RootNamespace>\r
- <AssemblyName>System.Private.CoreLib.InteropServices</AssemblyName>\r
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>\r
- <FileAlignment>512</FileAlignment>\r
- </PropertyGroup>\r
- <PropertyGroup>\r
- <SignAssembly>true</SignAssembly>\r
- <DelaySign>true</DelaySign>\r
- </PropertyGroup>\r
- <PropertyGroup>\r
- <AssemblyOriginatorKeyFile>../../msfinal.pub</AssemblyOriginatorKeyFile>\r
- </PropertyGroup>\r
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">\r
- <DebugSymbols>true</DebugSymbols>\r
- <DebugType>full</DebugType>\r
- <NoWarn>1699,1616,1699</NoWarn>\r
- <Optimize>false</Optimize>\r
- <DefineConstants>TRACE;NET_4_0;NET_4_5;NET_4_6;MONO;DISABLE_CAS_USE</DefineConstants>\r
- <ErrorReport>prompt</ErrorReport>\r
- <WarningLevel>4</WarningLevel>\r
- </PropertyGroup>\r
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">\r
- <DebugType>pdbonly</DebugType>\r
- <NoWarn>1699,1616,1699</NoWarn>\r
- <Optimize>true</Optimize>\r
- <DefineConstants>NET_4_0;NET_4_5;NET_4_6;MONO;DISABLE_CAS_USE</DefineConstants>\r
- <ErrorReport>prompt</ErrorReport>\r
- <WarningLevel>4</WarningLevel>\r
- </PropertyGroup>\r
- <!-- Set AddAdditionalExplicitAssemblyReferences to false, otherwise if targetting .NET4.0, \r
- Microsoft.NETFramework.props will force a dependency on the assembly System.Core. This\r
- is a problem to compile the Mono mscorlib.dll -->\r
- <PropertyGroup>\r
- <AddAdditionalExplicitAssemblyReferences>false</AddAdditionalExplicitAssemblyReferences>\r
- </PropertyGroup>\r
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />\r
- <ItemGroup>\r
- <Compile Include="AssemblyInfo.cs" />\r
- <Compile Include="TypeForwarders.cs" />\r </ItemGroup>\r
- <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \r
- Other similar extension points exist, see Microsoft.Common.targets.\r
- <Target Name="BeforeBuild">\r
- </Target>\r
- <Target Name="AfterBuild">\r
- </Target>\r
- -->\r
- <PropertyGroup>\r
- <PreBuildEvent Condition=" '$(OS)' != 'Windows_NT' ">
-
- </PreBuildEvent>\r
- <PreBuildEvent Condition=" '$(OS)' == 'Windows_NT' ">\r
-\r
- </PreBuildEvent>\r
- <PostBuildEvent Condition=" '$(OS)' != 'Windows_NT' ">
-
- </PostBuildEvent>\r
- <PostBuildEvent Condition=" '$(OS)' == 'Windows_NT' ">\r
-\r
- </PostBuildEvent>\r
- </PropertyGroup>\r
- <ItemGroup>\r
- <ProjectReference Include="../../corlib/corlib-net_4_x.csproj">\r
- <Project>{2CA6026B-2DC8-4C4C-A12C-1E8234049DB7}</Project>\r
- <Name>corlib-net_4_x</Name>\r
- </ProjectReference>\r
- <ProjectReference Include="../../System/System-net_4_x.csproj">\r
- <Project>{2762E921-91A8-4C87-91E9-BA628013F753}</Project>\r
- <Name>System-net_4_x</Name>\r
- </ProjectReference>\r
- </ItemGroup>\r
- <ItemGroup>\r
- <Folder Include="Properties\" />\r
- </ItemGroup>\r
-</Project>\r
-
+++ /dev/null
-MCS_BUILD_DIR = ../../../build
-
-thisdir = class/Facades/System.Private.CoreLib.InteropServices
-SUBDIRS =
-include $(MCS_BUILD_DIR)/rules.make
-
-LIBRARY_SUBDIR = Facades
-LIBRARY_INSTALL_DIR = $(mono_libdir)/mono/$(FRAMEWORK_VERSION)/Facades
-
-LIBRARY = System.Private.CoreLib.InteropServices.dll
-
-KEY_FILE = ../../msfinal.pub
-SIGN_FLAGS = /delaysign /keyfile:$(KEY_FILE) /nowarn:1616,1699
-LIB_REFS = System
-LIB_MCS_FLAGS = $(SIGN_FLAGS)
-
-PLATFORM_DEBUG_FLAGS =
-
-NO_TEST = yes
-
-include $(MCS_BUILD_DIR)/library.make
-
-
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>\r
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
- <PropertyGroup>\r
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>\r
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>\r
- <ProductVersion>9.0.30729</ProductVersion>\r
- <SchemaVersion>2.0</SchemaVersion>\r
- <ProjectGuid>{BE4A05DF-5630-4E80-9521-7B4216229A60}</ProjectGuid>\r
- <OutputType>Library</OutputType>\r
- <NoWarn>1699,1616,1699</NoWarn>\r
- <OutputPath>./../../../class/lib/net_4_x/Facades</OutputPath>\r
- <NoStdLib>True</NoStdLib>\r
- <NoConfig>True</NoConfig>\r
- \r
- <AppDesignerFolder>Properties</AppDesignerFolder>\r
- <RootNamespace>\r
- </RootNamespace>\r
- <AssemblyName>System.Private.CoreLib.InteropServices</AssemblyName>\r
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>\r
- <FileAlignment>512</FileAlignment>\r
- </PropertyGroup>\r
- <PropertyGroup>\r
- <SignAssembly>true</SignAssembly>\r
- <DelaySign>true</DelaySign>\r
- </PropertyGroup>\r
- <PropertyGroup>\r
- <AssemblyOriginatorKeyFile>../../msfinal.pub</AssemblyOriginatorKeyFile>\r
- </PropertyGroup>\r
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">\r
- <DebugSymbols>true</DebugSymbols>\r
- <DebugType>full</DebugType>\r
- <NoWarn>1699,1616,1699</NoWarn>\r
- <Optimize>false</Optimize>\r
- <DefineConstants>DEBUG;TRACE;NET_4_0;NET_4_5;NET_4_6;MONO;DISABLE_CAS_USE</DefineConstants>\r
- <ErrorReport>prompt</ErrorReport>\r
- <WarningLevel>4</WarningLevel>\r
- </PropertyGroup>\r
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">\r
- <DebugType>pdbonly</DebugType>\r
- <NoWarn>1699,1616,1699</NoWarn>\r
- <Optimize>true</Optimize>\r
- <DefineConstants>NET_4_0;NET_4_5;NET_4_6;MONO;DISABLE_CAS_USE</DefineConstants>\r
- <ErrorReport>prompt</ErrorReport>\r
- <WarningLevel>4</WarningLevel>\r
- </PropertyGroup>\r
- <!-- Set AddAdditionalExplicitAssemblyReferences to false, otherwise if targetting .NET4.0, \r
- Microsoft.NETFramework.props will force a dependency on the assembly System.Core. This\r
- is a problem to compile the Mono mscorlib.dll -->\r
- <PropertyGroup>\r
- <AddAdditionalExplicitAssemblyReferences>false</AddAdditionalExplicitAssemblyReferences>\r
- </PropertyGroup>\r
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />\r
- <ItemGroup>\r
- <Compile Include="AssemblyInfo.cs" />\r
- <Compile Include="TypeForwarders.cs" />\r </ItemGroup>\r
- <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \r
- Other similar extension points exist, see Microsoft.Common.targets.\r
- <Target Name="BeforeBuild">\r
- </Target>\r
- <Target Name="AfterBuild">\r
- </Target>\r
- -->\r
- <PropertyGroup>\r
- <PreBuildEvent Condition=" '$(OS)' != 'Windows_NT' ">\r
-\r
- </PreBuildEvent>\r
- <PreBuildEvent Condition=" '$(OS)' == 'Windows_NT' ">\r
-\r
- </PreBuildEvent>\r
-\r
- <PostBuildEvent Condition=" '$(OS)' != 'Windows_NT' ">\r
-\r
- </PostBuildEvent>\r
- <PostBuildEvent Condition=" '$(OS)' == 'Windows_NT' ">\r
-\r
- </PostBuildEvent>\r
- </PropertyGroup>\r
- <ItemGroup>\r
- <ProjectReference Include="../../corlib/corlib-net_4_x.csproj">\r
- <Project>{2CA6026B-2DC8-4C4C-A12C-1E8234049DB7}</Project>\r
- <Name>corlib-net_4_x</Name>\r
- </ProjectReference>\r
- <ProjectReference Include="../../corlib/corlib-net_4_x.csproj">\r
- <Project>{2CA6026B-2DC8-4C4C-A12C-1E8234049DB7}</Project>\r
- <Name>corlib-net_4_x</Name>\r
- </ProjectReference>\r
- <ProjectReference Include="../../System/System-net_4_x.csproj">\r
- <Project>{2762E921-91A8-4C87-91E9-BA628013F753}</Project>\r
- <Name>System-net_4_x</Name>\r
- </ProjectReference>\r
- </ItemGroup>\r
- <ItemGroup>\r
- <Folder Include="Properties\" />\r
- </ItemGroup>\r
-</Project>\r
-
+++ /dev/null
-TypeForwarders.cs
-AssemblyInfo.cs
-
+++ /dev/null
-//
-// Copyright (c) 2015 Xamarin Inc. (http://www.xamarin.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.InteropServices.CallingConvention))]
-[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.InteropServices.DllImportAttribute))]
-[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.InteropServices.GCHandle))]
-[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.InteropServices.GCHandleType))]
-[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.InteropServices.SafeHandle))]
-
-//Missing: [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(Internal.Reflection.Execution.PayForPlayExperience.MissingMetadataExceptionCreator))]
-//Missing: [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.CompilerServices.DependencyReductionTypeRemoved))]
-//Missing: [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.CompilerServices.PreInitializedAttribute))]
-//Missing: [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.InteropServices.InteropExtensions))]
-//Missing: [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.InteropServices.NativeCallableAttributes))]
-//Missing: [assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.RuntimeImportAttribute))]
System.Security.Cryptography.Hashing.Algorithms System.Security.Cryptography.RSA System.Security.Cryptography.RandomNumberGenerator \
System.Security.Principal.Windows System.Threading.Thread System.Threading.ThreadPool \
System.Xml.XPath System.Xml.XmlDocument System.Xml.Xsl.Primitives Microsoft.Win32.Registry.AccessControl System.Diagnostics.StackTrace System.Globalization.Extensions \
-System.IO.FileSystem.AccessControl System.Private.CoreLib.InteropServices System.Reflection.TypeExtensions \
+System.IO.FileSystem.AccessControl System.Reflection.TypeExtensions \
System.Security.SecureString System.Threading.AccessControl System.Threading.Overlapped System.Xml.XPath.XDocument \
System.Security.Cryptography.Primitives System.Text.Encoding.CodePages System.IO.FileSystem.Watcher \
System.Security.Cryptography.ProtectedData System.ServiceProcess.ServiceController System.IO.Pipes
System.IO.Pipes/AnonymousPipeServerStream.cs
System.IO.Pipes/NamedPipeClientStream.cs
System.IO.Pipes/NamedPipeServerStream.cs
+System.IO.Pipes/PipeAccessRights.cs
+System.IO.Pipes/PipeAccessRule.cs
+System.IO.Pipes/PipeAuditRule.cs
System.IO.Pipes/PipeDirection.cs
System.IO.Pipes/PipeInterfaces.cs
System.IO.Pipes/PipeOptions.cs
+System.IO.Pipes/PipeSecurity.cs
System.IO.Pipes/PipeStream.cs
+System.IO.Pipes/PipeStreamImpersonationWorker.cs
System.IO.Pipes/PipeTransmissionMode.cs
ReferenceSources/SR.cs
#include common_System.Core.dll.sources
#include dynamic_System.Core.dll.sources
-System.IO.Pipes/PipeAccessRights.cs
-System.IO.Pipes/PipeAccessRule.cs
-System.IO.Pipes/PipeAuditRule.cs
-System.IO.Pipes/PipeSecurity.cs
-System.IO.Pipes/PipeStreamImpersonationWorker.cs
System.IO.Pipes/PipeUnix.cs
System.IO.Pipes/PipeWin32.cs
}
web_request.Timeout = (int) timeout.TotalMilliseconds;
+ web_request.KeepAlive = httpbe.KeepAliveEnabled;
// There is no SOAP Action/To header when AddressingVersion is None.
if (message.Version.Envelope.Equals (EnvelopeVersion.Soap11) ||
newWebHeaders.Add(headerName,webHeaders[headerName]);
}
- webHeaders = newWebHeaders;
+ this.webHeaders = newWebHeaders;
}
}
static class AppContextSwitches {
public static readonly bool ThrowExceptionIfDisposedCancellationTokenSource = true;
public static readonly bool SetActorAsReferenceWhenCopyingClaimsIdentity = false;
+ public static readonly bool NoAsyncCurrentCulture = false;
}
}
\ No newline at end of file
const int CalendarTypeBits = 8;
const string MSG_READONLY = "This instance is read only";
+
+ static volatile CultureInfo s_DefaultThreadCurrentUICulture;
+ static volatile CultureInfo s_DefaultThreadCurrentCulture;
public static CultureInfo InvariantCulture {
get {
return Thread.CurrentThread.CurrentCulture;
}
set {
- throw new NotImplementedException ();
+ Thread.CurrentThread.CurrentCulture = value;
}
}
return Thread.CurrentThread.CurrentUICulture;
}
set {
- throw new NotImplementedException ();
+ Thread.CurrentThread.CurrentUICulture = value;
}
}
}
}
- internal void CheckNeutral ()
+ void CheckNeutral ()
{
}
public static CultureInfo DefaultThreadCurrentCulture {
get {
- return Thread.default_culture;
+ return s_DefaultThreadCurrentCulture;
}
set {
- Thread.default_culture = value;
+ s_DefaultThreadCurrentCulture = value;
}
}
public static CultureInfo DefaultThreadCurrentUICulture {
get {
- return Thread.default_ui_culture;
+ return s_DefaultThreadCurrentUICulture;
}
set {
- Thread.default_ui_culture = value;
+ s_DefaultThreadCurrentUICulture = value;
}
}
}
}
+ internal static CultureInfo UserDefaultUICulture {
+ get {
+ return ConstructCurrentUICulture ();
+ }
+ }
+
+ internal static CultureInfo UserDefaultCulture {
+ get {
+ return ConstructCurrentCulture ();
+ }
+ }
+
+
#region reference sources
// TODO:
internal static readonly bool IsTaiwanSku;
return true;
}
+ internal static bool VerifyCultureName(CultureInfo culture, bool throwException) {
+ Contract.Assert(culture!=null, "[CultureInfo.VerifyCultureName]culture!=null");
+
+ //If we have an instance of one of our CultureInfos, the user can't have changed the
+ //name and we know that all names are valid in files.
+ if (!culture.m_isInherited) {
+ return true;
+ }
+
+ return VerifyCultureName(culture.Name, throwException);
+
+ }
+
#endregion
}
}
buffer = EncoderFallback.CreateFallbackBuffer ();
if (Char.IsSurrogate (ch) && count > 1 &&
Char.IsSurrogate (chars [charIndex]))
- buffer.Fallback (ch, chars [charIndex], charIndex++ - 1);
+ {
+ buffer.Fallback (ch, chars [charIndex], charIndex - 1);
+ charIndex++;
+ count--;
+ }
else
buffer.Fallback (ch, charIndex - 1);
if (fallback_chars == null || fallback_chars.Length < buffer.Remaining)
IPrincipal principal;
int principal_version;
- bool current_culture_set;
- bool current_ui_culture_set;
- CultureInfo current_culture;
- CultureInfo current_ui_culture;
// the name of current_thread is
// important because they are used by the runtime.
[ThreadStatic]
static Thread current_thread;
- static internal CultureInfo default_culture;
- static internal CultureInfo default_ui_culture;
-
// can be both a ThreadStart and a ParameterizedThreadStart
private MulticastDelegate m_Delegate;
}
}
- //[MethodImplAttribute (MethodImplOptions.InternalCall)]
- //private static extern int current_lcid ();
-
- public CultureInfo CurrentCulture {
- get {
- CultureInfo culture = current_culture;
- if (current_culture_set && culture != null)
- return culture;
-
- if (default_culture != null)
- return default_culture;
-
- current_culture = culture = CultureInfo.ConstructCurrentCulture ();
- return culture;
- }
-
- [SecurityPermission (SecurityAction.Demand, ControlThread=true)]
- set {
- if (value == null)
- throw new ArgumentNullException ("value");
-
- value.CheckNeutral ();
- current_culture = value;
- current_culture_set = true;
- }
- }
-
- public CultureInfo CurrentUICulture {
- get {
- CultureInfo culture = current_ui_culture;
- if (current_ui_culture_set && culture != null)
- return culture;
-
- if (default_ui_culture != null)
- return default_ui_culture;
-
- current_ui_culture = culture = CultureInfo.ConstructCurrentUICulture ();
- return culture;
- }
-
- set {
- if (value == null)
- throw new ArgumentNullException ("value");
- current_ui_culture = value;
- current_ui_culture_set = true;
- }
- }
-
public bool IsThreadPoolThread {
get {
return IsThreadPoolThreadInternal;
return ManagedThreadId;
}
- internal CultureInfo GetCurrentUICultureNoAppX ()
- {
- return CultureInfo.CurrentUICulture;
- }
-
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void GetStackTraces (out Thread[] threads, out object[] stack_frames);
}
}
#else
+ public static string GetEnvironmentVariable (string variable, EnvironmentVariableTarget target)
+ {
+ if (target == EnvironmentVariableTarget.Process)
+ return GetEnvironmentVariable (variable);
+
+ return null;
+ }
+
+ public static IDictionary GetEnvironmentVariables (EnvironmentVariableTarget target)
+ {
+ if (target == EnvironmentVariableTarget.Process)
+ return GetEnvironmentVariables ();
+
+ return (IDictionary)new Hashtable ();
+ }
+
public static void SetEnvironmentVariable (string variable, string value)
{
if (variable == null)
InternalSetEnvironmentVariable (variable, value);
}
+
+ public static void SetEnvironmentVariable (string variable, string value, EnvironmentVariableTarget target)
+ {
+ if (target == EnvironmentVariableTarget.Process)
+ SetEnvironmentVariable (variable, value);
+
+ // other targets ignored
+ }
#endif
[MethodImplAttribute (MethodImplOptions.InternalCall)]
internal static extern void InternalSetEnvironmentVariable (string variable, string value);
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
+using System.Threading.Tasks;
using NUnit.Framework;
}
[Test]
- public void DefaultThreadCurrentCultureAndNumberFormaters () {
+ public void DefaultThreadCurrentCultureIsIgnoredWhenCultureFlowsToThread ()
+ {
string us_str = null;
string br_str = null;
+
var thread = new Thread (() => {
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
us_str = 100000.ToString ("C");
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("pt-BR");
br_str = 100000.ToString ("C");
});
+
+ var expected = 100000.ToString ("C");
+
thread.Start ();
thread.Join ();
CultureInfo.DefaultThreadCurrentCulture = null;
- Assert.AreEqual ("$100,000.00", us_str, "#1");
- Assert.AreEqual ("R$ 100.000,00", br_str, "#2");
+ Assert.AreEqual (expected, us_str, "#1");
+ Assert.AreEqual (expected, br_str, "#2");
+ }
+
+ [Test]
+ public void FlowCultureInfoFromParentThreadSinceNet46 ()
+ {
+ Func<Task> f = async () => {
+ Thread.CurrentThread.CurrentUICulture = new CultureInfo ("pt-BR");
+ await Task.Yield ();
+ Assert.AreEqual ("pt-BR", Thread.CurrentThread.CurrentUICulture.Name);
+ };
+
+ f ().Wait ();
}
}
}
Assert.AreEqual (testchars [i], (char) bytes [i]);
}
+ [Test] // Test GetBytes(string)
+ public void TestGetBytes7 ()
+ {
+ var latin1_encoding = Encoding.GetEncoding ("latin1");
+
+ var expected = new byte [] { 0x3F, 0x20, 0x3F, 0x20, 0x3F };
+ var actual = latin1_encoding.GetBytes("\u24c8 \u2075 \u221e"); // normal replacement
+ Assert.AreEqual (expected, actual, "#1");
+
+ expected = new byte [] { 0x3F, 0x3F };
+ actual = latin1_encoding.GetBytes("\ud83d\ude0a"); // surrogate pair replacement
+ Assert.AreEqual (expected, actual, "#2");
+
+ expected = new byte [] { 0x3F, 0x3F, 0x20 };
+ actual = latin1_encoding.GetBytes("\ud83d\ude0a "); // surrogate pair replacement
+ Assert.AreEqual (expected, actual, "#3");
+
+ expected = new byte [] { 0x20, 0x20, 0x3F, 0x3F, 0x20, 0x20 };
+ actual = latin1_encoding.GetBytes(" \ud83d\ude0a "); // surrogate pair replacement
+ Assert.AreEqual (expected, actual, "#4");
+
+ expected = new byte [] { 0x20, 0x20, 0x3F, 0x3F, 0x20, 0x20 };
+ actual = latin1_encoding.GetBytes(" \ud834\udd1e "); // surrogate pair replacement
+ Assert.AreEqual (expected, actual, "#5");
+
+ expected = new byte [] { 0x41, 0x42, 0x43, 0x00, 0x41, 0x42, 0x43 };
+ actual = latin1_encoding.GetBytes("ABC\0ABC"); // embedded zero byte not replaced
+ Assert.AreEqual (expected, actual, "#6");
+
+ expected = new byte [] { 0x20, 0x20, 0x3F, 0x20, 0x20 };
+ actual = latin1_encoding.GetBytes(" \ud834 "); // invalid surrogate pair replacement
+ Assert.AreEqual (expected, actual, "#7");
+ }
+
[Test] // Test GetChars(byte[])
public void TestGetChars1 ()
{
Assert.AreEqual (string.Empty, encoding.GetString (new byte [0], 0, 0), "#2");
}
+ [Test]
+ [ExpectedException (typeof (EncoderFallbackException))]
+ public void EncoderFallback ()
+ {
+ Encoding e = Encoding.ASCII.Clone () as Encoding;
+ e.EncoderFallback = new EncoderExceptionFallback ();
+ e.GetBytes ("\u24c8");
+ }
+
[Test]
[ExpectedException (typeof (DecoderFallbackException))]
public void DecoderFallback ()
--- /dev/null
+//
+// Latin1EncodingTest.cs
+//
+// Author:
+// Alexander Köplinger (alexander.koeplinger@xamarin.com)
+//
+// Copyright (C) 2016 Xamarin, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Text;
+
+using NUnit.Framework;
+using NUnit.Framework.Constraints;
+
+#if !MOBILE
+using NUnit.Framework.SyntaxHelpers;
+#endif
+
+namespace MonoTests.System.Text
+{
+ [TestFixture]
+ public class Latin1EncodingTest
+ {
+ private char[] testchars;
+ private byte[] testbytes;
+
+ [SetUp]
+ public void SetUp ()
+ {
+ testchars = new char[4];
+ testchars[0] = 'T';
+ testchars[1] = 'e';
+ testchars[2] = 's';
+ testchars[3] = 't';
+ testbytes = new byte[4];
+ testbytes[0] = (byte) 'T';
+ testbytes[1] = (byte) 'e';
+ testbytes[2] = (byte) 's';
+ testbytes[3] = (byte) 't';
+ }
+
+ [Test]
+ public void IsBrowserDisplay ()
+ {
+ Assert.IsTrue (Encoding.GetEncoding ("latin1").IsBrowserDisplay);
+ }
+
+ [Test]
+ public void IsBrowserSave ()
+ {
+ Assert.IsTrue (Encoding.GetEncoding ("latin1").IsBrowserSave);
+ }
+
+ [Test]
+ public void IsMailNewsDisplay ()
+ {
+ Assert.IsTrue (Encoding.GetEncoding ("latin1").IsMailNewsDisplay);
+ }
+
+ [Test]
+ public void IsMailNewsSave ()
+ {
+ Assert.IsTrue (Encoding.GetEncoding ("latin1").IsMailNewsSave);
+ }
+
+ [Test] // Test GetBytes(char[])
+ public void TestGetBytes1 ()
+ {
+ Encoding latin1_encoding = Encoding.GetEncoding ("latin1");
+ byte[] bytes = latin1_encoding.GetBytes(testchars);
+ for (int i = 0; i < testchars.Length; i++)
+ Assert.AreEqual (testchars[i], (char) bytes[i]);
+ }
+
+ [Test] // Test GetBytes(char[], int, int)
+ public void TestGetBytes2 ()
+ {
+ Encoding latin1_encoding = Encoding.GetEncoding ("latin1");
+ byte[] bytes = latin1_encoding.GetBytes(testchars, 1, 1);
+ Assert.AreEqual (1, bytes.Length, "#1");
+ Assert.AreEqual (testchars [1], (char) bytes [0], "#2");
+ }
+
+ [Test] // Test non-Latin1 char in char[]
+ public void TestGetBytes3 ()
+ {
+ Encoding latin1_encoding = Encoding.GetEncoding ("latin1");
+ testchars[2] = (char) 0x100;
+ byte[] bytes = latin1_encoding.GetBytes(testchars);
+ Assert.AreEqual ('T', (char) bytes [0], "#1");
+ Assert.AreEqual ('e', (char) bytes [1], "#2");
+ Assert.AreEqual ('?', (char) bytes [2], "#3");
+ Assert.AreEqual ('t', (char) bytes [3], "#4");
+ }
+
+ [Test] // Test GetBytes(char[], int, int, byte[], int)
+ public void TestGetBytes4 ()
+ {
+ Encoding latin1_encoding = Encoding.GetEncoding ("latin1");
+ byte[] bytes = new Byte[1];
+ int cnt = latin1_encoding.GetBytes(testchars, 1, 1, bytes, 0);
+ Assert.AreEqual (1, cnt, "#1");
+ Assert.AreEqual (testchars [1], (char) bytes [0], "#2");
+ }
+
+ [Test] // Test GetBytes(string, int, int, byte[], int)
+ public void TestGetBytes5 ()
+ {
+ Encoding latin1_encoding = Encoding.GetEncoding ("latin1");
+ byte[] bytes = new Byte[1];
+ int cnt = latin1_encoding.GetBytes("Test", 1, 1, bytes, 0);
+ Assert.AreEqual ('e', (char) bytes [0], "#1");
+ }
+
+ [Test] // Test GetBytes(string)
+ public void TestGetBytes6 ()
+ {
+ Encoding latin1_encoding = Encoding.GetEncoding ("latin1");
+ byte[] bytes = latin1_encoding.GetBytes("Test");
+ for (int i = 0; i < testchars.Length; i++)
+ Assert.AreEqual (testchars [i], (char) bytes [i]);
+ }
+
+ [Test] // Test GetBytes(string)
+ public void TestGetBytes7 ()
+ {
+ var latin1_encoding = Encoding.GetEncoding ("latin1");
+
+ var expected = new byte [] { 0x3F, 0x20, 0x3F, 0x20, 0x3F };
+ var actual = latin1_encoding.GetBytes("\u24c8 \u2075 \u221e"); // normal replacement
+ Assert.AreEqual (expected, actual, "#1");
+
+ expected = new byte [] { 0x3F, 0x3F };
+ actual = latin1_encoding.GetBytes("\ud83d\ude0a"); // surrogate pair replacement
+ Assert.AreEqual (expected, actual, "#2");
+
+ expected = new byte [] { 0x3F, 0x3F, 0x20 };
+ actual = latin1_encoding.GetBytes("\ud83d\ude0a "); // surrogate pair replacement
+ Assert.AreEqual (expected, actual, "#3");
+
+ expected = new byte [] { 0x20, 0x20, 0x3F, 0x3F, 0x20, 0x20 };
+ actual = latin1_encoding.GetBytes(" \ud83d\ude0a "); // surrogate pair replacement
+ Assert.AreEqual (expected, actual, "#4");
+
+ expected = new byte [] { 0x20, 0x20, 0x3F, 0x3F, 0x20, 0x20 };
+ actual = latin1_encoding.GetBytes(" \ud834\udd1e "); // surrogate pair replacement
+ Assert.AreEqual (expected, actual, "#5");
+
+ expected = new byte [] { 0x41, 0x42, 0x43, 0x00, 0x41, 0x42, 0x43 };
+ actual = latin1_encoding.GetBytes("ABC\0ABC"); // embedded zero byte not replaced
+ Assert.AreEqual (expected, actual, "#6");
+
+ expected = new byte [] { 0x20, 0x20, 0x3F, 0x20, 0x20 };
+ actual = latin1_encoding.GetBytes(" \ud834 "); // invalid surrogate pair replacement
+ Assert.AreEqual (expected, actual, "#7");
+ }
+
+ [Test] // Test GetChars(byte[])
+ public void TestGetChars1 ()
+ {
+ Encoding latin1_encoding = Encoding.GetEncoding ("latin1");
+ char[] chars = latin1_encoding.GetChars(testbytes);
+ for (int i = 0; i < testbytes.Length; i++)
+ Assert.AreEqual (testbytes[i], (byte) chars[i]);
+ }
+
+ [Test] // Test GetChars(byte[], int, int)
+ public void TestGetChars2 ()
+ {
+ Encoding latin1_encoding = Encoding.GetEncoding ("latin1");
+ char[] chars = latin1_encoding.GetChars(testbytes, 1, 1);
+ Assert.AreEqual (1, chars.Length, "#1");
+ Assert.AreEqual (testbytes [1], (byte) chars [0], "#2");
+ }
+
+ [Test] // Test GetChars(byte[], int, int, char[], int)
+ public void TestGetChars4 ()
+ {
+ Encoding latin1_encoding = Encoding.GetEncoding ("latin1");
+ char[] chars = new char[1];
+ int cnt = latin1_encoding.GetChars(testbytes, 1, 1, chars, 0);
+ Assert.AreEqual (1, cnt, "#1");
+ Assert.AreEqual (testbytes [1], (byte) chars [0], "#2");
+ }
+
+ [Test] // Test GetString(char[])
+ public void TestGetString1 ()
+ {
+ Encoding latin1_encoding = Encoding.GetEncoding ("latin1");
+ string str = latin1_encoding.GetString(testbytes);
+ Assert.AreEqual ("Test", str);
+ }
+
+ [Test] // Test GetString(char[], int, int)
+ public void TestGetString2 ()
+ {
+ Encoding latin1_encoding = Encoding.GetEncoding ("latin1");
+ string str = latin1_encoding.GetString(testbytes, 1, 2);
+ Assert.AreEqual ("es", str);
+ }
+
+ [Test] // Test Decoder
+ public void TestDecoder ()
+ {
+ Encoding latin1_encoding = Encoding.GetEncoding ("latin1");
+ char[] chars = new char[1];
+ int cnt = latin1_encoding.GetDecoder().GetChars(testbytes, 1, 1, chars, 0);
+ Assert.AreEqual (1, cnt, "#1");
+ Assert.AreEqual (testbytes [1], (byte) chars [0], "#2");
+ }
+
+ [Test] // Test Decoder
+ public void TestEncoder ()
+ {
+ Encoding latin1_encoding = Encoding.GetEncoding ("latin1");
+ byte[] bytes = new Byte[1];
+ int cnt = latin1_encoding.GetEncoder().GetBytes(testchars, 1, 1, bytes, 0, false);
+ Assert.AreEqual (1, cnt, "#1");
+ Assert.AreEqual (testchars [1], (char) bytes [0], "#2");
+ }
+
+ [Test]
+ public void TestZero ()
+ {
+ Encoding encoding = Encoding.GetEncoding ("latin1");
+ Assert.AreEqual (string.Empty, encoding.GetString (new byte [0]), "#1");
+ Assert.AreEqual (string.Empty, encoding.GetString (new byte [0], 0, 0), "#2");
+ }
+
+ [Test]
+ [ExpectedException (typeof (EncoderFallbackException))]
+ public void EncoderFallback ()
+ {
+ Encoding e = Encoding.GetEncoding ("latin1").Clone () as Encoding;
+ e.EncoderFallback = new EncoderExceptionFallback ();
+ e.GetBytes ("\u24c8");
+ }
+
+ [Test]
+ // [ExpectedException (typeof (ArgumentException))]
+ public void DecoderFallback2 ()
+ {
+ var bytes = new byte[] {
+ 0x30, 0xa0, 0x31, 0xa8
+ };
+ var enc = (Encoding)Encoding.GetEncoding ("latin1").Clone ();
+ enc.DecoderFallback = new TestFallbackDecoder ();
+
+ var chars = new char [7];
+ var ret = enc.GetChars (bytes, 0, bytes.Length, chars, 0);
+ Console.WriteLine (ret);
+
+ for (int i = 0; i < chars.Length; i++) {
+ Console.Write ("{0:x2} ", (int)chars [i]);
+ }
+ Console.WriteLine ();
+ }
+
+ [Test]
+ public void DecoderFallback3 ()
+ {
+ var bytes = new byte[] {
+ 0x30, 0xa0, 0x31, 0xa8
+ };
+ var enc = (Encoding)Encoding.GetEncoding ("latin1").Clone ();
+ enc.DecoderFallback = new TestFallbackDecoder ();
+
+ var chars = new char[] { '9', '8', '7', '6', '5' };
+ var ret = enc.GetChars (bytes, 0, bytes.Length, chars, 0);
+
+ Assert.That (ret, Is.EqualTo (4), "ret");
+ Assert.That (chars [0], Is.EqualTo ('0'), "chars[0]");
+ Assert.That (chars [1], Is.EqualTo ((char)0xA0), "chars[1]");
+ Assert.That (chars [2], Is.EqualTo ('1'), "chars[2]");
+ Assert.That (chars [3], Is.EqualTo ((char)0xA8), "chars[3]");
+ Assert.That (chars [4], Is.EqualTo ('5'), "chars[4]");
+ }
+
+ class TestFallbackDecoder : DecoderFallback {
+ const int count = 2;
+
+ public override int MaxCharCount {
+ get { return count; }
+ }
+
+ public override DecoderFallbackBuffer CreateFallbackBuffer ()
+ {
+ return new Buffer ();
+ }
+
+ class Buffer : DecoderFallbackBuffer {
+ char[] queue;
+ int index;
+
+ public override int Remaining {
+ get {
+ return queue.Length - index;
+ }
+ }
+
+ public override char GetNextChar ()
+ {
+ return index < queue.Length ? queue [index++] : '\0';
+ }
+
+ public override bool Fallback (byte[] bytes, int unused)
+ {
+ queue = new char[bytes.Length * count];
+ index = 0;
+ for (int i = 0; i < bytes.Length; i++) {
+ for (int j = 0; j < count; j++)
+ queue [index++] = (char)(bytes [i]+j);
+ }
+ return true;
+ }
+
+ public override bool MovePrevious ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ public override void Reset ()
+ {
+ base.Reset ();
+ }
+ }
+ }
+
+ }
+}
var token = source.Token;
var evt = new ManualResetEventSlim ();
bool result = false;
- bool thrown = false;
- var task = Task.Factory.StartNew (() => evt.Wait (100));
+ var task = Task.Factory.StartNew (() => { Assert.IsTrue (evt.Wait (2000), "#1"); });
var cont = task.ContinueWith (t => result = true, token, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
source.Cancel();
evt.Set ();
- task.Wait (100);
+ Assert.IsTrue (task.Wait (2000), "#2");
try {
- cont.Wait (100);
- } catch (Exception ex) {
- thrown = true;
+ Assert.IsFalse (cont.Wait (4000), "#3");
+ } catch (AggregateException ex) {
}
- Assert.IsTrue (task.IsCompleted);
- Assert.IsTrue (cont.IsCanceled);
- Assert.IsFalse (result);
- Assert.IsTrue (thrown);
+ Assert.IsTrue (task.IsCompleted, "#4");
+ Assert.IsTrue (cont.IsCanceled, "#5");
+ Assert.IsFalse (result, "#6");
}
[Test]
var del = Delegate.Remove (del1, del2);
}
+ [Test]
+ [ExpectedException (typeof (ArgumentException))]
+ public void CreateDelegateThrowsAnArgumentExceptionWhenCalledWithAnOpenGeneric()
+ {
+ var m = GetType().GetMethod("AnyGenericMethod");
+ Delegate.CreateDelegate(typeof(Action), this, m);
+ }
+
+ public void AnyGenericMethod<T>()
+ {
+ }
+
static bool Int32D2 (int x, int y)
{
return (x & y) == y;
System.Text/EncodingTest.cs
System.Text/EncodingTester.cs
System.Text/EncodingInfoTest.cs
+System.Text/Latin1EncodingTest.cs
System.Text/StringBuilderTest.cs
System.Text/TestEncoding.cs
System.Text/UnicodeEncodingTest.cs
dls.Store.SetData(slot, data);
}
-#if !MONO
// #threadCultureInfo
//
}
#if! FEATURE_LEAK_CULTURE_INFO
+
+#if MONO
+ static void nativeInitCultureAccessors()
+ {
+ m_CurrentCulture = CultureInfo.ConstructCurrentCulture ();
+ m_CurrentUICulture = CultureInfo.ConstructCurrentUICulture ();
+ }
+#else
[System.Security.SecurityCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
private static extern void nativeInitCultureAccessors();
#endif
+#endif
+
+#if !MONO
/*=============================================================*/
if (res && errors != ec.Report.Errors)
return null;
+ if (block.IsAsync && block.Original.ParametersBlock.HasCapturedThis && ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.block.IsAsync) {
+ //
+ // We'll do ldftn to load the fabricated m_X method but
+ // because we are inside struct the method can be hoisted
+ // anywhere in the parent scope
+ //
+ ec.CurrentBlock.ParametersBlock.HasReferenceToStoreyForInstanceLambdas = true;
+ }
+
return res ? this : null;
}
parent = storey = sm;
}
}
+ } else if (src_block.ParametersBlock.HasReferenceToStoreyForInstanceLambdas) {
+ src_block.ParametersBlock.StateMachine.AddParentStoreyReference (ec, storey);
}
modifiers = storey != null ? Modifiers.INTERNAL : Modifiers.PRIVATE;
AwaitBlock = 1 << 13,
FinallyBlock = 1 << 14,
CatchBlock = 1 << 15,
+ HasReferenceToStoreyForInstanceLambdas = 1 << 16,
Iterator = 1 << 20,
NoFlowAnalysis = 1 << 21,
InitializationEmitted = 1 << 22
break;
}
}
-
+
//
// We are the first storey on path and 'this' has to be hoisted
//
//
// If we are state machine with no parent. We can hook into parent without additional
- // reference and capture this directly
+ // reference and capture this directly
//
ExplicitBlock parent_storey_block = pb;
while (parent_storey_block.Parent != null) {
#region Properties
+ public bool HasReferenceToStoreyForInstanceLambdas {
+ get {
+ return (flags & Flags.HasReferenceToStoreyForInstanceLambdas) != 0;
+ }
+ set {
+ flags = value ? flags | Flags.HasReferenceToStoreyForInstanceLambdas : flags & ~Flags.HasReferenceToStoreyForInstanceLambdas;
+ }
+ }
+
public bool IsAsync {
get {
return (flags & Flags.HasAsyncModifier) != 0;
--- /dev/null
+using System;
+using System.Threading.Tasks;
+
+class X
+{
+ public static void Main ()
+ {
+ new X ().Test ();
+ }
+
+ void Test ()
+ {
+ object v1 = null;
+
+ Action a = () =>
+ {
+ if (v1 == null)
+ {
+ object v2 = null;
+
+ Action a2 = () =>
+ {
+ Console.WriteLine (v2);
+ };
+
+ Action a3 = async () =>
+ {
+ // This scope needs to access to Scope which can do ldftn on instance method
+ {
+ Func<Task> a4 = async () =>
+ {
+ await Foo ();
+ };
+ }
+
+ await Task.Yield ();
+ };
+
+ a3 ();
+ }
+ };
+
+ a ();
+ }
+
+ async Task Foo ()
+ {
+ await Task.FromResult (1);
+ }
+
+}
\ No newline at end of file
</method>
</type>
</test>
+ <test name="test-async-89.cs">
+ <type name="X">
+ <method name="Void Main()" attrs="150">
+ <size>12</size>
+ </method>
+ <method name="Void Test()" attrs="129">
+ <size>41</size>
+ </method>
+ <method name="System.Threading.Tasks.Task Foo()" attrs="129">
+ <size>33</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ <type name="X+<Test>c__AnonStorey1">
+ <method name="Void <>m__0()" attrs="131">
+ <size>67</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ <type name="X+<Foo>c__async0">
+ <method name="Void MoveNext()" attrs="486">
+ <size>158</size>
+ </method>
+ <method name="Void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)" attrs="486">
+ <size>13</size>
+ </method>
+ </type>
+ <type name="X+<Test>c__AnonStorey1+<Test>c__AnonStorey2">
+ <method name="Void <>m__0()" attrs="131">
+ <size>13</size>
+ </method>
+ <method name="Void <>m__1()" attrs="131">
+ <size>48</size>
+ </method>
+ <method name="System.Threading.Tasks.Task <>m__2()" attrs="131">
+ <size>46</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ <type name="X+<Test>c__AnonStorey1+<Test>c__AnonStorey2+<Test>c__async3">
+ <method name="Void MoveNext()" attrs="486">
+ <size>179</size>
+ </method>
+ <method name="Void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)" attrs="486">
+ <size>13</size>
+ </method>
+ </type>
+ <type name="X+<Test>c__AnonStorey1+<Test>c__AnonStorey2+<Test>c__async4">
+ <method name="Void MoveNext()" attrs="486">
+ <size>167</size>
+ </method>
+ <method name="Void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)" attrs="486">
+ <size>13</size>
+ </method>
+ </type>
+ </test>
<test name="test-cls-00.cs">
<type name="CLSCLass_6">
<method name="Void add_Disposed(Delegate)" attrs="2182">
using System.IO;
using System.Collections;
using System.Collections.Generic;
-using System.Reflection;
-using System.Reflection.Emit;
using System.Security.Cryptography;
using System.Text;
using System.Configuration.Assemblies;
+using IKVM.Reflection;
+using IKVM.Reflection.Emit;
using Mono.Security.Cryptography;
-using IKR = IKVM.Reflection;
namespace Mono.AssemblyLinker
{
No
}
+ public enum Platform {
+ AnyCPU,
+ AnyCPU32Preferred,
+ Arm,
+ X86,
+ X64,
+ IA64
+ }
+
public class AssemblyLinker {
ArrayList inputFiles = new ArrayList ();
string entryPoint;
string win32IconFile;
string win32ResFile;
+ string title;
+ string description;
+ string company;
+ string product;
+ string copyright;
+ string trademark;
string templateFile;
bool isTemplateFile = false;
Target target = Target.Dll;
+ Platform platform = Platform.AnyCPU;
DelaySign delaysign = DelaySign.NotSet;
string keyfile;
string keyname;
string culture;
+ Universe universe;
public static int Main (String[] args) {
return new AssemblyLinker ().DynMain (args);
}
private int DynMain (String[] args) {
- ParseArgs (args);
+ using (universe = new Universe (UniverseOptions.MetadataOnly)) {
+ universe.LoadFile (typeof (object).Assembly.Location);
+ ParseArgs (args);
- DoIt ();
+ DoIt ();
- return 0;
+ return 0;
+ }
}
private void ParseArgs (string[] args)
if (realArg.StartsWith ("0x"))
realArg = realArg.Substring (2);
uint val = Convert.ToUInt32 (realArg, 16);
- AddCattr (typeof (AssemblyAlgorithmIdAttribute), typeof (uint), val);
+ AddCattr (typeof (System.Reflection.AssemblyAlgorithmIdAttribute), typeof (uint), val);
} catch (Exception) {
ReportInvalidArgument (opt, arg);
}
case "company":
if (arg == null)
ReportMissingText (opt);
- AddCattr (typeof (AssemblyCompanyAttribute), arg);
+ company = arg;
return true;
case "config":
case "configuration":
if (arg == null)
ReportMissingText (opt);
- AddCattr (typeof (AssemblyConfigurationAttribute), arg);
+ AddCattr (typeof (System.Reflection.AssemblyConfigurationAttribute), arg);
return true;
case "copy":
case "copyright":
if (arg == null)
ReportMissingText (opt);
- AddCattr (typeof (AssemblyCopyrightAttribute), arg);
+ copyright = arg;
return true;
case "c":
case "description":
if (arg == null)
ReportMissingText (opt);
- AddCattr (typeof (AssemblyDescriptionAttribute), arg);
+ description = arg;
return true;
case "e":
if (arg == null)
ReportMissingText (opt);
- AddCattr (typeof (AssemblyFileVersionAttribute), arg);
+ AddCattr (typeof (System.Reflection.AssemblyFileVersionAttribute), arg);
return true;
case "flags":
if (realArg.StartsWith ("0x"))
realArg = realArg.Substring (2);
uint val = Convert.ToUInt32 (realArg, 16);
- AddCattr (typeof (AssemblyFlagsAttribute), typeof (uint), val);
+ AddCattr (typeof (System.Reflection.AssemblyFlagsAttribute), typeof (uint), val);
} catch (Exception) {
ReportInvalidArgument (opt, arg);
}
outFile = arg;
return true;
+ case "platform":
+ if (arg == null)
+ ReportMissingText (opt);
+ switch (arg.ToLowerInvariant ()) {
+ case "arm":
+ platform = Platform.Arm;
+ break;
+ case "anycpu":
+ platform = Platform.AnyCPU;
+ break;
+ case "x86":
+ platform = Platform.X86;
+ break;
+ case "x64":
+ platform = Platform.X64;
+ break;
+ case "itanium":
+ platform = Platform.IA64;
+ break;
+ case "anycpu32bitpreferred":
+ platform = Platform.AnyCPU32Preferred;
+ break;
+ default:
+ ReportInvalidArgument (opt, arg);
+ break;
+ }
+ return true;
+
case "prod":
case "product":
if (arg == null)
ReportMissingText (opt);
- AddCattr (typeof (AssemblyProductAttribute), arg);
+ product = arg;
return true;
case "productv":
case "productversion":
if (arg == null)
ReportMissingText (opt);
- AddCattr (typeof (AssemblyInformationalVersionAttribute), arg);
+ AddCattr (typeof (System.Reflection.AssemblyInformationalVersionAttribute), arg);
return true;
case "t":
case "title":
if (arg == null)
ReportMissingText (opt);
- AddCattr (typeof (AssemblyTitleAttribute), arg);
+ title = arg;
return true;
case "trade":
case "trademark":
if (arg == null)
ReportMissingText (opt);
- AddCattr (typeof (AssemblyTrademarkAttribute), arg);
+ trademark = arg;
return true;
case "v":
Version ();
break;
}
- AddCattr (typeof (AssemblyVersionAttribute), arg);
+ AddCattr (typeof (System.Reflection.AssemblyVersionAttribute), arg);
return true;
case "win32icon":
return command.ToLower ();
}
- private void AddCattr (Type attrType, Type arg, object value) {
- cattrs.Add (new CustomAttributeBuilder (attrType.GetConstructor (new Type [] { arg }), new object [] { value }));
+ private void AddCattr (System.Type attrType, System.Type arg, object value) {
+ var importedAttrType = universe.Import(attrType);
+ var importedArg = universe.Import(arg);
+
+ cattrs.Add (new CustomAttributeBuilder (importedAttrType.GetConstructor (new [] { importedArg }), new [] { value }));
}
- private void AddCattr (Type attrType, object value) {
+ private void AddCattr (System.Type attrType, object value) {
AddCattr (attrType, typeof (string), value);
}
if (isTemplateFile)
aname = ReadCustomAttributesFromTemplateFile (templateFile, aname);
+ if (!String.IsNullOrEmpty (title))
+ AddCattr (typeof (System.Reflection.AssemblyTitleAttribute), title);
+ if (!String.IsNullOrEmpty (description))
+ AddCattr (typeof (System.Reflection.AssemblyDescriptionAttribute), description);
+ if (!String.IsNullOrEmpty (company))
+ AddCattr (typeof (System.Reflection.AssemblyCompanyAttribute), company);
+ if (!String.IsNullOrEmpty (product))
+ AddCattr (typeof (System.Reflection.AssemblyProductAttribute), product);
+ if (!String.IsNullOrEmpty (copyright))
+ AddCattr (typeof (System.Reflection.AssemblyCopyrightAttribute), copyright);
+ if (!String.IsNullOrEmpty (trademark))
+ AddCattr (typeof (System.Reflection.AssemblyTrademarkAttribute), trademark);
+
SetKeyPair (aname);
if (fileName != outFile)
- ab = AppDomain.CurrentDomain.DefineDynamicAssembly (aname, AssemblyBuilderAccess.Save, Path.GetDirectoryName (outFile));
+ ab = universe.DefineDynamicAssembly (aname, AssemblyBuilderAccess.Save, Path.GetDirectoryName (outFile));
else
- ab = AppDomain.CurrentDomain.DefineDynamicAssembly (aname, AssemblyBuilderAccess.Save);
+ ab = universe.DefineDynamicAssembly (aname, AssemblyBuilderAccess.Save);
foreach (CustomAttributeBuilder cb in cattrs)
ab.SetCustomAttribute (cb);
*/
foreach (ModuleInfo mod in inputFiles) {
- MethodInfo mi = typeof (AssemblyBuilder).GetMethod ("AddModule", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
- if (mi == null)
- Report (0, "Cannot add modules on this runtime: try the Mono runtime instead.");
-
if (mod.target != null) {
File.Copy (mod.fileName, mod.target, true);
mod.fileName = mod.target;
if (isAssembly)
ReportWarning (1020, "Ignoring included assembly '" + mod.fileName + "'");
else
- mi.Invoke (ab, new object [] { mod.fileName });
+ ab.__AddModule (universe.OpenRawModule(mod.fileName));
}
/*
MethodInfo mainMethodInfo = null;
try {
- Type mainType = ab.GetType (mainClass);
+ IKVM.Reflection.Type mainType = ab.GetType (mainClass);
if (mainType != null)
mainMethodInfo = mainType.GetMethod (mainMethod);
}
if (win32IconFile != null) {
try {
- MethodInfo mi = typeof (AssemblyBuilder).GetMethod ("DefineIconResource", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
- if (mi == null)
- Report (0, "Cannot embed win32 icons on this runtime: try the Mono runtime instead.");
- mi.Invoke (ab, new object [] { win32IconFile });
+ ab.__DefineIconResource (File.ReadAllBytes (win32IconFile));
}
catch (Exception ex) {
Report (1031, "Error reading icon '" + win32IconFile + "' --" + ex);
}
}
+ ModuleBuilder mainModule = null;
+
foreach (ResourceInfo res in resources) {
if (res.name == null)
res.name = Path.GetFileName (res.fileName);
Report (1046, String.Format ("Resource identifier '{0}' has already been used in this assembly", res.name));
if (res.isEmbedded) {
- MethodInfo mi = typeof (AssemblyBuilder).GetMethod ("EmbedResourceFile", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic,
- null, CallingConventions.Any, new Type [] { typeof (string), typeof (string) }, null);
- if (mi == null)
- Report (0, "Cannot embed resources on this runtime: try the Mono runtime instead.");
- mi.Invoke (ab, new object [] { res.name, res.fileName });
+ if (mainModule == null) {
+ mainModule = ab.DefineDynamicModule (fileName, fileName, false);
+ }
+
+ Stream stream = new MemoryStream (File.ReadAllBytes (res.fileName));
+
+ mainModule.DefineManifestResource (res.name, stream, res.isPrivate ? ResourceAttributes.Private : ResourceAttributes.Public);
}
else {
if (res.target != null) {
}
}
+ PortableExecutableKinds pekind = PortableExecutableKinds.ILOnly;
+ ImageFileMachine machine;
+
+ switch (platform) {
+ case Platform.X86:
+ pekind |= PortableExecutableKinds.Required32Bit;
+ machine = ImageFileMachine.I386;
+ break;
+ case Platform.X64:
+ pekind |= PortableExecutableKinds.PE32Plus;
+ machine = ImageFileMachine.AMD64;
+ break;
+ case Platform.IA64:
+ machine = ImageFileMachine.IA64;
+ break;
+ case Platform.AnyCPU32Preferred:
+ pekind |= PortableExecutableKinds.Preferred32Bit;
+ machine = ImageFileMachine.I386;
+ break;
+ case Platform.Arm:
+ machine = ImageFileMachine.ARM;
+ break;
+ case Platform.AnyCPU:
+ default:
+ machine = ImageFileMachine.I386;
+ break;
+ }
+
try {
- ab.Save (fileName);
+ ab.Save (fileName, pekind, machine);
}
catch (Exception ex) {
Report (1019, "Metadata failure creating assembly -- " + ex);
{
// LAMESPEC: according to MSDN, the template assembly must have a
// strong name but this is not enforced
- const IKR.UniverseOptions options = IKR.UniverseOptions.MetadataOnly;
-
- var universe = new IKR.Universe (options);
var asm = universe.LoadFile (templateFile);
// Create missing assemblies, we don't want to load them!
// Code taken from ikdasm
var names = new HashSet<string> ();
- IKR.AssemblyName[] assembly_refs = asm.ManifestModule.__GetReferencedAssemblies ();
+ AssemblyName[] assembly_refs = asm.ManifestModule.__GetReferencedAssemblies ();
- var resolved_assemblies = new IKR.Assembly [assembly_refs.Length];
+ var resolved_assemblies = new Assembly [assembly_refs.Length];
for (int i = 0; i < resolved_assemblies.Length; i++) {
string name = assembly_refs [i].Name;
keyname = key_name_value;
}
break;
+
+ case "System.Reflection.AssemblyTitleAttribute": {
+ if (title != null)
+ // ignore if specified on command line
+ continue;
+
+ // AssemblyTitleAttribute .ctor(string title)
+ string title_value = (string) attr_data.ConstructorArguments [0].Value;
+
+ if (!String.IsNullOrEmpty (title_value))
+ title = title_value;
+ }
+ break;
+
+ case "System.Reflection.AssemblyDescriptionAttribute": {
+ if (description != null)
+ // ignore if specified on command line
+ continue;
+
+ // AssemblyDescriptionAttribute .ctor(string description)
+ string description_value = (string) attr_data.ConstructorArguments [0].Value;
+
+ if (!String.IsNullOrEmpty (description_value))
+ description = description_value;
+ }
+ break;
+
+ case "System.Reflection.AssemblyProductAttribute": {
+ if (product != null)
+ // ignore if specified on command line
+ continue;
+
+ // AssemblyProductAttribute .ctor(string product)
+ string product_value = (string) attr_data.ConstructorArguments [0].Value;
+
+ if (!String.IsNullOrEmpty (product_value))
+ product = product_value;
+ }
+ break;
+
+ case "System.Reflection.AssemblyCompanyAttribute": {
+ if (company != null)
+ // ignore if specified on command line
+ continue;
+
+ // AssemblyCompanyAttribute .ctor(string company)
+ string company_value = (string) attr_data.ConstructorArguments [0].Value;
+
+ if (!String.IsNullOrEmpty (company_value))
+ company = company_value;
+
+ }
+ break;
+
+ case "System.Reflection.AssemblyCopyrightAttribute": {
+ if (copyright != null)
+ // ignore if specified on command line
+ continue;
+
+ // AssemblyCopyrightAttribute .ctor(string copyright)
+ string copyright_value = (string) attr_data.ConstructorArguments [0].Value;
+
+ if (!String.IsNullOrEmpty (copyright_value))
+ copyright = copyright_value;
+ }
+ break;
+
+ case "System.Reflection.AssemblyTrademarkAttribute": {
+ if (trademark != null)
+ // ignore if specified on command line
+ continue;
+
+ // AssemblyTrademarkAttribute .ctor(string trademark)
+ string trademark_value = (string) attr_data.ConstructorArguments [0].Value;
+
+ if (!String.IsNullOrEmpty (trademark_value))
+ trademark = trademark_value;
+ }
+ break;
}
}
" /main:<method> Specifies the method name of the entry point",
" /nologo Suppress the startup banner and copyright message",
" /out:<filename> Output file name for the assembly manifest",
+ " /platform:<text> Limit which platforms this code can run on; must be",
+ " one of x86, Itanium, x64, arm, anycpu32bitpreferred,",
+ " or anycpu (the default)",
" /prod[uct]:<text> Product name",
" /productv[ersion]:<text> Product version",
" /t[arget]:lib[rary] Create a library",
switch (e) {
case MONO_GC_EVENT_PRE_STOP_WORLD:
MONO_GC_WORLD_STOP_BEGIN ();
- mono_thread_info_suspend_lock ();
break;
case MONO_GC_EVENT_POST_STOP_WORLD:
case MONO_GC_EVENT_POST_START_WORLD:
MONO_GC_WORLD_RESTART_END (1);
- mono_thread_info_suspend_unlock ();
break;
case MONO_GC_EVENT_START:
}
mono_profiler_gc_event (e, 0);
+
+ switch (e) {
+ case MONO_GC_EVENT_PRE_STOP_WORLD:
+ mono_thread_info_suspend_lock ();
+ mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0);
+ break;
+ case MONO_GC_EVENT_POST_START_WORLD:
+ mono_thread_info_suspend_unlock ();
+ mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0);
+ break;
+ default:
+ break;
+ }
}
+
static void
on_gc_heap_resize (size_t new_size)
return 0;
}
-gboolean
+MonoBoolean
mono_gc_pending_finalizers (void)
{
return GC_should_invoke_finalizers ();
#endif
#ifndef HOST_WIN32
+ mono_w32handle_init ();
wapi_init ();
#endif
typedef void (*FinalizerThreadCallback) (gpointer user_data);
-/* if there are finalizers to run, run them. Returns the number of finalizers run */
-gboolean mono_gc_pending_finalizers (void);
-void mono_gc_finalize_notify (void);
-
void* mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size);
void* mono_gc_alloc_obj (MonoVTable *vtable, size_t size);
void* mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length);
if (log_finalizers)
g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
+ mono_profiler_gc_finalize_object_begin (o);
+
runtime_invoke (o, NULL, &exc, NULL);
+ mono_profiler_gc_finalize_object_end (o);
+
if (log_finalizers)
g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
finalize_domain_objects ();
+ mono_profiler_gc_finalize_begin ();
+
/* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
* before the domain is unloaded.
*/
mono_gc_invoke_finalizers ();
+ mono_profiler_gc_finalize_end ();
+
mono_threads_join_threads ();
reference_queue_proccess_all ();
MonoObject *delegate;
gpointer func;
MonoMethod *method = info->method;
+ MonoMethodSignature *sig = mono_method_signature(method);
mono_class_init_checked (delegate_class, &error);
if (mono_error_set_pending_exception (&error))
}
}
+ if (sig->generic_param_count && method->wrapper_type == MONO_WRAPPER_NONE) {
+ if (!method->is_inflated) {
+ mono_set_pending_exception(mono_get_exception_argument("method", " Cannot bind to the target method because its signature differs from that of the delegate type"));
+ return NULL;
+ }
+ }
+
delegate = mono_object_new_checked (mono_object_domain (type), delegate_class, &error);
if (mono_error_set_pending_exception (&error))
return NULL;
while (left < right) {
int pos = (left + right) / 2;
- MonoJitInfo *ji = (MonoJitInfo *)get_hazardous_pointer((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX);
+ MonoJitInfo *ji = (MonoJitInfo *)mono_get_hazardous_pointer((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX);
gint8 *code_end = (gint8*)ji->code_start + ji->code_size;
if (addr < code_end)
MonoJitInfoTableChunk *chunk = table->chunks [chunk_pos];
while (pos < chunk->num_elements) {
- ji = (MonoJitInfo *)get_hazardous_pointer ((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX);
+ ji = (MonoJitInfo *)mono_get_hazardous_pointer ((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX);
++pos;
table by a hazard pointer and make sure that the pointer is
still there after we've made it hazardous, we don't have to
worry about the writer freeing the table. */
- table = (MonoJitInfoTable *)get_hazardous_pointer ((gpointer volatile*)&domain->jit_info_table, hp, JIT_INFO_TABLE_HAZARD_INDEX);
+ table = (MonoJitInfoTable *)mono_get_hazardous_pointer ((gpointer volatile*)&domain->jit_info_table, hp, JIT_INFO_TABLE_HAZARD_INDEX);
ji = jit_info_table_find (table, hp, (gint8*)addr);
if (hp)
/* Maybe its an AOT module */
if (try_aot && mono_get_root_domain () && mono_get_root_domain ()->aot_modules) {
- table = (MonoJitInfoTable *)get_hazardous_pointer ((gpointer volatile*)&mono_get_root_domain ()->aot_modules, hp, JIT_INFO_TABLE_HAZARD_INDEX);
+ table = (MonoJitInfoTable *)mono_get_hazardous_pointer ((gpointer volatile*)&mono_get_root_domain ()->aot_modules, hp, JIT_INFO_TABLE_HAZARD_INDEX);
module_ji = jit_info_table_find (table, hp, (gint8*)addr);
if (module_ji)
ji = jit_info_find_in_aot_func (domain, module_ji->d.image, addr);
MONO_API int mono_gc_collection_count (int generation);
MONO_API int64_t mono_gc_get_used_size (void);
MONO_API int64_t mono_gc_get_heap_size (void);
+MONO_API MonoBoolean mono_gc_pending_finalizers (void);
+MONO_API void mono_gc_finalize_notify (void);
MONO_API int mono_gc_invoke_finalizers (void);
/* heap walking is only valid in the pre-stop-world event callback */
MONO_API int mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data);
void mono_profiler_gc_handle (int op, int type, uintptr_t handle, MonoObject *obj);
void mono_profiler_gc_roots (int num, void **objects, int *root_types, uintptr_t *extra_info);
+void mono_profiler_gc_finalize_begin (void);
+void mono_profiler_gc_finalize_object_begin (MonoObject *obj);
+void mono_profiler_gc_finalize_object_end (MonoObject *obj);
+void mono_profiler_gc_finalize_end (void);
+
void mono_profiler_code_chunk_new (gpointer chunk, int size);
void mono_profiler_code_chunk_destroy (gpointer chunk);
void mono_profiler_code_buffer_new (gpointer buffer, int size, MonoProfilerCodeBufferType type, gconstpointer data);
MonoProfileGCHandleFunc gc_handle;
MonoProfileGCRootFunc gc_roots;
+ MonoProfileGCFinalizeFunc gc_finalize_begin;
+ MonoProfileGCFinalizeObjectFunc gc_finalize_object_begin;
+ MonoProfileGCFinalizeObjectFunc gc_finalize_object_end;
+ MonoProfileGCFinalizeFunc gc_finalize_end;
+
MonoProfileFunc runtime_initialized_event;
MonoProfilerCodeChunkNew code_chunk_new;
prof_list->gc_roots = roots_callback;
}
+void
+mono_profiler_gc_finalize_begin (void)
+{
+ for (ProfilerDesc *prof = prof_list; prof; prof = prof->next)
+ if ((prof->events & MONO_PROFILE_GC_FINALIZATION) && prof->gc_finalize_begin)
+ prof->gc_finalize_begin (prof->profiler);
+}
+
+void
+mono_profiler_gc_finalize_object_begin (MonoObject *obj)
+{
+ for (ProfilerDesc *prof = prof_list; prof; prof = prof->next)
+ if ((prof->events & MONO_PROFILE_GC_FINALIZATION) && prof->gc_finalize_object_begin)
+ prof->gc_finalize_object_begin (prof->profiler, obj);
+}
+
+void
+mono_profiler_gc_finalize_object_end (MonoObject *obj)
+{
+ for (ProfilerDesc *prof = prof_list; prof; prof = prof->next)
+ if ((prof->events & MONO_PROFILE_GC_FINALIZATION) && prof->gc_finalize_object_end)
+ prof->gc_finalize_object_end (prof->profiler, obj);
+}
+
+void
+mono_profiler_gc_finalize_end (void)
+{
+ for (ProfilerDesc *prof = prof_list; prof; prof = prof->next)
+ if ((prof->events & MONO_PROFILE_GC_FINALIZATION) && prof->gc_finalize_end)
+ prof->gc_finalize_end (prof->profiler);
+}
+
+void
+mono_profiler_install_gc_finalize (MonoProfileGCFinalizeFunc begin, MonoProfileGCFinalizeObjectFunc begin_obj, MonoProfileGCFinalizeObjectFunc end_obj, MonoProfileGCFinalizeFunc end)
+{
+ if (!prof_list)
+ return;
+
+ prof_list->gc_finalize_begin = begin;
+ prof_list->gc_finalize_object_begin = begin_obj;
+ prof_list->gc_finalize_object_begin = end_obj;
+ prof_list->gc_finalize_end = end;
+}
+
void
mono_profiler_install_runtime_initialized (MonoProfileFunc runtime_initialized_callback)
{
MONO_PROFILE_IOMAP_EVENTS = 1 << 18, /* this should likely be removed, too */
MONO_PROFILE_GC_MOVES = 1 << 19,
MONO_PROFILE_GC_ROOTS = 1 << 20,
- MONO_PROFILE_CONTEXT_EVENTS = 1 << 21
+ MONO_PROFILE_CONTEXT_EVENTS = 1 << 21,
+ MONO_PROFILE_GC_FINALIZATION = 1 << 22
} MonoProfileFlags;
typedef enum {
MONO_PROFILE_FAILED
} MonoProfileResult;
+// Keep somewhat in sync with libgc/include/gc.h:enum GC_EventType
typedef enum {
MONO_GC_EVENT_START,
MONO_GC_EVENT_MARK_START,
MONO_GC_EVENT_RECLAIM_START,
MONO_GC_EVENT_RECLAIM_END,
MONO_GC_EVENT_END,
+ /*
+ * This is the actual arrival order of the following events:
+ *
+ * MONO_GC_EVENT_PRE_STOP_WORLD
+ * MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED
+ * MONO_GC_EVENT_POST_STOP_WORLD
+ * MONO_GC_EVENT_PRE_START_WORLD
+ * MONO_GC_EVENT_POST_START_WORLD_UNLOCKED
+ * MONO_GC_EVENT_POST_START_WORLD
+ *
+ * The LOCKED and UNLOCKED events guarantee that, by the time they arrive,
+ * the GC and suspend locks will both have been acquired and released,
+ * respectively.
+ */
MONO_GC_EVENT_PRE_STOP_WORLD,
MONO_GC_EVENT_POST_STOP_WORLD,
MONO_GC_EVENT_PRE_START_WORLD,
- MONO_GC_EVENT_POST_START_WORLD
+ MONO_GC_EVENT_POST_START_WORLD,
+ MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED,
+ MONO_GC_EVENT_POST_START_WORLD_UNLOCKED
} MonoGCEvent;
/* coverage info */
typedef void (*MonoProfileGCHandleFunc) (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj);
typedef void (*MonoProfileGCRootFunc) (MonoProfiler *prof, int num_roots, void **objects, int *root_types, uintptr_t *extra_info);
+typedef void (*MonoProfileGCFinalizeFunc) (MonoProfiler *prof);
+typedef void (*MonoProfileGCFinalizeObjectFunc) (MonoProfiler *prof, MonoObject *obj);
+
typedef void (*MonoProfileIomapFunc) (MonoProfiler *prof, const char *report, const char *pathname, const char *new_pathname);
typedef mono_bool (*MonoProfileCoverageFilterFunc) (MonoProfiler *prof, MonoMethod *method);
MONO_API void mono_profiler_install_gc (MonoProfileGCFunc callback, MonoProfileGCResizeFunc heap_resize_callback);
MONO_API void mono_profiler_install_gc_moves (MonoProfileGCMoveFunc callback);
MONO_API void mono_profiler_install_gc_roots (MonoProfileGCHandleFunc handle_callback, MonoProfileGCRootFunc roots_callback);
+MONO_API void mono_profiler_install_gc_finalize (MonoProfileGCFinalizeFunc begin, MonoProfileGCFinalizeObjectFunc begin_obj, MonoProfileGCFinalizeObjectFunc end_obj, MonoProfileGCFinalizeFunc end);
MONO_API void mono_profiler_install_runtime_initialized (MonoProfileFunc runtime_initialized_callback);
MONO_API void mono_profiler_install_code_chunk_new (MonoProfilerCodeChunkNew callback);
gboolean sgen_is_bridge_object (GCObject *obj);
void sgen_mark_bridge_object (GCObject *obj);
+gboolean sgen_bridge_handle_gc_param (const char *opt);
gboolean sgen_bridge_handle_gc_debug (const char *opt);
void sgen_bridge_print_gc_debug_usage (void);
+typedef struct {
+ char *dump_prefix;
+ gboolean accounting;
+ gboolean scc_precise_merge; // Used by Tarjan
+} SgenBridgeProcessorConfig;
+
typedef struct {
void (*reset_data) (void);
void (*processing_stw_step) (void);
MonoGCBridgeObjectKind (*class_kind) (MonoClass *klass);
void (*register_finalized_object) (GCObject *object);
void (*describe_pointer) (GCObject *object);
- void (*enable_accounting) (void);
- // Optional-- used for debugging
- void (*set_dump_prefix) (const char *prefix);
+ /* Should be called once, immediately after init */
+ void (*set_config) (const SgenBridgeProcessorConfig *);
/*
* These are set by processing_build_callback_data().
static BridgeProcessorSelection bridge_processor_selection = BRIDGE_PROCESSOR_DEFAULT;
// Most recently requested callbacks
static MonoGCBridgeCallbacks pending_bridge_callbacks;
+// Configuration to be passed to bridge processor after init
+static SgenBridgeProcessorConfig bridge_processor_config;
// Currently-in-use callbacks
MonoGCBridgeCallbacks bridge_callbacks;
}
}
+static gboolean
+bridge_processor_started ()
+{
+ return bridge_processor.reset_data != NULL;
+}
+
// Initialize a single bridge processor
static void
init_bridge_processor (SgenBridgeProcessor *processor, BridgeProcessorSelection selection)
bridge_callbacks = pending_bridge_callbacks;
// If a bridge was registered but there is no bridge processor yet
- if (bridge_callbacks.cross_references && !bridge_processor.reset_data)
+ if (bridge_callbacks.cross_references && !bridge_processor_started ()) {
init_bridge_processor (&bridge_processor, bridge_processor_selection);
+ if (bridge_processor.set_config)
+ bridge_processor.set_config (&bridge_processor_config);
+
+ // Config is no longer needed so free its memory
+ free (bridge_processor_config.dump_prefix);
+ bridge_processor_config.dump_prefix = NULL;
+ }
+
sgen_gc_unlock ();
}
}
if (selection == BRIDGE_PROCESSOR_INVALID)
g_warning ("Invalid value for bridge processor implementation, valid values are: 'new', 'old' and 'tarjan'.");
- else if (bridge_processor.reset_data)
+ else if (bridge_processor_started ())
g_warning ("Cannot set bridge processor implementation once bridge has already started");
else
bridge_processor_selection = selection;
static void
set_dump_prefix (const char *prefix)
{
- if (!bridge_processor.set_dump_prefix) {
- fprintf (stderr, "Warning: Bridge implementation does not support dumping - ignoring.\n");
- return;
- }
-
- bridge_processor.set_dump_prefix (prefix);
+ if (bridge_processor_config.dump_prefix)
+ free (bridge_processor_config.dump_prefix);
+ bridge_processor_config.dump_prefix = strdup (prefix);
}
/* Test support code */
mono_gc_register_bridge_callbacks (&callbacks);
}
+gboolean
+sgen_bridge_handle_gc_param (const char *opt)
+{
+ g_assert (!bridge_processor_started ());
+
+ if (!strcmp (opt, "bridge-require-precise-merge")) {
+ bridge_processor_config.scc_precise_merge = TRUE;
+ } else {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
gboolean
sgen_bridge_handle_gc_debug (const char *opt)
{
+ g_assert (!bridge_processor_started ());
+
if (g_str_has_prefix (opt, "bridge=")) {
opt = strchr (opt, '=') + 1;
register_test_bridge_callbacks (g_strdup (opt));
} else if (!strcmp (opt, "enable-bridge-accounting")) {
- bridge_processor.enable_accounting ();
+ bridge_processor_config.accounting = TRUE;
} else if (g_str_has_prefix (opt, "bridge-dump=")) {
char *prefix = strchr (opt, '=') + 1;
- set_dump_prefix (prefix);
+ set_dump_prefix(prefix);
} else if (g_str_has_prefix (opt, "bridge-compare-to=")) {
const char *name = strchr (opt, '=') + 1;
BridgeProcessorSelection selection = bridge_processor_name (name);
if (selection != BRIDGE_PROCESSOR_INVALID) {
+ // Compare processor doesn't get config
init_bridge_processor (&compare_to_bridge_processor, selection);
} else {
g_warning ("Invalid bridge implementation to compare against - ignoring.");
* When SGen is done marking, it puts together a list of all dead bridged
* objects. This is passed to the bridge processor, which does an analysis to
* simplify the graph: It replaces strongly-connected components with single
- * nodes, and then removes any nodes corresponding to components which do not
- * contain bridged objects.
+ * nodes, and may remove nodes corresponding to components which do not contain
+ * bridged objects.
*
* The output of the SCC analysis is passed to the client's `cross_references()`
* callback. This consists of 2 arrays, an array of SCCs (MonoGCBridgeSCC),
MONO_BEGIN_DECLS
enum {
- SGEN_BRIDGE_VERSION = 4
+ SGEN_BRIDGE_VERSION = 5
};
typedef enum {
return sgen_gc_invoke_finalizers ();
}
-gboolean
+MonoBoolean
mono_gc_pending_finalizers (void)
{
return sgen_have_pending_finalizers ();
} else if (g_str_has_prefix (opt, "toggleref-test")) {
/* FIXME: This should probably in MONO_GC_DEBUG */
sgen_register_test_toggleref_callback ();
- } else {
+ } else if (!sgen_bridge_handle_gc_param (opt)) {
return FALSE;
}
return TRUE;
#endif
} SCC;
+static char *dump_prefix = NULL;
+
// Maps managed objects to corresponding HashEntry stricts
static SgenHashTable hash_table = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_HASH_TABLE, INTERNAL_MEM_BRIDGE_HASH_TABLE_ENTRY, sizeof (HashEntry), mono_aligned_addr_hash, NULL);
#endif
static void
-enable_accounting (void)
+set_config (const SgenBridgeProcessorConfig *config)
{
- SgenHashTable table = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_HASH_TABLE, INTERNAL_MEM_BRIDGE_HASH_TABLE_ENTRY, sizeof (HashEntryWithAccounting), mono_aligned_addr_hash, NULL);
- bridge_accounting_enabled = TRUE;
- hash_table = table;
+ if (config->accounting) {
+ SgenHashTable table = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_HASH_TABLE, INTERNAL_MEM_BRIDGE_HASH_TABLE_ENTRY, sizeof (HashEntryWithAccounting), mono_aligned_addr_hash, NULL);
+ bridge_accounting_enabled = TRUE;
+ hash_table = table;
+ }
+ if (config->dump_prefix) {
+ dump_prefix = strdup (config->dump_prefix);
+ }
}
static MonoGCBridgeObjectKind
}
#endif
-static char *dump_prefix = NULL;
-
static void
dump_graph (void)
{
fclose (file);
}
-static void
-set_dump_prefix (const char *prefix)
-{
- dump_prefix = strdup (prefix);
-}
-
static int
compare_hash_entries (const HashEntry *e1, const HashEntry *e2)
{
collector->class_kind = class_kind;
collector->register_finalized_object = register_finalized_object;
collector->describe_pointer = describe_pointer;
- collector->enable_accounting = enable_accounting;
- collector->set_dump_prefix = set_dump_prefix;
+ collector->set_config = set_config;
bridge_processor = collector;
}
static void
-enable_accounting (void)
+set_config (const SgenBridgeProcessorConfig *config)
{
- SgenHashTable table = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_HASH_TABLE, INTERNAL_MEM_BRIDGE_HASH_TABLE_ENTRY, sizeof (HashEntryWithAccounting), mono_aligned_addr_hash, NULL);
- bridge_accounting_enabled = TRUE;
- hash_table = table;
+ if (config->accounting) {
+ SgenHashTable table = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_HASH_TABLE, INTERNAL_MEM_BRIDGE_HASH_TABLE_ENTRY, sizeof (HashEntryWithAccounting), mono_aligned_addr_hash, NULL);
+ bridge_accounting_enabled = TRUE;
+ hash_table = table;
+ }
}
static MonoGCBridgeObjectKind
collector->class_kind = class_kind;
collector->register_finalized_object = register_finalized_object;
collector->describe_pointer = describe_pointer;
- collector->enable_accounting = enable_accounting;
+ collector->set_config = set_config;
bridge_processor = collector;
}
acquire_gc_locks ();
+ mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, generation);
+
/* We start to scan after locks are taking, this ensures we won't be interrupted. */
sgen_process_togglerefs ();
*/
release_gc_locks ();
+ mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, generation);
+
*stw_time = usec;
}
#include "sgen/sgen-gc.h"
#include "sgen-bridge-internals.h"
-#include "sgen/sgen-hash-table.h"
-#include "sgen/sgen-qsort.h"
#include "tabledefs.h"
#include "utils/mono-logger-internals.h"
* which colors. The color graph then becomes the reduced SCC graph.
*/
-static void
-enable_accounting (void)
-{
- // bridge_accounting_enabled = TRUE;
- // hash_table = (SgenHashTable)SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_HASH_TABLE, INTERNAL_MEM_BRIDGE_HASH_TABLE_ENTRY, sizeof (HashEntryWithAccounting), mono_aligned_addr_hash, NULL);
-}
-
// Is this class bridged or not, and should its dependencies be scanned or not?
// The result of this callback will be cached for use by is_opaque_object later.
static MonoGCBridgeObjectKind
//enable usage logging
// #define DUMP_GRAPH 1
+/* Used in bridgeless_color_is_heavy:
+ * The idea here is as long as the reference fanin and fanout on a node are both 2 or greater, then
+ * removing that node will result in a net increase in edge count. So the question is which node
+ * removals are counterproductive (i.e., how many edges saved balances out one node added).
+ * The number of edges saved by preserving a node is (fanin*fanout - fanin - fanout).
+ *
+ * With all that in mind:
+ *
+ * - HEAVY_REFS_MIN is the number that *both* fanin and fanout must meet to preserve the node.
+ * - HEAVY_COMBINED_REFS_MIN is the number (fanin*fanout) must meet to preserve the node.
+ *
+ * Note HEAVY_COMBINED_REFS_MIN must be <= 2*INCOMING_COLORS_MAX, or we won't know the true fanin.
+ */
+
+#define HEAVY_REFS_MIN 2
+#define HEAVY_COMBINED_REFS_MIN 60
+
+/* Used in ColorData:
+ * The higher INCOMING_COLORS_BITS is the higher HEAVY_COMBINED_REFS_MIN can be (see above).
+ * However, API_INDEX_BITS + INCOMING_COLORS_BITS must be equal to 31, and if API_INDEX_BITS is too
+ * low then terrible things will happen if too many colors are generated. (The number of colors we
+ * will ever attempt to generate is currently naturally limited by the JNI GREF limit.)
+ */
+
+#define API_INDEX_BITS 26
+#define INCOMING_COLORS_BITS 5
+
+#define API_INDEX_MAX ((1<<API_INDEX_BITS)-1)
+#define INCOMING_COLORS_MAX ((1<<INCOMING_COLORS_BITS)-1)
+
// ScanData state
enum {
INITIAL,
/*
Optimizations:
We can split this data structure in two, those with bridges and those without
+ (and only bridgeless need to record incoming_colors)
*/
typedef struct {
- // Colors (ColorDatas) linked to by objects with this color
+ // Colors (ColorDatas) linked to by objects with this color
DynPtrArray other_colors;
- // Bridge objects (GCObjects) held by objects with this color
+ // Bridge objects (GCObjects) held by objects with this color
DynPtrArray bridges;
- int api_index : 31;
+ // Index of this color's MonoGCBridgeSCC in the array passed to the client (or -1 for none)
+ signed api_index : API_INDEX_BITS;
+ // Count of colors that list this color in their other_colors
+ unsigned incoming_colors : INCOMING_COLORS_BITS;
unsigned visited : 1;
} ColorData;
// Tarjan algorithm index (order visited)
int index;
// Tarjan index of lowest-index object known reachable from here
- int low_index : 27;
+ signed low_index : 27;
// See "ScanData state" enum above
unsigned state : 2;
unsigned obj_state : 2;
} ScanData;
+/* Should color be made visible to client even though it has no bridges?
+ * True if we predict the number of reduced edges to be enough to justify the extra node.
+ */
+static inline gboolean
+bridgeless_color_is_heavy (ColorData *data) {
+ int fanin = data->incoming_colors;
+ int fanout = dyn_array_ptr_size (&data->other_colors);
+ return fanin > HEAVY_REFS_MIN && fanout > HEAVY_REFS_MIN
+ && fanin*fanout >= HEAVY_COMBINED_REFS_MIN;
+}
+// Should color be made visible to client?
+static inline gboolean
+color_visible_to_client (ColorData *data) {
+ return dyn_array_ptr_size (&data->bridges) || bridgeless_color_is_heavy (data);
+}
// Stacks of ScanData objects used for tarjan algorithm.
// The Tarjan algorithm is normally defined recursively; here scan_stack simulates the call stack of a recursive algorithm,
// GCObjects on which register_finalized_object has been called
static DynPtrArray registered_bridges;
-// ColorData objects
+// As we traverse the graph, which ColorData objects are accessible from our current position?
static DynPtrArray color_merge_array;
+// Running hash of the contents of the color_merge_array.
+static unsigned int color_merge_array_hash;
+
+static void color_merge_array_empty ()
+{
+ dyn_array_ptr_empty (&color_merge_array);
+ color_merge_array_hash = 0;
+}
static int ignored_objects;
static int object_index;
static int num_colors_with_bridges;
+static int num_sccs;
static int xref_count;
static size_t setup_time, tarjan_time, scc_setup_time, gather_xref_time, xref_setup_time, cleanup_time;
static ObjectBucket *cur_object_bucket;
static int object_data_count;
+// Arenas to allocate ScanData from
static ObjectBucket*
new_object_bucket (void)
{
//ColorData buckets
#define NUM_COLOR_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ColorData))
-// Same as ObjectBucket except NUM_COLOR_ENTRIES and NUM_SCAN_ENTRIES differ
+// Arenas for ColorDatas, same as ObjectBucket except items-per-bucket differs
typedef struct _ColorBucket ColorBucket;
struct _ColorBucket {
ColorBucket *next;
//----------
typedef struct {
ColorData *color;
- int hash;
+ unsigned int hash;
} HashEntry;
/*
-We tried 2/32, 2/128, 4/32, 4/128, 6/128 and 8/128.
+The merge cache maps an ordered list of ColorDatas [the color_merge_array] to a single ColorData.
+
+About cache bucket tuning: We tried 2/32, 2/128, 4/32, 4/128, 6/128 and 8/128.
The performance cost between 4/128 and 8/128 is so small since cache movement happens completely in the same cacheline,
making the extra space pretty much free.
#define ELEMENTS_PER_BUCKET 8
#define COLOR_CACHE_SIZE 128
static HashEntry merge_cache [COLOR_CACHE_SIZE][ELEMENTS_PER_BUCKET];
+static unsigned int hash_perturb;
+
+// If true, disable an optimization where sometimes SCC nodes are merged without a perfect check
+static gboolean scc_precise_merge;
-static int
-mix_hash (size_t hash)
+static unsigned int
+mix_hash (uintptr_t source)
{
- return (int)(((hash * 215497) >> 16) ^ ((hash * 1823231) + hash));
+ unsigned int hash = source;
+
+ // The full hash determines whether two colors can be merged-- sometimes exclusively.
+ // This value changes every GC, so XORing it in before performing the hash will make the
+ // chance that two different colors will produce the same hash on successive GCs very low.
+ hash = hash ^ hash_perturb;
+
+ // Actual hash
+ hash = (((hash * 215497) >> 16) ^ ((hash * 1823231) + hash));
+
+ // Mix in highest bits on 64-bit systems only
+ if (sizeof (source) > 4)
+ hash = hash ^ (source >> 32);
+
+ return hash;
}
static void
reset_cache (void)
{
memset (merge_cache, 0, sizeof (merge_cache));
+
+ // When using the precise merge debug option, we do not want the inconsistency caused by hash_perturb.
+ if (!scc_precise_merge)
+ ++hash_perturb;
}
return FALSE;
}
+static gboolean
+match_colors_estimate (DynPtrArray *a, DynPtrArray *b)
+{
+ return dyn_array_ptr_size (a) == dyn_array_ptr_size (b);
+}
+
+
static gboolean
match_colors (DynPtrArray *a, DynPtrArray *b)
{
return TRUE;
}
-static int cache_hits, cache_misses;
+// If scc_precise_merge, "semihits" refers to find_in_cache calls aborted because the merge array was too large.
+// Otherwise "semihits" refers to cache hits where the match was only estimated.
+static int cache_hits, cache_semihits, cache_misses;
+// The cache contains only non-bridged colors.
static ColorData*
find_in_cache (int *insert_index)
{
HashEntry *bucket;
- int i, hash, size, index;
+ int i, size, index;
size = dyn_array_ptr_size (&color_merge_array);
- /* Cache checking is very ineficient with a lot of elements*/
- if (size > 3)
+
+ /* Color equality checking is very expensive with a lot of elements, so when there are many
+ * elements we switch to a cheap comparison method which allows false positives. When false
+ * positives occur the worst that can happen is two items will be inappropriately merged
+ * and memory will be retained longer than it should be. We assume that will correct itself
+ * on the next GC (the hash_perturb mechanism increases the probability of this).
+ *
+ * Because this has *some* potential to create problems, if the user set the debug option
+ * 'enable-tarjan-precise-merge' we bail out early (and never merge SCCs with >3 colors).
+ */
+ gboolean color_merge_array_large = size > 3;
+ if (scc_precise_merge && color_merge_array_large) {
+ ++cache_semihits;
return NULL;
+ }
- hash = 0;
- for (i = 0 ; i < size; ++i)
- hash += mix_hash ((size_t)dyn_array_ptr_get (&color_merge_array, i));
- if (!hash)
+ unsigned int hash = color_merge_array_hash;
+ if (!hash) // 0 is used to indicate an empty bucket entry
hash = 1;
index = hash & (COLOR_CACHE_SIZE - 1);
for (i = 0; i < ELEMENTS_PER_BUCKET; ++i) {
if (bucket [i].hash != hash)
continue;
- if (match_colors (&bucket [i].color->other_colors, &color_merge_array)) {
- ++cache_hits;
- return bucket [i].color;
+
+ if (color_merge_array_large) {
+ if (match_colors_estimate (&bucket [i].color->other_colors, &color_merge_array)) {
+ ++cache_semihits;
+ return bucket [i].color;
+ }
+ } else {
+ if (match_colors (&bucket [i].color->other_colors, &color_merge_array)) {
+ ++cache_hits;
+ return bucket [i].color;
+ }
}
}
return NULL;
}
+// A color is needed for an SCC. If the SCC has bridges, the color MUST be newly allocated.
+// If the SCC lacks bridges, the allocator MAY use the cache to merge it with an existing one.
static ColorData*
-new_color (gboolean force_new)
+new_color (gboolean has_bridges)
{
- int i = -1;
+ int cacheSlot = -1;
ColorData *cd;
/* XXX Try to find an equal one and return it */
- if (!force_new) {
- cd = find_in_cache (&i);
+ if (!has_bridges) {
+ cd = find_in_cache (&cacheSlot);
if (cd)
return cd;
}
cd = alloc_color_data ();
cd->api_index = -1;
dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
- /* if i >= 0, it means we prepared a given slot to receive the new color */
- if (i >= 0)
- merge_cache [i][0].color = cd;
+
+ // Inform targets
+ for (int i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i) {
+ ColorData *points_to = (ColorData *)dyn_array_ptr_get (&color_merge_array, i);
+ points_to->incoming_colors = MIN (points_to->incoming_colors + 1, INCOMING_COLORS_MAX);
+ }
+
+ /* if cacheSlot >= 0, it means we prepared a given slot to receive the new color */
+ if (cacheSlot >= 0)
+ merge_cache [cacheSlot][0].color = cd;
return cd;
}
cd = other->color;
if (!cd->visited) {
+ color_merge_array_hash += mix_hash ((uintptr_t) other->color);
dyn_array_ptr_add (&color_merge_array, other->color);
cd->visited = TRUE;
}
#include "sgen/sgen-scan-object.h"
}
+// A non-bridged object needs a single color describing the current merge array.
static ColorData*
reduce_color (void)
{
ColorData *color = NULL;
int size = dyn_array_ptr_size (&color_merge_array);
+ // Merge array is empty-- this SCC points to no bridged colors.
+ // This SCC can be ignored completely.
if (size == 0)
color = NULL;
+
+ // Merge array has one item-- this SCC points to a single bridged color.
+ // This SCC can be forwarded to the pointed-to color.
else if (size == 1) {
color = (ColorData *)dyn_array_ptr_get (&color_merge_array, 0);
+
+ // This SCC gets to talk to the color allocator.
} else
color = new_color (FALSE);
g_assert (cd->visited);
cd->visited = FALSE;
}
- dyn_array_ptr_empty (&color_merge_array);
+ color_merge_array_empty ();
found_bridge = FALSE;
}
g_assert (dyn_array_ptr_size (&scan_stack) == 1);
g_assert (dyn_array_ptr_size (&loop_stack) == 0);
- dyn_array_ptr_empty (&color_merge_array);
+ color_merge_array_empty ();
while (dyn_array_ptr_size (&scan_stack) > 0) {
ScanData *data = (ScanData *)dyn_array_ptr_pop (&scan_stack);
if (src->visited)
continue;
src->visited = TRUE;
- if (dyn_array_ptr_size (&src->bridges))
+ if (color_visible_to_client (src))
dyn_array_ptr_add (&color_merge_array, src);
else
gather_xrefs (src);
if (!src->visited)
continue;
src->visited = FALSE;
- if (!dyn_array_ptr_size (&src->bridges))
+ if (!color_visible_to_client (src))
reset_xrefs (src);
}
}
static void
processing_build_callback_data (int generation)
{
- int j, api_index;
- MonoGCBridgeSCC **api_sccs;
- MonoGCBridgeXRef *api_xrefs;
+ int j;
gint64 curtime;
ColorBucket *cur;
printf ("number of SCCs %d\n", num_colors_with_bridges);
#endif
+ // Count the number of SCCs visible to the client
+ num_sccs = 0;
+ for (cur = root_color_bucket; cur; cur = cur->next) {
+ ColorData *cd;
+ for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
+ if (color_visible_to_client (cd))
+ num_sccs++;
+ }
+ }
+
/* This is a straightforward translation from colors to the bridge callback format. */
- api_sccs = (MonoGCBridgeSCC **)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC*) * num_colors_with_bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE);
- api_index = xref_count = 0;
+ MonoGCBridgeSCC **api_sccs = (MonoGCBridgeSCC **)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC*) * num_sccs, INTERNAL_MEM_BRIDGE_DATA, TRUE);
+ int api_index = 0;
+ xref_count = 0;
+ // Convert visible SCCs, along with their bridged object list, to MonoGCBridgeSCCs in the client's SCC list
for (cur = root_color_bucket; cur; cur = cur->next) {
ColorData *cd;
for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
int bridges = dyn_array_ptr_size (&cd->bridges);
- if (!bridges)
+ if (!(bridges || bridgeless_color_is_heavy (cd)))
continue;
api_sccs [api_index] = (MonoGCBridgeSCC *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE);
for (j = 0; j < bridges; ++j)
api_sccs [api_index]->objs [j] = (MonoObject *)dyn_array_ptr_get (&cd->bridges, j);
+
+ g_assert(api_index < API_INDEX_MAX);
api_index++;
}
}
scc_setup_time = step_timer (&curtime);
+ // Eliminate non-visible SCCs from the SCC list and redistribute xrefs
for (cur = root_color_bucket; cur; cur = cur->next) {
ColorData *cd;
for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
- int bridges = dyn_array_ptr_size (&cd->bridges);
- if (!bridges)
+ if (!color_visible_to_client (cd))
continue;
- dyn_array_ptr_empty (&color_merge_array);
+ color_merge_array_empty ();
gather_xrefs (cd);
reset_xrefs (cd);
dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
dump_color_table (" after xref pass", TRUE);
#endif
- api_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeXRef) * xref_count, INTERNAL_MEM_BRIDGE_DATA, TRUE);
- api_index = 0;
+ // Write out xrefs array
+ MonoGCBridgeXRef *api_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeXRef) * xref_count, INTERNAL_MEM_BRIDGE_DATA, TRUE);
+ int xref_index = 0;
for (cur = root_color_bucket; cur; cur = cur->next) {
ColorData *src;
for (src = &cur->data [0]; src < cur->next_data; ++src) {
- int bridges = dyn_array_ptr_size (&src->bridges);
- if (!bridges)
+ if (!color_visible_to_client (src))
continue;
for (j = 0; j < dyn_array_ptr_size (&src->other_colors); ++j) {
ColorData *dest = (ColorData *)dyn_array_ptr_get (&src->other_colors, j);
- g_assert (dyn_array_ptr_size (&dest->bridges)); /* We flattened the color graph, so this must never happen. */
+ g_assert (color_visible_to_client (dest)); /* Supposedly we already eliminated all xrefs to non-visible objects. */
+
+ api_xrefs [xref_index].src_scc_index = src->api_index;
+ api_xrefs [xref_index].dst_scc_index = dest->api_index;
- api_xrefs [api_index].src_scc_index = src->api_index;
- api_xrefs [api_index].dst_scc_index = dest->api_index;
- ++api_index;
+ ++xref_index;
}
}
}
- g_assert (xref_count == api_index);
+ g_assert (xref_count == xref_index);
xref_setup_time = step_timer (&curtime);
#if defined (DUMP_GRAPH)
#endif
//FIXME move half of the cleanup to before the bridge callback?
- bridge_processor->num_sccs = num_colors_with_bridges;
+ bridge_processor->num_sccs = num_sccs;
bridge_processor->api_sccs = api_sccs;
bridge_processor->num_xrefs = xref_count;
bridge_processor->api_xrefs = api_xrefs;
int bridge_count = dyn_array_ptr_size (®istered_bridges);
int object_count = object_data_count;
int color_count = color_data_count;
- int scc_count = num_colors_with_bridges;
+ int colors_with_bridges_count = num_colors_with_bridges;
SGEN_TV_GETTIME (curtime);
cleanup_time = step_timer (&curtime);
- mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_TAR_BRIDGE bridges %d objects %d colors %d ignored %d sccs %d xref %d cache %d/%d setup %.2fms tarjan %.2fms scc-setup %.2fms gather-xref %.2fms xref-setup %.2fms cleanup %.2fms",
- bridge_count, object_count, color_count,
- ignored_objects, scc_count, xref_count,
- cache_hits, cache_misses,
+ mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_TAR_BRIDGE bridges %d objects %d opaque %d colors %d colors-bridged %d colors-visible %d xref %d cache-hit %d cache-%s %d cache-miss %d setup %.2fms tarjan %.2fms scc-setup %.2fms gather-xref %.2fms xref-setup %.2fms cleanup %.2fms",
+ bridge_count, object_count, ignored_objects,
+ color_count, colors_with_bridges_count, num_sccs, xref_count,
+ cache_hits, (scc_precise_merge ? "abstain" : "semihit"), cache_semihits, cache_misses,
setup_time / 10000.0f,
tarjan_time / 10000.0f,
scc_setup_time / 10000.0f,
xref_setup_time / 10000.0f,
cleanup_time / 10000.0f);
- cache_hits = cache_misses = 0;
+ cache_hits = cache_semihits = cache_misses = 0;
ignored_objects = 0;
}
// printf (" is visited: %d\n", (int)entry->is_visited);
}
+static void
+set_config (const SgenBridgeProcessorConfig *config)
+{
+ if (config->scc_precise_merge) {
+ hash_perturb = 0;
+ scc_precise_merge = TRUE;
+ }
+}
+
void
sgen_tarjan_bridge_init (SgenBridgeProcessor *collector)
{
collector->class_kind = class_kind;
collector->register_finalized_object = register_finalized_object;
collector->describe_pointer = describe_pointer;
- collector->enable_accounting = enable_accounting;
- // collector->set_dump_prefix = set_dump_prefix;
+ collector->set_config = set_config;
sgen_register_fixed_internal_mem_type (INTERNAL_MEM_TARJAN_OBJ_BUCKET, BUCKET_SIZE);
g_assert (sizeof (ObjectBucket) <= BUCKET_SIZE);
g_assert (sizeof (ColorBucket) <= BUCKET_SIZE);
+ g_assert (API_INDEX_BITS + INCOMING_COLORS_BITS <= 31);
bridge_processor = collector;
}
void
mono_thread_internal_unhandled_exception (MonoObject* exc)
{
- if (mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_CURRENT) {
- MonoClass *klass = exc->vtable->klass;
- if (is_threadabort_exception (klass)) {
- mono_thread_internal_reset_abort (mono_thread_internal_current ());
- } else if (!is_appdomainunloaded_exception (klass)) {
- mono_unhandled_exception (exc);
- if (mono_environment_exitcode_get () == 1) {
- mono_environment_exitcode_set (255);
- mono_invoke_unhandled_exception_hook (exc);
- g_assert_not_reached ();
- }
+ MonoClass *klass = exc->vtable->klass;
+ if (is_threadabort_exception (klass)) {
+ mono_thread_internal_reset_abort (mono_thread_internal_current ());
+ } else if (!is_appdomainunloaded_exception (klass)
+ && mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_CURRENT) {
+ mono_unhandled_exception (exc);
+ if (mono_environment_exitcode_get () == 1) {
+ mono_environment_exitcode_set (255);
+ mono_invoke_unhandled_exception_hook (exc);
+ g_assert_not_reached ();
}
}
}
wrap_path (g_strdup_printf ("%s.o", acfg->tmpfname)), ld_flags);
#else
// Default (linux)
- char *args = g_strdup_printf ("%s %s -shared -o %s %s %s %s", tool_prefix, LD_OPTIONS,
+ char *args = g_strdup_printf ("%s -shared -o %s %s %s %s", LD_OPTIONS,
wrap_path (tmp_outfile_name), wrap_path (llvm_ofile),
wrap_path (g_strdup_printf ("%s.o", acfg->tmpfname)), ld_flags);
thread->internal_thread->state |= ThreadState_Background;
thread->internal_thread->flags |= MONO_THREAD_FLAG_DONT_MANAGE;
- mono_set_is_debugger_attached (TRUE);
-
if (agent_config.defer) {
if (!wait_for_attach ()) {
DEBUG_PRINTF (1, "[dbg] Can't attach, aborting debugger thread.\n");
attach_failed = TRUE; // Don't abort process when we can't listen
} else {
+ mono_set_is_debugger_attached (TRUE);
/* Send start event to client */
process_profiler_event (EVENT_KIND_VM_START, mono_thread_get_main ());
}
+ } else {
+ mono_set_is_debugger_attached (TRUE);
}
while (!attach_failed) {
#include <mono/utils/mono-mmap.h>
#include <mono/utils/mono-memory-model.h>
#include <mono/utils/mono-tls.h>
-#include <mono/utils/mono-hwcap-x86.h>
+#include <mono/utils/mono-hwcap.h>
#include <mono/utils/mono-threads.h>
#include "trace.h"
#include <mono/metadata/profiler-private.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/utils/mono-mmap.h>
-#include <mono/utils/mono-hwcap-arm.h>
+#include <mono/utils/mono-hwcap.h>
#include <mono/utils/mono-memory-model.h>
#include <mono/utils/mono-threads-coop.h>
#include <mono/metadata/threads.h>
#include <mono/metadata/profiler-private.h>
#include <mono/utils/mono-math.h>
-#include <mono/utils/mono-hwcap-ia64.h>
+#include <mono/utils/mono-hwcap.h>
#include "trace.h"
#include "mini-ia64.h"
values [ins->dreg] = LLVMBuildSelect (builder, v, lhs, rhs, dname);
break;
}
+
+/*
+ * See the ARM64 comment in mono/utils/atomic.h for an explanation of why this
+ * hack is necessary (for now).
+ */
+#ifdef TARGET_ARM64
+#define ARM64_ATOMIC_FENCE_FIX mono_llvm_build_fence (builder, LLVM_BARRIER_SEQ)
+#else
+#define ARM64_ATOMIC_FENCE_FIX
+#endif
+
case OP_ATOMIC_EXCHANGE_I4:
case OP_ATOMIC_EXCHANGE_I8: {
LLVMValueRef args [2];
args [0] = convert (ctx, lhs, LLVMPointerType (t, 0));
args [1] = convert (ctx, rhs, t);
+ ARM64_ATOMIC_FENCE_FIX;
values [ins->dreg] = mono_llvm_build_atomic_rmw (builder, LLVM_ATOMICRMW_OP_XCHG, args [0], args [1]);
+ ARM64_ATOMIC_FENCE_FIX;
break;
}
case OP_ATOMIC_ADD_I4:
args [0] = convert (ctx, lhs, LLVMPointerType (t, 0));
args [1] = convert (ctx, rhs, t);
+ ARM64_ATOMIC_FENCE_FIX;
values [ins->dreg] = LLVMBuildAdd (builder, mono_llvm_build_atomic_rmw (builder, LLVM_ATOMICRMW_OP_ADD, args [0], args [1]), args [1], dname);
+ ARM64_ATOMIC_FENCE_FIX;
break;
}
case OP_ATOMIC_CAS_I4:
args [1] = convert (ctx, values [ins->sreg3], t);
/* new value */
args [2] = convert (ctx, values [ins->sreg2], t);
+ ARM64_ATOMIC_FENCE_FIX;
val = mono_llvm_build_cmpxchg (builder, args [0], args [1], args [2]);
+ ARM64_ATOMIC_FENCE_FIX;
/* cmpxchg returns a pair */
values [ins->dreg] = LLVMBuildExtractValue (builder, val, 0, "");
break;
addr = convert (ctx, addr, LLVMPointerType (t, 0));
+ ARM64_ATOMIC_FENCE_FIX;
values [ins->dreg] = emit_load_general (ctx, bb, &builder, size, addr, lhs, dname, is_volatile, barrier);
+ ARM64_ATOMIC_FENCE_FIX;
if (sext)
values [ins->dreg] = LLVMBuildSExt (builder, values [ins->dreg], LLVMInt32Type (), dname);
addr = LLVMBuildGEP (builder, convert (ctx, base, LLVMPointerType (t, 0)), &index, 1, "");
value = convert (ctx, values [ins->sreg1], t);
+ ARM64_ATOMIC_FENCE_FIX;
emit_store_general (ctx, bb, &builder, size, value, addr, base, is_volatile, barrier);
+ ARM64_ATOMIC_FENCE_FIX;
break;
}
case OP_RELAXED_NOP: {
#include <mono/metadata/appdomain.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/utils/mono-mmap.h>
-#include <mono/utils/mono-hwcap-mips.h>
+#include <mono/utils/mono-hwcap.h>
#include <mono/arch/mips/mips-codegen.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/utils/mono-proclib.h>
#include <mono/utils/mono-mmap.h>
-#include <mono/utils/mono-hwcap-ppc.h>
+#include <mono/utils/mono-hwcap.h>
#include "mini-ppc.h"
#ifdef TARGET_POWERPC64
#include <mono/utils/mono-error-internals.h>
#include <mono/utils/mono-math.h>
#include <mono/utils/mono-mmap.h>
-#include <mono/utils/mono-hwcap-s390x.h>
+#include <mono/utils/mono-hwcap.h>
#include <mono/utils/mono-threads.h>
#include "mini-s390x.h"
gboolean lmf_addr_key_inited = FALSE;
-facilityList_t facs;
-
/*
* The code generated for sequence points reads from this location,
* which is made read-only when single stepping is enabled.
}
break;
case OP_ICONV_TO_R_UN: {
- if (facs.fpe) {
+ if (mono_hwcap_s390x_has_fpe) {
s390_cdlfbr (code, ins->dreg, 5, ins->sreg1, 0);
} else {
s390_llgfr (code, s390_r0, ins->sreg1);
}
break;
case OP_LCONV_TO_R_UN: {
- if (facs.fpe) {
+ if (mono_hwcap_s390x_has_fpe) {
s390_cdlgbr (code, ins->dreg, 5, ins->sreg1, 0);
} else {
short int *jump;
s390_ngr (code, ins->dreg, s390_r0);
break;
case OP_FCONV_TO_U1:
- if (facs.fpe) {
+ if (mono_hwcap_s390x_has_fpe) {
s390_clgdbr (code, ins->dreg, 5, ins->sreg1, 0);
s390_lghi (code, s390_r0, 0xff);
s390_ngr (code, ins->dreg, s390_r0);
s390_ngr (code, ins->dreg, s390_r0);
break;
case OP_FCONV_TO_U2:
- if (facs.fpe) {
+ if (mono_hwcap_s390x_has_fpe) {
s390_clgdbr (code, ins->dreg, 5, ins->sreg1, 0);
s390_llill (code, s390_r0, 0xffff);
s390_ngr (code, ins->dreg, s390_r0);
break;
case OP_FCONV_TO_U4:
case OP_FCONV_TO_U:
- if (facs.fpe) {
+ if (mono_hwcap_s390x_has_fpe) {
s390_clfdbr (code, ins->dreg, 5, ins->sreg1, 0);
} else {
code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE);
s390_cgdbr (code, ins->dreg, 5, ins->sreg1);
break;
case OP_FCONV_TO_U8:
- if (facs.fpe) {
+ if (mono_hwcap_s390x_has_fpe) {
s390_clgdbr (code, ins->dreg, 5, ins->sreg1, 0);
} else {
code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 8, FALSE);
{
guint32 sseOpts = 0;
- if (facs.vec != 0)
+ if (mono_hwcap_s390x_has_vec)
sseOpts = (SIMD_VERSION_SSE1 | SIMD_VERSION_SSE2 |
SIMD_VERSION_SSE3 | SIMD_VERSION_SSSE3 |
SIMD_VERSION_SSE41 | SIMD_VERSION_SSE42 |
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/tokentype.h>
#include <mono/utils/mono-math.h>
-#include <mono/utils/mono-hwcap-sparc.h>
+#include <mono/utils/mono-hwcap.h>
#include "mini-sparc.h"
#include "trace.h"
#include <mono/utils/mono-counters.h>
#include <mono/utils/mono-mmap.h>
#include <mono/utils/mono-memory-model.h>
-#include <mono/utils/mono-hwcap-x86.h>
+#include <mono/utils/mono-hwcap.h>
#include <mono/utils/mono-threads.h>
#include "trace.h"
case MONO_GC_EVENT_RECLAIM_END: return "reclaim end";
case MONO_GC_EVENT_END: return "end";
case MONO_GC_EVENT_PRE_STOP_WORLD: return "pre stop";
+ case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED: return "pre stop lock";
case MONO_GC_EVENT_POST_STOP_WORLD: return "post stop";
case MONO_GC_EVENT_PRE_START_WORLD: return "pre start";
case MONO_GC_EVENT_POST_START_WORLD: return "post start";
+ case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED: return "post start unlock";
+ default:
+ return "unknown";
+ }
+}
+
+static const char*
+sync_point_name (int type)
+{
+ switch (type) {
+ case SYNC_POINT_PERIODIC: return "periodic";
+ case SYNC_POINT_WORLD_STOP: return "world stop";
+ case SYNC_POINT_WORLD_START: return "world start";
default:
return "unknown";
}
}
static MethodDesc**
-decode_bt (MethodDesc** sframes, int *size, unsigned char *p, unsigned char **endp, intptr_t ptr_base)
+decode_bt (ProfContext *ctx, MethodDesc** sframes, int *size, unsigned char *p, unsigned char **endp, intptr_t ptr_base, intptr_t *method_base)
{
MethodDesc **frames;
int i;
- int flags = decode_uleb128 (p, &p);
+ if (ctx->data_version < 13)
+ decode_uleb128 (p, &p); /* flags */
int count = decode_uleb128 (p, &p);
- if (flags != 0)
- return NULL;
if (count > *size)
frames = (MethodDesc **)malloc (count * sizeof (void*));
else
frames = sframes;
for (i = 0; i < count; ++i) {
intptr_t ptrdiff = decode_sleb128 (p, &p);
- frames [i] = lookup_method (ptr_base + ptrdiff);
+ if (ctx->data_version > 12) {
+ *method_base += ptrdiff;
+ frames [i] = lookup_method (*method_base);
+ } else {
+ frames [i] = lookup_method (ptr_base + ptrdiff);
+ }
}
*size = count;
*endp = p;
if (new_size > max_heap_size)
max_heap_size = new_size;
} else if (subtype == TYPE_GC_EVENT) {
- uint64_t ev = decode_uleb128 (p, &p);
- int gen = decode_uleb128 (p, &p);
+ uint64_t ev;
+ if (ctx->data_version > 12)
+ ev = *p++;
+ else
+ ev = decode_uleb128 (p, &p);
+ int gen;
+ if (ctx->data_version > 12)
+ gen = *p++;
+ else
+ gen = decode_uleb128 (p, &p);
if (debug)
fprintf (outfile, "gc event for gen%d: %s at %llu (thread: 0x%zx)\n", gen, gc_event_name (ev), (unsigned long long) time_base, thread->thread_id);
if (gen > 2) {
intptr_t objdiff = decode_sleb128 (p, &p);
if (has_bt) {
num_bt = 8;
- frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
+ frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
if (!frames) {
fprintf (outfile, "Cannot load backtrace\n");
return 0;
uint32_t handle = decode_uleb128 (p, &p);
if (has_bt) {
num_bt = 8;
- frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
+ frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
if (!frames) {
fprintf (outfile, "Cannot load backtrace\n");
return 0;
fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
if (frames != sframes)
free (frames);
+ } else if (subtype == TYPE_GC_FINALIZE_START) {
+ // TODO: Generate a finalizer report based on these events.
+ if (debug)
+ fprintf (outfile, "gc finalizer queue being processed at %llu\n", (unsigned long long) time_base);
+ } else if (subtype == TYPE_GC_FINALIZE_END) {
+ if (debug)
+ fprintf (outfile, "gc finalizer queue finished processing at %llu\n", (unsigned long long) time_base);
+ } else if (subtype == TYPE_GC_FINALIZE_OBJECT_START) {
+ intptr_t objdiff = decode_sleb128 (p, &p);
+ if (debug)
+ fprintf (outfile, "gc finalizing object %p at %llu\n", (void *) OBJ_ADDR (objdiff), (unsigned long long) time_base);
+ } else if (subtype == TYPE_GC_FINALIZE_OBJECT_END) {
+ intptr_t objdiff = decode_sleb128 (p, &p);
+ if (debug)
+ fprintf (outfile, "gc finalized object %p at %llu\n", (void *) OBJ_ADDR (objdiff), (unsigned long long) time_base);
}
break;
}
time_base += tdiff;
if (mtype == TYPE_CLASS) {
intptr_t imptrdiff = decode_sleb128 (p, &p);
- uint64_t flags = decode_uleb128 (p, &p);
- if (flags) {
- fprintf (outfile, "non-zero flags in class\n");
- return 0;
- }
+ if (ctx->data_version < 13)
+ decode_uleb128 (p, &p); /* flags */
if (debug)
fprintf (outfile, "%s class %p (%s in %p) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (void*)(ptr_base + imptrdiff), (unsigned long long) time_base);
if (subtype == TYPE_END_LOAD)
while (*p) p++;
p++;
} else if (mtype == TYPE_IMAGE) {
- uint64_t flags = decode_uleb128 (p, &p);
- if (flags) {
- fprintf (outfile, "non-zero flags in image\n");
- return 0;
- }
+ if (ctx->data_version < 13)
+ decode_uleb128 (p, &p); /* flags */
if (debug)
fprintf (outfile, "%s image %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
if (subtype == TYPE_END_LOAD)
while (*p) p++;
p++;
} else if (mtype == TYPE_ASSEMBLY) {
- uint64_t flags = decode_uleb128 (p, &p);
- if (flags) {
- fprintf (outfile, "non-zero flags in assembly\n");
- return 0;
- }
+ if (ctx->data_version < 13)
+ decode_uleb128 (p, &p); /* flags */
if (debug)
fprintf (outfile, "%s assembly %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
if (subtype == TYPE_END_LOAD)
while (*p) p++;
p++;
} else if (mtype == TYPE_DOMAIN) {
- uint64_t flags = decode_uleb128 (p, &p);
- if (flags) {
- fprintf (outfile, "non-zero flags in domain\n");
- return 0;
- }
+ if (ctx->data_version < 13)
+ decode_uleb128 (p, &p); /* flags */
DomainContext *nd = get_domain (ctx, ptr_base + ptrdiff);
/* no subtype means it's a name event, rather than start/stop */
if (subtype == 0)
p++;
}
} else if (mtype == TYPE_CONTEXT) {
- uint64_t flags = decode_uleb128 (p, &p);
- if (flags) {
- fprintf (outfile, "non-zero flags in context\n");
- return 0;
- }
+ if (ctx->data_version < 13)
+ decode_uleb128 (p, &p); /* flags */
intptr_t domaindiff = decode_sleb128 (p, &p);
if (debug)
fprintf (outfile, "%s context %p (%p) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), (void *) (ptr_base + domaindiff), (unsigned long long) time_base);
if (subtype == TYPE_END_LOAD)
get_remctx (ctx, ptr_base + ptrdiff)->domain_id = ptr_base + domaindiff;
} else if (mtype == TYPE_THREAD) {
- uint64_t flags = decode_uleb128 (p, &p);
- if (flags) {
- fprintf (outfile, "non-zero flags in thread\n");
- return 0;
- }
+ if (ctx->data_version < 13)
+ decode_uleb128 (p, &p); /* flags */
ThreadContext *nt = get_thread (ctx, ptr_base + ptrdiff);
/* no subtype means it's a name event, rather than start/stop */
if (subtype == 0)
fprintf (outfile, "alloced object %p, size %llu (%s) at %llu\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) len, lookup_class (ptr_base + ptrdiff)->name, (unsigned long long) time_base);
if (has_bt) {
num_bt = 8;
- frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
+ frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
if (!frames) {
fprintf (outfile, "Cannot load backtrace\n");
return 0;
if (subtype == TYPE_HEAP_OBJECT) {
HeapObjectDesc *ho = NULL;
int i;
- intptr_t objdiff = decode_sleb128 (p + 1, &p);
+ intptr_t objdiff;
+ if (ctx->data_version > 12) {
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ objdiff = decode_sleb128 (p, &p);
+ } else
+ objdiff = decode_sleb128 (p + 1, &p);
intptr_t ptrdiff = decode_sleb128 (p, &p);
uint64_t size = decode_uleb128 (p, &p);
uintptr_t num = decode_uleb128 (p, &p);
if (debug && size)
fprintf (outfile, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) size, cd->name, num);
} else if (subtype == TYPE_HEAP_ROOT) {
- uintptr_t num = decode_uleb128 (p + 1, &p);
+ uintptr_t num;
+ if (ctx->data_version > 12) {
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ num = decode_uleb128 (p, &p);
+ } else
+ num = decode_uleb128 (p + 1, &p);
uintptr_t gc_num G_GNUC_UNUSED = decode_uleb128 (p, &p);
int i;
for (i = 0; i < num; ++i) {
intptr_t objdiff = decode_sleb128 (p, &p);
- int root_type = decode_uleb128 (p, &p);
+ int root_type;
+ if (ctx->data_version > 12)
+ root_type = *p++;
+ else
+ root_type = decode_uleb128 (p, &p);
/* we just discard the extra info for now */
uintptr_t extra_info = decode_uleb128 (p, &p);
if (debug)
}
if (has_bt) {
num_bt = 8;
- frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
+ frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
if (!frames) {
fprintf (outfile, "Cannot load backtrace\n");
return 0;
if (!(time_base >= time_from && time_base < time_to))
record = 0;
if (subtype == TYPE_CLAUSE) {
- int clause_type = decode_uleb128 (p, &p);
+ int clause_type;
+ if (ctx->data_version > 12)
+ clause_type = *p++;
+ else
+ clause_type = decode_uleb128 (p, &p);
int clause_num = decode_uleb128 (p, &p);
int64_t ptrdiff = decode_sleb128 (p, &p);
method_base += ptrdiff;
throw_count++;
if (has_bt) {
has_bt = 8;
- frames = decode_bt (sframes, &has_bt, p, &p, ptr_base);
+ frames = decode_bt (ctx, sframes, &has_bt, p, &p, ptr_base, &method_base);
if (!frames) {
fprintf (outfile, "Cannot load backtrace\n");
return 0;
LOG_TIME (time_base, tdiff);
time_base += tdiff;
if (subtype == TYPE_JITHELPER) {
- int type = decode_uleb128 (p, &p);
+ int type;
+ if (ctx->data_version > 12)
+ type = *p++;
+ else
+ type = decode_uleb128 (p, &p);
intptr_t codediff = decode_sleb128 (p, &p);
int codelen = decode_uleb128 (p, &p);
const char *name;
int subtype = *p & 0xf0;
if (subtype == TYPE_SAMPLE_HIT) {
int i;
- int sample_type = decode_uleb128 (p + 1, &p);
- uint64_t tstamp = decode_uleb128 (p, &p);
+ int sample_type;
+ uint64_t tstamp;
+ if (ctx->data_version > 12) {
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ sample_type = *p++;
+ tstamp = time_base;
+ } else {
+ sample_type = decode_uleb128 (p + 1, &p);
+ tstamp = decode_uleb128 (p, &p);
+ }
void *tid = (void *) thread_id;
if (ctx->data_version > 10)
tid = (void *) (ptr_base + decode_sleb128 (p, &p));
for (i = 0; i < count; ++i) {
MethodDesc *method;
int64_t ptrdiff = decode_sleb128 (p, &p);
- int il_offset = decode_sleb128 (p, &p);
- int native_offset = decode_sleb128 (p, &p);
method_base += ptrdiff;
method = lookup_method (method_base);
if (debug)
- fprintf (outfile, "sample hit bt %d: %s at IL offset %d (native: %d)\n", i, method->name, il_offset, native_offset);
+ fprintf (outfile, "sample hit bt %d: %s\n", i, method->name);
+ if (ctx->data_version < 13) {
+ decode_sleb128 (p, &p); /* il offset */
+ decode_sleb128 (p, &p); /* native offset */
+ }
}
}
} else if (subtype == TYPE_SAMPLE_USYM) {
/* un unmanaged symbol description */
- uintptr_t addr = ptr_base + decode_sleb128 (p + 1, &p);
+ uintptr_t addr;
+ if (ctx->data_version > 12) {
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ addr = ptr_base + decode_sleb128 (p, &p);
+ } else
+ addr = ptr_base + decode_sleb128 (p + 1, &p);
uintptr_t size = decode_uleb128 (p, &p);
char *name;
name = pstrdup ((char*)p);
while (*p) p++;
p++;
} else if (subtype == TYPE_SAMPLE_COUNTERS_DESC) {
- uint64_t i, len = decode_uleb128 (p + 1, &p);
+ uint64_t i, len;
+ if (ctx->data_version > 12) {
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ len = decode_uleb128 (p, &p);
+ } else
+ len = decode_uleb128 (p + 1, &p);
for (i = 0; i < len; i++) {
uint64_t type, unit, variance, index;
uint64_t section = decode_uleb128 (p, &p);
}
name = pstrdup ((char*)p);
while (*p++);
- type = decode_uleb128 (p, &p);
- unit = decode_uleb128 (p, &p);
- variance = decode_uleb128 (p, &p);
+ if (ctx->data_version > 12) {
+ type = *p++;
+ unit = *p++;
+ variance = *p++;
+ } else {
+ type = decode_uleb128 (p, &p);
+ unit = decode_uleb128 (p, &p);
+ variance = decode_uleb128 (p, &p);
+ }
index = decode_uleb128 (p, &p);
add_counter (section_str, name, (int)type, (int)unit, (int)variance, (int)index);
}
int i;
CounterValue *value, *previous = NULL;
CounterList *list;
- uint64_t timestamp = decode_uleb128 (p + 1, &p);
+ uint64_t timestamp; // milliseconds since startup
+ if (ctx->data_version > 12) {
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ timestamp = (time_base - startup_time) / 1000 / 1000;
+ } else
+ timestamp = decode_uleb128 (p + 1, &p);
uint64_t time_between = timestamp / 1000 * 1000 * 1000 * 1000 + startup_time;
while (1) {
uint64_t type, index = decode_uleb128 (p, &p);
}
}
- type = decode_uleb128 (p, &p);
+ if (ctx->data_version > 12)
+ type = *p++;
+ else
+ type = decode_uleb128 (p, &p);
value = (CounterValue *)calloc (1, sizeof (CounterValue));
value->timestamp = timestamp;
int token, n_offsets, method_id;
p++;
+
+ if (ctx->data_version > 12) {
+ uint64_t tdiff = decode_uleb128 (p, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ }
+
assembly = (const char *)p; while (*p) p++; p++;
klass = (const char *)p; while (*p) p++; p++;
name = (const char *)p; while (*p) p++; p++;
int offset, count, line, column, method_id;
p++;
+
+ if (ctx->data_version > 12) {
+ uint64_t tdiff = decode_uleb128 (p, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ }
+
method_id = decode_uleb128 (p, &p);
offset = decode_uleb128 (p, &p);
count = decode_uleb128 (p, &p);
int number_of_methods, fully_covered, partially_covered;
p++;
+ if (ctx->data_version > 12) {
+ uint64_t tdiff = decode_uleb128 (p, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ }
+
name = (char *)p; while (*p) p++; p++;
guid = (char *)p; while (*p) p++; p++;
filename = (char *)p; while (*p) p++; p++;
int number_of_methods, fully_covered, partially_covered;
p++;
+ if (ctx->data_version > 12) {
+ uint64_t tdiff = decode_uleb128 (p, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ }
+
assembly_name = (char *)p; while (*p) p++; p++;
class_name = (char *)p; while (*p) p++; p++;
number_of_methods = decode_uleb128 (p, &p);
}
break;
}
+ case TYPE_META: {
+ int subtype = *p & 0xf0;
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ if (subtype == TYPE_SYNC_POINT) {
+ int type = *p++;
+ if (debug)
+ fprintf (outfile, "sync point %i (%s)\n", type, sync_point_name (type));
+ }
+ break;
+ }
default:
fprintf (outfile, "unhandled profiler event: 0x%x at file offset: %llu + %lld (len: %d\n)\n", *p, (unsigned long long) file_offset, (long long) (p - ctx->buf), len);
exit (1);
DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_METHOD);
DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_STATEMENT);
DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_CLASS);
+
+ DUMP_EVENT_STAT (TYPE_META, TYPE_SYNC_POINT);
}
#include <mono/metadata/threads.h>
#include <mono/metadata/mono-gc.h>
#include <mono/metadata/debug-helpers.h>
+#include <mono/metadata/mono-gc.h>
#include <mono/metadata/mono-perfcounters.h>
#include <mono/metadata/appdomain.h>
#include <mono/metadata/assembly.h>
#include <mono/utils/mono-os-mutex.h>
#include <mono/utils/mono-os-semaphore.h>
#include <mono/utils/mono-conc-hashtable.h>
+#include <mono/utils/mono-linked-list-set.h>
#include <mono/utils/lock-free-alloc.h>
#include <mono/utils/lock-free-queue.h>
#include <mono/utils/hazard-pointer.h>
#include <mono/utils/mono-threads.h>
+#include <mono/utils/mono-threads-api.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
/* Worst-case size in bytes of a 64-bit value encoded with LEB128. */
#define LEB128_SIZE 10
-/* Size in bytes of the event ID prefix. */
-#define EVENT_SIZE 1
+/* Size of a value encoded as a single byte. */
+#define BYTE_SIZE 1
+/* Size in bytes of the event prefix (ID + time). */
+#define EVENT_SIZE (BYTE_SIZE + LEB128_SIZE)
static int nocalls = 0;
static int notraces = 0;
static gint32 class_loads;
static gint32 class_unloads;
-typedef struct _LogBuffer LogBuffer;
+static MonoLinkedListSet profiler_thread_list;
/*
* file format:
* strings are represented as a 0-terminated utf8 sequence.
*
* backtrace format:
- * [flags: uleb128] must be 0
* [num: uleb128] number of frames following
* [frame: sleb128]* num MonoMethod pointers as differences from ptr_base
*
* type GC format:
* type: TYPE_GC
* exinfo: one of TYPE_GC_EVENT, TYPE_GC_RESIZE, TYPE_GC_MOVE, TYPE_GC_HANDLE_CREATED[_BT],
- * TYPE_GC_HANDLE_DESTROYED[_BT]
+ * TYPE_GC_HANDLE_DESTROYED[_BT], TYPE_GC_FINALIZE_START, TYPE_GC_FINALIZE_END,
+ * TYPE_GC_FINALIZE_OBJECT_START, TYPE_GC_FINALIZE_OBJECT_END
* [time diff: uleb128] nanoseconds since last timing
* if exinfo == TYPE_GC_RESIZE
* [heap_size: uleb128] new heap size
* if exinfo == TYPE_GC_EVENT
- * [event type: uleb128] GC event (MONO_GC_EVENT_* from profiler.h)
- * [generation: uleb128] GC generation event refers to
+ * [event type: byte] GC event (MONO_GC_EVENT_* from profiler.h)
+ * [generation: byte] GC generation event refers to
* if exinfo == TYPE_GC_MOVE
* [num_objects: uleb128] number of object moves that follow
* [objaddr: sleb128]+ num_objects object pointer differences from obj_base
* upper bits reserved as flags
* [handle: uleb128] GC handle value
* If exinfo == TYPE_GC_HANDLE_DESTROYED_BT, a backtrace follows.
+ * if exinfo == TYPE_GC_FINALIZE_OBJECT_{START,END}
+ * [object: sleb128] the object as a difference from obj_base
*
* type metadata format:
* type: TYPE_METADATA
* [pointer: sleb128] pointer of the metadata type depending on mtype
* if mtype == TYPE_CLASS
* [image: sleb128] MonoImage* as a pointer difference from ptr_base
- * [flags: uleb128] must be 0
* [name: string] full class name
* if mtype == TYPE_IMAGE
- * [flags: uleb128] must be 0
* [name: string] image file name
* if mtype == TYPE_ASSEMBLY
- * [flags: uleb128] must be 0
* [name: string] assembly name
- * if mtype == TYPE_DOMAIN
- * [flags: uleb128] must be 0
* if mtype == TYPE_DOMAIN && exinfo == 0
* [name: string] domain friendly name
* if mtype == TYPE_CONTEXT
- * [flags: uleb128] must be 0
* [domain: sleb128] domain id as pointer
- * if mtype == TYPE_THREAD && (format_version < 11 || (format_version > 10 && exinfo == 0))
- * [flags: uleb128] must be 0
+ * if mtype == TYPE_THREAD && exinfo == 0
* [name: string] thread name
*
* type method format:
* exinfo: one of: TYPE_JITHELPER
* [time diff: uleb128] nanoseconds since last timing
* if exinfo == TYPE_JITHELPER
- * [type: uleb128] MonoProfilerCodeBufferType enum value
+ * [type: byte] MonoProfilerCodeBufferType enum value
* [buffer address: sleb128] pointer to the native code as a diff from ptr_base
* [buffer size: uleb128] size of the generated code
* if type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
* [class: sleb128] the object MonoClass* as a difference from ptr_base
* [size: uleb128] size of the object on the heap
* [num_refs: uleb128] number of object references
- * if (format version > 1) each referenced objref is preceded by a
- * uleb128 encoded offset: the first offset is from the object address
- * and each next offset is relative to the previous one
+ * each referenced objref is preceded by a uleb128 encoded offset: the
+ * first offset is from the object address and each next offset is relative
+ * to the previous one
* [objrefs: sleb128]+ object referenced as a difference from obj_base
* The same object can appear multiple times, but only the first time
* with size != 0: in the other cases this data will only be used to
* [num_roots: uleb128] number of root references
* [num_gc: uleb128] number of major gcs
* [object: sleb128] the object as a difference from obj_base
- * [root_type: uleb128] the root_type: MonoProfileGCRootType (profiler.h)
+ * [root_type: byte] the root_type: MonoProfileGCRootType (profiler.h)
* [extra_info: uleb128] the extra_info value
* object, root_type and extra_info are repeated num_roots times
*
* type: TYPE_SAMPLE
* exinfo: one of TYPE_SAMPLE_HIT, TYPE_SAMPLE_USYM, TYPE_SAMPLE_UBIN, TYPE_SAMPLE_COUNTERS_DESC, TYPE_SAMPLE_COUNTERS
* if exinfo == TYPE_SAMPLE_HIT
- * [sample_type: uleb128] type of sample (SAMPLE_*)
+ * [sample_type: byte] type of sample (SAMPLE_*)
* [timestamp: uleb128] nanoseconds since startup (note: different from other timestamps!)
- * if (format_version > 10)
- * [thread: sleb128] thread id as difference from ptr_base
+ * [thread: sleb128] thread id as difference from ptr_base
* [count: uleb128] number of following instruction addresses
* [ip: sleb128]* instruction pointer as difference from ptr_base
- * if (format_version > 5)
- * [mbt_count: uleb128] number of managed backtrace info triplets (method + IL offset + native offset)
- * [method: sleb128]* MonoMethod* as a pointer difference from the last such
- * pointer or the buffer method_base (the first such method can be also indentified by ip, but this is not neccessarily true)
- * [il_offset: sleb128]* IL offset inside method where the hit occurred
- * [native_offset: sleb128]* native offset inside method where the hit occurred
+ * [mbt_count: uleb128] number of managed backtrace frames
+ * [method: sleb128]* MonoMethod* as a pointer difference from the last such
+ * pointer or the buffer method_base (the first such method can be also indentified by ip, but this is not neccessarily true)
* if exinfo == TYPE_SAMPLE_USYM
* [address: sleb128] symbol address as a difference from ptr_base
* [size: uleb128] symbol size (may be 0 if unknown)
* if section == MONO_COUNTER_PERFCOUNTERS:
* [section_name: string] section name of counter
* [name: string] name of counter
- * [type: uleb128] type of counter
- * [unit: uleb128] unit of counter
- * [variance: uleb128] variance of counter
+ * [type: byte] type of counter
+ * [unit: byte] unit of counter
+ * [variance: byte] variance of counter
* [index: uleb128] unique index of counter
* if exinfo == TYPE_SAMPLE_COUNTERS
* [timestamp: uleb128] sampling timestamp
* [index: uleb128] unique index of counter
* if index == 0:
* break
- * [type: uleb128] type of counter value
+ * [type: byte] type of counter value
* if type == string:
* if value == null:
* [0: uleb128] 0 -> value is null
* [partially_covered: uleb128] the number of partially covered methods
* currently partially_covered will always be 0, and fully_covered is the
* number of methods that are fully and partially covered.
- */
-
-/*
- * Format oddities that we ought to fix:
*
- * - Methods written in emit_bt () should be based on the buffer's base
- * method instead of the base pointer.
- * - The TYPE_SAMPLE_HIT event contains (currently) pointless data like
- * always-one unmanaged frame count and always-zero IL offsets.
- *
- * These are mostly small things and are not worth a format change by
- * themselves. They should be done when some other major change has to
- * be done to the format.
+ * type meta format:
+ * type: TYPE_META
+ * exinfo: one of: TYPE_SYNC_POINT
+ * [time diff: uleb128] nanoseconds since last timing
+ * if exinfo == TYPE_SYNC_POINT
+ * [type: byte] MonoProfilerSyncPointType enum value
*/
// Pending data to be written to the log, for a single thread.
// Threads periodically flush their own LogBuffers by calling safe_send
+typedef struct _LogBuffer LogBuffer;
struct _LogBuffer {
// Next (older) LogBuffer in processing queue
LogBuffer *next;
uintptr_t last_method;
uintptr_t obj_base;
uintptr_t thread_id;
- int locked;
- int call_depth;
// Bytes allocated for this LogBuffer
int size;
unsigned char buf [1];
};
+typedef struct {
+ MonoLinkedListSetNode node;
+
+ // The current log buffer for this thread.
+ LogBuffer *buffer;
+
+ // Methods referenced by events in `buffer`, see `MethodInfo`.
+ GPtrArray *methods;
+
+ // Current call depth for enter/leave events.
+ int call_depth;
+
+ // Indicates whether this thread is currently writing to its `buffer`.
+ int busy;
+} MonoProfilerThread;
+
static inline void
ign_res (int G_GNUC_UNUSED unused, ...)
{
}
-#define ENTER_LOG(lb,str) if ((lb)->locked) {ign_res (write(2, str, strlen(str))); ign_res (write(2, "\n", 1));return;} else {(lb)->locked++;}
-#define EXIT_LOG(lb) (lb)->locked--;
+/*
+ * These macros create a scope to avoid leaking the buffer returned
+ * from ensure_logbuf () as it may have been invalidated by a GC
+ * thread during STW. If you called init_thread () with add_to_lls =
+ * FALSE, then don't use these macros.
+ */
+
+#define ENTER_LOG \
+ do { \
+ buffer_lock (); \
+ g_assert (!PROF_TLS_GET ()->busy++ && "Why are we trying to write a new event while already writing one?")
-typedef struct _BinaryObject BinaryObject;
+#define EXIT_LOG \
+ PROF_TLS_GET ()->busy--; \
+ buffer_unlock (); \
+ } while (0)
+
+static volatile gint32 buffer_rwlock_count;
+static volatile gpointer buffer_rwlock_exclusive;
+
+// Can be used recursively.
+static void
+buffer_lock (void)
+{
+ /*
+ * If the thread holding the exclusive lock tries to modify the
+ * reader count, just make it a no-op. This way, we also avoid
+ * invoking the GC safe point macros below, which could break if
+ * done from a thread that is currently the initiator of STW.
+ *
+ * In other words, we rely on the fact that the GC thread takes
+ * the exclusive lock in the gc_event () callback when the world
+ * is about to stop.
+ */
+ if (InterlockedReadPointer (&buffer_rwlock_exclusive) != (gpointer) thread_id ()) {
+ MONO_ENTER_GC_SAFE;
+
+ while (InterlockedReadPointer (&buffer_rwlock_exclusive))
+ mono_thread_info_yield ();
+
+ InterlockedIncrement (&buffer_rwlock_count);
+
+ MONO_EXIT_GC_SAFE;
+ }
+
+ mono_memory_barrier ();
+}
+
+static void
+buffer_unlock (void)
+{
+ mono_memory_barrier ();
+
+ // See the comment in buffer_lock ().
+ if (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id ())
+ return;
+
+ g_assert (InterlockedRead (&buffer_rwlock_count) && "Why are we trying to decrement a zero reader count?");
+
+ InterlockedDecrement (&buffer_rwlock_count);
+}
+
+// Cannot be used recursively.
+static void
+buffer_lock_excl (void)
+{
+ gpointer tid = (gpointer) thread_id ();
+ g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) != tid && "Why are we taking the exclusive lock twice?");
+
+ MONO_ENTER_GC_SAFE;
+
+ while (InterlockedCompareExchangePointer (&buffer_rwlock_exclusive, tid, 0))
+ mono_thread_info_yield ();
+
+ while (InterlockedRead (&buffer_rwlock_count))
+ mono_thread_info_yield ();
+
+ MONO_EXIT_GC_SAFE;
+
+ mono_memory_barrier ();
+}
+
+static void
+buffer_unlock_excl (void)
+{
+ mono_memory_barrier ();
+
+ g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) && "Why is the exclusive lock not held?");
+ g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id () && "Why does another thread hold the exclusive lock?");
+ g_assert (!InterlockedRead (&buffer_rwlock_count) && "Why are there readers when the exclusive lock is held?");
+
+ InterlockedWritePointer (&buffer_rwlock_exclusive, NULL);
+}
+
+typedef struct _BinaryObject BinaryObject;
struct _BinaryObject {
BinaryObject *next;
void *addr;
pthread_t dumper_thread;
#endif
volatile gint32 run_writer_thread;
+ MonoLockFreeAllocSizeClass writer_entry_size_class;
+ MonoLockFreeAllocator writer_entry_allocator;
MonoLockFreeQueue writer_queue;
MonoSemType writer_queue_sem;
MonoConcurrentHashTable *method_table;
GPtrArray *coverage_filters;
};
-typedef struct _WriterQueueEntry WriterQueueEntry;
-struct _WriterQueueEntry {
+typedef struct {
MonoLockFreeQueueNode node;
GPtrArray *methods;
LogBuffer *buffer;
-};
+} WriterQueueEntry;
+
+#define WRITER_ENTRY_BLOCK_SIZE (mono_pagesize ())
-typedef struct _MethodInfo MethodInfo;
-struct _MethodInfo {
+typedef struct {
MonoMethod *method;
MonoJitInfo *ji;
uint64_t time;
-};
-
-#ifdef TLS_INIT
-#undef TLS_INIT
-#endif
+} MethodInfo;
#ifdef HOST_WIN32
-#define TLS_SET(x,y) (TlsSetValue (x, y))
-#define TLS_GET(t,x) ((t *) TlsGetValue (x))
-#define TLS_INIT(x) (x = TlsAlloc ())
-static int tlsbuffer;
-static int tlsmethodlist;
+
+#define PROF_TLS_SET(VAL) (TlsSetValue (profiler_tls, (VAL)))
+#define PROF_TLS_GET() ((MonoProfilerThread *) TlsGetValue (profiler_tls))
+#define PROF_TLS_INIT() (profiler_tls = TlsAlloc ())
+#define PROF_TLS_FREE() (TlsFree (profiler_tls))
+
+static DWORD profiler_tls;
+
#elif HAVE_KW_THREAD
-#define TLS_SET(x,y) (x = y)
-#define TLS_GET(t,x) (x)
-#define TLS_INIT(x)
-static __thread LogBuffer* tlsbuffer = NULL;
-static __thread GPtrArray* tlsmethodlist = NULL;
+
+#define PROF_TLS_SET(VAL) (profiler_tls = (VAL))
+#define PROF_TLS_GET() (profiler_tls)
+#define PROF_TLS_INIT()
+#define PROF_TLS_FREE()
+
+static __thread MonoProfilerThread *profiler_tls;
+
#else
-#define TLS_SET(x,y) (pthread_setspecific (x, y))
-#define TLS_GET(t,x) ((t *) pthread_getspecific (x))
-#define TLS_INIT(x) (pthread_key_create (&x, NULL))
-static pthread_key_t tlsbuffer;
-static pthread_key_t tlsmethodlist;
-#endif
-static void safe_send (MonoProfiler *profiler, LogBuffer *logbuffer);
+#define PROF_TLS_SET(VAL) (pthread_setspecific (profiler_tls, (VAL)))
+#define PROF_TLS_GET() ((MonoProfilerThread *) pthread_getspecific (profiler_tls))
+#define PROF_TLS_INIT() (pthread_key_create (&profiler_tls, NULL))
+#define PROF_TLS_FREE() (pthread_key_delete (&profiler_tls))
+
+static pthread_key_t profiler_tls;
+
+#endif
static char*
pstrdup (const char *s)
}
static void
-init_thread (void)
+init_buffer_state (MonoProfilerThread *thread)
{
- if (!TLS_GET (LogBuffer, tlsbuffer)) {
- LogBuffer *logbuffer = create_buffer ();
- TLS_SET (tlsbuffer, logbuffer);
- logbuffer->thread_id = thread_id ();
- }
- if (!TLS_GET (GPtrArray, tlsmethodlist)) {
- GPtrArray *methodlist = g_ptr_array_new ();
- TLS_SET (tlsmethodlist, methodlist);
+ thread->buffer = create_buffer ();
+ thread->methods = NULL;
+}
+
+static void
+clear_hazard_pointers (MonoThreadHazardPointers *hp)
+{
+ mono_hazard_pointer_clear (hp, 0);
+ mono_hazard_pointer_clear (hp, 1);
+ mono_hazard_pointer_clear (hp, 2);
+}
+
+static MonoProfilerThread *
+init_thread (gboolean add_to_lls)
+{
+ MonoProfilerThread *thread = PROF_TLS_GET ();
+
+ /*
+ * Sometimes we may try to initialize a thread twice. One example is the
+ * main thread: We initialize it when setting up the profiler, but we will
+ * also get a thread_start () callback for it. Another example is when
+ * attaching new threads to the runtime: We may get a gc_alloc () callback
+ * for that thread's thread object (where we initialize it), soon followed
+ * by a thread_start () callback.
+ *
+ * These cases are harmless anyhow. Just return if we've already done the
+ * initialization work.
+ */
+ if (thread)
+ return thread;
+
+ thread = malloc (sizeof (MonoProfilerThread));
+ thread->node.key = thread_id ();
+ thread->call_depth = 0;
+ thread->busy = 0;
+
+ init_buffer_state (thread);
+
+ /*
+ * Some internal profiler threads don't need to be cleaned up
+ * by the main thread on shutdown.
+ */
+ if (add_to_lls) {
+ MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
+ g_assert (mono_lls_insert (&profiler_thread_list, hp, &thread->node) && "Why can't we insert the thread in the LLS?");
+ clear_hazard_pointers (hp);
}
- //printf ("thread %p at time %llu\n", (void*)logbuffer->thread_id, logbuffer->time_base);
+ PROF_TLS_SET (thread);
+
+ return thread;
+}
+
+// Only valid if init_thread () was called with add_to_lls = FALSE.
+static void
+deinit_thread (MonoProfilerThread *thread)
+{
+ free (thread);
+ PROF_TLS_SET (NULL);
}
static LogBuffer *
if (old && old->cursor + bytes + 100 < old->buf_end)
return old;
- LogBuffer *new_ = (LogBuffer *)create_buffer ();
- new_->thread_id = thread_id ();
+ LogBuffer *new_ = create_buffer ();
new_->next = old;
- if (old)
- new_->call_depth = old->call_depth;
-
return new_;
}
-static LogBuffer*
-ensure_logbuf (int bytes)
+// Only valid if init_thread () was called with add_to_lls = FALSE.
+static LogBuffer *
+ensure_logbuf_unsafe (int bytes)
{
- LogBuffer *old = TLS_GET (LogBuffer, tlsbuffer);
+ MonoProfilerThread *thread = PROF_TLS_GET ();
+ LogBuffer *old = thread->buffer;
LogBuffer *new_ = ensure_logbuf_inner (old, bytes);
if (new_ == old)
return old; // Still enough space.
- TLS_SET (tlsbuffer, new_);
- init_thread ();
+ thread->buffer = new_;
return new_;
}
+/*
+ * Any calls to this function should be wrapped in the ENTER_LOG and
+ * EXIT_LOG macros to prevent the returned pointer from leaking
+ * outside of the critical region created by the calls to buffer_lock ()
+ * and buffer_unlock () that those macros insert. If the pointer leaks,
+ * it can and will lead to crashes as the GC or helper thread may
+ * invalidate the pointer at any time.
+ *
+ * Note: If you're calling from a thread that called init_thread () with
+ * add_to_lls = FALSE, you should use ensure_logbuf_unsafe () and omit
+ * the macros.
+ */
+static LogBuffer*
+ensure_logbuf (int bytes)
+{
+ g_assert (PROF_TLS_GET ()->busy && "Why are we trying to expand our buffer without the busy flag set?");
+
+ return ensure_logbuf_unsafe (bytes);
+}
+
static void
emit_byte (LogBuffer *logbuffer, int value)
{
assert (logbuffer->cursor <= logbuffer->buf_end);
}
+static void
+emit_event_time (LogBuffer *logbuffer, int event, uint64_t time)
+{
+ emit_byte (logbuffer, event);
+ emit_time (logbuffer, time);
+}
+
+static void
+emit_event (LogBuffer *logbuffer, int event)
+{
+ emit_event_time (logbuffer, event, current_time ());
+}
+
static void
emit_svalue (LogBuffer *logbuffer, int64_t value)
{
*/
//g_assert (ji);
- MethodInfo *info = (MethodInfo *)malloc (sizeof (MethodInfo));
+ MethodInfo *info = (MethodInfo *) malloc (sizeof (MethodInfo));
info->method = method;
info->ji = ji;
info->time = current_time ();
- g_ptr_array_add (TLS_GET (GPtrArray, tlsmethodlist), info);
+ MonoProfilerThread *thread = PROF_TLS_GET ();
+ GPtrArray *arr = thread->methods ? thread->methods : (thread->methods = g_ptr_array_new ());
+ g_ptr_array_add (arr, info);
}
}
emit_method_inner (logbuffer, method);
}
-static void
-emit_method_as_ptr (MonoProfiler *prof, LogBuffer *logbuffer, MonoMethod *method)
-{
- register_method_local (prof, method, NULL);
- emit_ptr (logbuffer, method);
-}
-
static void
emit_obj (LogBuffer *logbuffer, void *ptr)
{
}
static void
-send_buffer (MonoProfiler *prof, GPtrArray *methods, LogBuffer *buffer)
+send_buffer (MonoProfiler *prof, MonoProfilerThread *thread)
{
- WriterQueueEntry *entry = (WriterQueueEntry *)calloc (1, sizeof (WriterQueueEntry));
+ WriterQueueEntry *entry = mono_lock_free_alloc (&prof->writer_entry_allocator);
+ entry->methods = thread->methods;
+ entry->buffer = thread->buffer;
+
mono_lock_free_queue_node_init (&entry->node, FALSE);
- entry->methods = methods;
- entry->buffer = buffer;
+
mono_lock_free_queue_enqueue (&prof->writer_queue, &entry->node);
mono_os_sem_post (&prof->writer_queue_sem);
}
+static void
+remove_thread (MonoProfiler *prof, MonoProfilerThread *thread, gboolean from_callback)
+{
+ MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
+
+ if (mono_lls_remove (&profiler_thread_list, hp, &thread->node)) {
+ LogBuffer *buffer = thread->buffer;
+
+ /*
+ * No need to take the buffer lock here as no other threads can
+ * be accessing this buffer anymore.
+ */
+
+ if (!from_callback) {
+ /*
+ * The thread is being cleaned up by the main thread during
+ * shutdown. This typically happens for internal runtime
+ * threads. We need to synthesize a thread end event.
+ */
+
+ buffer = ensure_logbuf_inner (buffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* tid */
+ );
+
+ emit_event (buffer, TYPE_END_UNLOAD | TYPE_METADATA);
+ emit_byte (buffer, TYPE_THREAD);
+ emit_ptr (buffer, (void *) thread->node.key);
+ }
+
+ send_buffer (prof, thread);
+
+ mono_thread_hazardous_try_free (thread, free);
+ }
+
+ clear_hazard_pointers (hp);
+
+ if (from_callback)
+ PROF_TLS_SET (NULL);
+}
+
static void
dump_buffer (MonoProfiler *profiler, LogBuffer *buf)
{
free_buffer (buf, buf->size);
}
+static void
+dump_buffer_threadless (MonoProfiler *profiler, LogBuffer *buf)
+{
+ for (LogBuffer *iter = buf; iter; iter = iter->next)
+ iter->thread_id = 0;
+
+ dump_buffer (profiler, buf);
+}
+
static void
process_requests (MonoProfiler *profiler)
{
}
static void counters_init (MonoProfiler *profiler);
-static void counters_sample (MonoProfiler *profiler, uint64_t timestamp, gboolean threadless);
+static void counters_sample (MonoProfiler *profiler, uint64_t timestamp);
-/*
- * Can be called only at safe callback locations.
- */
static void
-safe_send (MonoProfiler *profiler, LogBuffer *logbuffer)
+safe_send (MonoProfiler *profiler)
{
/* We need the runtime initialized so that we have threads and hazard
* pointers available. Otherwise, the lock free queue will not work and
if (!InterlockedRead (&runtime_inited))
return;
- int cd = logbuffer->call_depth;
+ MonoProfilerThread *thread = PROF_TLS_GET ();
- send_buffer (profiler, TLS_GET (GPtrArray, tlsmethodlist), TLS_GET (LogBuffer, tlsbuffer));
+ buffer_lock ();
- TLS_SET (tlsbuffer, NULL);
- TLS_SET (tlsmethodlist, NULL);
+ send_buffer (profiler, thread);
+ init_buffer_state (thread);
- init_thread ();
+ buffer_unlock ();
+}
- TLS_GET (LogBuffer, tlsbuffer)->call_depth = cd;
+static void
+send_if_needed (MonoProfiler *prof)
+{
+ if (PROF_TLS_GET ()->buffer->next)
+ safe_send (prof);
}
static void
-safe_send_threadless (MonoProfiler *prof, LogBuffer *buf)
+safe_send_threadless (MonoProfiler *prof)
{
+ LogBuffer *buf = PROF_TLS_GET ()->buffer;
+
for (LogBuffer *iter = buf; iter; iter = iter->next)
iter->thread_id = 0;
- safe_send (prof, buf);
+ safe_send (prof);
+}
+
+static void
+send_if_needed_threadless (MonoProfiler *prof)
+{
+ if (PROF_TLS_GET ()->buffer->next)
+ safe_send_threadless (prof);
+}
+
+// Assumes that the exclusive lock is held.
+static void
+sync_point_flush (MonoProfiler *prof)
+{
+ g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id () && "Why don't we hold the exclusive lock?");
+
+ MONO_LLS_FOREACH_SAFE (&profiler_thread_list, MonoProfilerThread, thread) {
+ send_buffer (prof, thread);
+ init_buffer_state (thread);
+ } MONO_LLS_FOREACH_SAFE_END
+}
+
+// Assumes that the exclusive lock is held.
+static void
+sync_point_mark (MonoProfiler *prof, MonoProfilerSyncPointType type)
+{
+ g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id () && "Why don't we hold the exclusive lock?");
+
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* type */
+ );
+
+ emit_event (logbuffer, TYPE_META | TYPE_SYNC_POINT);
+ emit_byte (logbuffer, type);
+
+ EXIT_LOG;
+
+ switch (type) {
+ case SYNC_POINT_PERIODIC:
+ safe_send_threadless (prof);
+ break;
+ case SYNC_POINT_WORLD_STOP:
+ case SYNC_POINT_WORLD_START:
+ safe_send (prof);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+// Assumes that the exclusive lock is held.
+static void
+sync_point (MonoProfiler *prof, MonoProfilerSyncPointType type)
+{
+ sync_point_flush (prof);
+ sync_point_mark (prof, type);
}
static int
gc_reference (MonoObject *obj, MonoClass *klass, uintptr_t size, uintptr_t num, MonoObject **refs, uintptr_t *offsets, void *data)
{
- int i;
- uintptr_t last_offset = 0;
- //const char *name = mono_class_get_name (klass);
+ /* account for object alignment in the heap */
+ size += 7;
+ size &= ~7;
+
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
LEB128_SIZE /* obj */ +
LEB128_SIZE /* ref */
)
);
- emit_byte (logbuffer, TYPE_HEAP_OBJECT | TYPE_HEAP);
+
+ emit_event (logbuffer, TYPE_HEAP_OBJECT | TYPE_HEAP);
emit_obj (logbuffer, obj);
emit_ptr (logbuffer, klass);
- /* account for object alignment in the heap */
- size += 7;
- size &= ~7;
emit_value (logbuffer, size);
emit_value (logbuffer, num);
- for (i = 0; i < num; ++i) {
+
+ uintptr_t last_offset = 0;
+
+ for (int i = 0; i < num; ++i) {
emit_value (logbuffer, offsets [i] - last_offset);
last_offset = offsets [i];
emit_obj (logbuffer, refs [i]);
}
- //if (num)
- // printf ("obj: %p, klass: %s, refs: %d, size: %d\n", obj, name, (int)num, (int)size);
+
+ EXIT_LOG;
+
return 0;
}
static void
heap_walk (MonoProfiler *profiler)
{
- int do_walk = 0;
- uint64_t now;
- LogBuffer *logbuffer;
if (!do_heap_shot)
return;
- logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */
- );
- now = current_time ();
- if (hs_mode_ms && (now - last_hs_time)/1000000 >= hs_mode_ms)
- do_walk = 1;
+
+ gboolean do_walk = 0;
+ uint64_t now = current_time ();
+
+ if (hs_mode_ms && (now - last_hs_time) / 1000000 >= hs_mode_ms)
+ do_walk = TRUE;
else if (hs_mode_gc && (gc_count % hs_mode_gc) == 0)
- do_walk = 1;
+ do_walk = TRUE;
else if (hs_mode_ondemand)
do_walk = heapshot_requested;
else if (!hs_mode_ms && !hs_mode_gc && profiler->last_gc_gen_started == mono_gc_max_generation ())
- do_walk = 1;
+ do_walk = TRUE;
if (!do_walk)
return;
+
heapshot_requested = 0;
- emit_byte (logbuffer, TYPE_HEAP_START | TYPE_HEAP);
- emit_time (logbuffer, now);
+
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */
+ );
+
+ emit_event (logbuffer, TYPE_HEAP_START | TYPE_HEAP);
+
+ EXIT_LOG;
+
mono_gc_walk_heap (0, gc_reference, NULL);
- logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */
+
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */
);
+
now = current_time ();
- emit_byte (logbuffer, TYPE_HEAP_END | TYPE_HEAP);
- emit_time (logbuffer, now);
+
+ emit_event (logbuffer, TYPE_HEAP_END | TYPE_HEAP);
+
+ EXIT_LOG;
+
last_hs_time = now;
}
static void
-gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
- uint64_t now;
+gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation)
+{
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- LEB128_SIZE /* gc event */ +
- LEB128_SIZE /* generation */
+ BYTE_SIZE /* gc event */ +
+ BYTE_SIZE /* generation */
);
- now = current_time ();
- ENTER_LOG (logbuffer, "gcevent");
- emit_byte (logbuffer, TYPE_GC_EVENT | TYPE_GC);
- emit_time (logbuffer, now);
- emit_value (logbuffer, ev);
- emit_value (logbuffer, generation);
- /* to deal with nested gen1 after gen0 started */
- if (ev == MONO_GC_EVENT_START) {
+
+ emit_event (logbuffer, TYPE_GC_EVENT | TYPE_GC);
+ emit_byte (logbuffer, ev);
+ emit_byte (logbuffer, generation);
+
+ EXIT_LOG;
+
+ switch (ev) {
+ case MONO_GC_EVENT_START:
+ /* to deal with nested gen1 after gen0 started */
profiler->last_gc_gen_started = generation;
+
if (generation == mono_gc_max_generation ())
gc_count++;
- }
- if (ev == MONO_GC_EVENT_PRE_START_WORLD)
+ break;
+ case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED:
+ /*
+ * Ensure that no thread can be in the middle of writing to
+ * a buffer when the world stops...
+ */
+ buffer_lock_excl ();
+ break;
+ case MONO_GC_EVENT_POST_STOP_WORLD:
+ /*
+ * ... So that we now have a consistent view of all buffers.
+ * This allows us to flush them. We need to do this because
+ * they may contain object allocation events that need to be
+ * committed to the log file before any object move events
+ * that will be produced during this GC.
+ */
+ sync_point (profiler, SYNC_POINT_WORLD_STOP);
+ break;
+ case MONO_GC_EVENT_PRE_START_WORLD:
heap_walk (profiler);
- EXIT_LOG (logbuffer);
- if (ev == MONO_GC_EVENT_POST_START_WORLD)
- safe_send (profiler, logbuffer);
- //printf ("gc event %d for generation %d\n", ev, generation);
+ break;
+ case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED:
+ /*
+ * Similarly, we must now make sure that any object moves
+ * written to the GC thread's buffer are flushed. Otherwise,
+ * object allocation events for certain addresses could come
+ * after the move events that made those addresses available.
+ */
+ sync_point_mark (profiler, SYNC_POINT_WORLD_START);
+
+ /*
+ * Finally, it is safe to allow other threads to write to
+ * their buffers again.
+ */
+ buffer_unlock_excl ();
+ break;
+ default:
+ break;
+ }
}
static void
-gc_resize (MonoProfiler *profiler, int64_t new_size) {
- uint64_t now;
+gc_resize (MonoProfiler *profiler, int64_t new_size)
+{
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
LEB128_SIZE /* new size */
);
- now = current_time ();
- ENTER_LOG (logbuffer, "gcresize");
- emit_byte (logbuffer, TYPE_GC_RESIZE | TYPE_GC);
- emit_time (logbuffer, now);
+
+ emit_event (logbuffer, TYPE_GC_RESIZE | TYPE_GC);
emit_value (logbuffer, new_size);
- //printf ("gc resized to %lld\n", new_size);
- EXIT_LOG (logbuffer);
+
+ EXIT_LOG;
}
// If you alter MAX_FRAMES, you may need to alter SAMPLE_BLOCK_SIZE too.
*/
if (data->count > num_frames)
printf ("bad num frames: %d\n", data->count);
- emit_value (logbuffer, 0); /* flags */
emit_value (logbuffer, data->count);
//if (*p != data.count) {
// printf ("bad num frames enc at %d: %d -> %d\n", count, data.count, *p); printf ("frames end: %p->%p\n", p, logbuffer->cursor); exit(0);}
while (data->count) {
- emit_method_as_ptr (prof, logbuffer, data->methods [--data->count]);
+ emit_method (prof, logbuffer, data->methods [--data->count]);
}
}
static void
gc_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
{
- uint64_t now;
- uintptr_t len;
- int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces)? TYPE_ALLOC_BT: 0;
+ init_thread (TRUE);
+
+ int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces) ? TYPE_ALLOC_BT : 0;
FrameData data;
- LogBuffer *logbuffer;
- len = mono_object_get_size (obj);
+ uintptr_t len = mono_object_get_size (obj);
/* account for object alignment in the heap */
len += 7;
len &= ~7;
+
if (do_bt)
collect_bt (&data);
- logbuffer = ensure_logbuf (
+
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
LEB128_SIZE /* klass */ +
LEB128_SIZE /* obj */ +
LEB128_SIZE /* size */ +
(do_bt ? (
- LEB128_SIZE /* flags */ +
LEB128_SIZE /* count */ +
data.count * (
LEB128_SIZE /* method */
)
) : 0)
);
- now = current_time ();
- ENTER_LOG (logbuffer, "gcalloc");
- emit_byte (logbuffer, do_bt | TYPE_ALLOC);
- emit_time (logbuffer, now);
+
+ emit_event (logbuffer, do_bt | TYPE_ALLOC);
emit_ptr (logbuffer, klass);
emit_obj (logbuffer, obj);
emit_value (logbuffer, len);
+
if (do_bt)
emit_bt (prof, logbuffer, &data);
- EXIT_LOG (logbuffer);
- if (logbuffer->next)
- safe_send (prof, logbuffer);
+
+ EXIT_LOG;
+
+ send_if_needed (prof);
+
process_requests (prof);
- //printf ("gc alloc %s at %p\n", mono_class_get_name (klass), obj);
}
static void
gc_moves (MonoProfiler *prof, void **objects, int num)
{
- int i;
- uint64_t now;
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
LEB128_SIZE /* num */ +
num * (
LEB128_SIZE /* object */
)
);
- now = current_time ();
- ENTER_LOG (logbuffer, "gcmove");
- emit_byte (logbuffer, TYPE_GC_MOVE | TYPE_GC);
- emit_time (logbuffer, now);
+
+ emit_event (logbuffer, TYPE_GC_MOVE | TYPE_GC);
emit_value (logbuffer, num);
- for (i = 0; i < num; ++i)
+
+ for (int i = 0; i < num; ++i)
emit_obj (logbuffer, objects [i]);
- //printf ("gc moved %d objects\n", num/2);
- EXIT_LOG (logbuffer);
+
+ EXIT_LOG;
}
static void
gc_roots (MonoProfiler *prof, int num, void **objects, int *root_types, uintptr_t *extra_info)
{
- int i;
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
LEB128_SIZE /* num */ +
LEB128_SIZE /* extra info */
)
);
- ENTER_LOG (logbuffer, "gcroots");
- emit_byte (logbuffer, TYPE_HEAP_ROOT | TYPE_HEAP);
+
+ emit_event (logbuffer, TYPE_HEAP_ROOT | TYPE_HEAP);
emit_value (logbuffer, num);
emit_value (logbuffer, mono_gc_collection_count (mono_gc_max_generation ()));
- for (i = 0; i < num; ++i) {
+
+ for (int i = 0; i < num; ++i) {
emit_obj (logbuffer, objects [i]);
- emit_value (logbuffer, root_types [i]);
+ emit_byte (logbuffer, root_types [i]);
emit_value (logbuffer, extra_info [i]);
}
- EXIT_LOG (logbuffer);
+
+ EXIT_LOG;
}
static void
gc_handle (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj)
{
int do_bt = nocalls && InterlockedRead (&runtime_inited) && !notraces;
- uint64_t now;
FrameData data;
if (do_bt)
collect_bt (&data);
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
LEB128_SIZE /* type */ +
LEB128_SIZE /* handle */ +
(op == MONO_PROFILER_GC_HANDLE_CREATED ? (
LEB128_SIZE /* obj */
) : 0) +
(do_bt ? (
- LEB128_SIZE /* flags */ +
LEB128_SIZE /* count */ +
data.count * (
LEB128_SIZE /* method */
) : 0)
);
- now = current_time ();
- ENTER_LOG (logbuffer, "gchandle");
-
if (op == MONO_PROFILER_GC_HANDLE_CREATED)
- emit_byte (logbuffer, (do_bt ? TYPE_GC_HANDLE_CREATED_BT : TYPE_GC_HANDLE_CREATED) | TYPE_GC);
+ emit_event (logbuffer, (do_bt ? TYPE_GC_HANDLE_CREATED_BT : TYPE_GC_HANDLE_CREATED) | TYPE_GC);
else if (op == MONO_PROFILER_GC_HANDLE_DESTROYED)
- emit_byte (logbuffer, (do_bt ? TYPE_GC_HANDLE_DESTROYED_BT : TYPE_GC_HANDLE_DESTROYED) | TYPE_GC);
+ emit_event (logbuffer, (do_bt ? TYPE_GC_HANDLE_DESTROYED_BT : TYPE_GC_HANDLE_DESTROYED) | TYPE_GC);
else
g_assert_not_reached ();
- emit_time (logbuffer, now);
emit_value (logbuffer, type);
emit_value (logbuffer, handle);
if (do_bt)
emit_bt (prof, logbuffer, &data);
- EXIT_LOG (logbuffer);
+ EXIT_LOG;
+
+ process_requests (prof);
+}
+
+static void
+finalize_begin (MonoProfiler *prof)
+{
+ ENTER_LOG;
+
+ LogBuffer *buf = ensure_logbuf (
+ EVENT_SIZE /* event */
+ );
+
+ emit_event (buf, TYPE_GC_FINALIZE_START | TYPE_GC);
+
+ EXIT_LOG;
+
+ process_requests (prof);
+}
+
+static void
+finalize_end (MonoProfiler *prof)
+{
+ ENTER_LOG;
+
+ LogBuffer *buf = ensure_logbuf (
+ EVENT_SIZE /* event */
+ );
+
+ emit_event (buf, TYPE_GC_FINALIZE_END | TYPE_GC);
+
+ EXIT_LOG;
+
+ process_requests (prof);
+}
+
+static void
+finalize_object_begin (MonoProfiler *prof, MonoObject *obj)
+{
+ ENTER_LOG;
+
+ LogBuffer *buf = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* obj */
+ );
+
+ emit_event (buf, TYPE_GC_FINALIZE_OBJECT_START | TYPE_GC);
+ emit_obj (buf, obj);
+
+ EXIT_LOG;
+
+ process_requests (prof);
+}
+
+static void
+finalize_object_end (MonoProfiler *prof, MonoObject *obj)
+{
+ ENTER_LOG;
+
+ LogBuffer *buf = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* obj */
+ );
+
+ emit_event (buf, TYPE_GC_FINALIZE_OBJECT_END | TYPE_GC);
+ emit_obj (buf, obj);
+
+ EXIT_LOG;
+
process_requests (prof);
}
static void
image_loaded (MonoProfiler *prof, MonoImage *image, int result)
{
- uint64_t now;
- const char *name;
- int nlen;
- LogBuffer *logbuffer;
if (result != MONO_PROFILE_OK)
return;
- name = mono_image_get_filename (image);
- nlen = strlen (name) + 1;
- logbuffer = ensure_logbuf (
+
+ const char *name = mono_image_get_filename (image);
+ int nlen = strlen (name) + 1;
+
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- EVENT_SIZE /* type */ +
+ BYTE_SIZE /* type */ +
LEB128_SIZE /* image */ +
- LEB128_SIZE /* flags */ +
nlen /* name */
);
- now = current_time ();
- ENTER_LOG (logbuffer, "image");
- emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
- emit_time (logbuffer, now);
+
+ emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
emit_byte (logbuffer, TYPE_IMAGE);
emit_ptr (logbuffer, image);
- emit_value (logbuffer, 0); /* flags */
memcpy (logbuffer->cursor, name, nlen);
logbuffer->cursor += nlen;
- //printf ("loaded image %p (%s)\n", image, name);
- EXIT_LOG (logbuffer);
- if (logbuffer->next)
- safe_send (prof, logbuffer);
+
+ EXIT_LOG;
+
+ send_if_needed (prof);
+
process_requests (prof);
InterlockedIncrement (&image_loads);
{
const char *name = mono_image_get_filename (image);
int nlen = strlen (name) + 1;
+
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- EVENT_SIZE /* type */ +
+ BYTE_SIZE /* type */ +
LEB128_SIZE /* image */ +
- LEB128_SIZE /* flags */ +
nlen /* name */
);
- uint64_t now = current_time ();
- ENTER_LOG (logbuffer, "image-unload");
- emit_byte (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
- emit_time (logbuffer, now);
+ emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
emit_byte (logbuffer, TYPE_IMAGE);
emit_ptr (logbuffer, image);
- emit_value (logbuffer, 0); /* flags */
memcpy (logbuffer->cursor, name, nlen);
logbuffer->cursor += nlen;
- EXIT_LOG (logbuffer);
- if (logbuffer->next)
- safe_send (prof, logbuffer);
+ EXIT_LOG;
+
+ send_if_needed (prof);
process_requests (prof);
char *name = mono_stringify_assembly_name (mono_assembly_get_name (assembly));
int nlen = strlen (name) + 1;
+
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- EVENT_SIZE /* type */ +
+ BYTE_SIZE /* type */ +
LEB128_SIZE /* assembly */ +
- LEB128_SIZE /* flags */ +
nlen /* name */
);
- uint64_t now = current_time ();
- ENTER_LOG (logbuffer, "assembly-load");
- emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
- emit_time (logbuffer, now);
+ emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
emit_byte (logbuffer, TYPE_ASSEMBLY);
emit_ptr (logbuffer, assembly);
- emit_value (logbuffer, 0); /* flags */
memcpy (logbuffer->cursor, name, nlen);
logbuffer->cursor += nlen;
- EXIT_LOG (logbuffer);
+
+ EXIT_LOG;
mono_free (name);
- if (logbuffer->next)
- safe_send (prof, logbuffer);
+ send_if_needed (prof);
process_requests (prof);
{
char *name = mono_stringify_assembly_name (mono_assembly_get_name (assembly));
int nlen = strlen (name) + 1;
+
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- EVENT_SIZE /* type */ +
+ BYTE_SIZE /* type */ +
LEB128_SIZE /* assembly */ +
- LEB128_SIZE /* flags */ +
nlen /* name */
);
- uint64_t now = current_time ();
- ENTER_LOG (logbuffer, "assembly-unload");
- emit_byte (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
- emit_time (logbuffer, now);
+ emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
emit_byte (logbuffer, TYPE_ASSEMBLY);
emit_ptr (logbuffer, assembly);
- emit_value (logbuffer, 0); /* flags */
memcpy (logbuffer->cursor, name, nlen);
logbuffer->cursor += nlen;
- EXIT_LOG (logbuffer);
+
+ EXIT_LOG;
mono_free (name);
- if (logbuffer->next)
- safe_send (prof, logbuffer);
+ send_if_needed (prof);
process_requests (prof);
static void
class_loaded (MonoProfiler *prof, MonoClass *klass, int result)
{
- uint64_t now;
- char *name;
- int nlen;
- MonoImage *image;
- LogBuffer *logbuffer;
if (result != MONO_PROFILE_OK)
return;
+
+ char *name;
+
if (InterlockedRead (&runtime_inited))
name = mono_type_get_name (mono_class_get_type (klass));
else
name = type_name (klass);
- nlen = strlen (name) + 1;
- image = mono_class_get_image (klass);
- logbuffer = ensure_logbuf (
+
+ int nlen = strlen (name) + 1;
+ MonoImage *image = mono_class_get_image (klass);
+
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- EVENT_SIZE /* type */ +
+ BYTE_SIZE /* type */ +
LEB128_SIZE /* klass */ +
LEB128_SIZE /* image */ +
- LEB128_SIZE /* flags */ +
nlen /* name */
);
- now = current_time ();
- ENTER_LOG (logbuffer, "class");
- emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
- emit_time (logbuffer, now);
+
+ emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
emit_byte (logbuffer, TYPE_CLASS);
emit_ptr (logbuffer, klass);
emit_ptr (logbuffer, image);
- emit_value (logbuffer, 0); /* flags */
memcpy (logbuffer->cursor, name, nlen);
logbuffer->cursor += nlen;
- //printf ("loaded class %p (%s)\n", klass, name);
+
+ EXIT_LOG;
+
if (runtime_inited)
mono_free (name);
else
free (name);
- EXIT_LOG (logbuffer);
- if (logbuffer->next)
- safe_send (prof, logbuffer);
+
+ send_if_needed (prof);
+
process_requests (prof);
InterlockedIncrement (&class_loads);
int nlen = strlen (name) + 1;
MonoImage *image = mono_class_get_image (klass);
+
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- EVENT_SIZE /* type */ +
+ BYTE_SIZE /* type */ +
LEB128_SIZE /* klass */ +
LEB128_SIZE /* image */ +
- LEB128_SIZE /* flags */ +
nlen /* name */
);
- uint64_t now = current_time ();
- ENTER_LOG (logbuffer, "class-unload");
- emit_byte (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
- emit_time (logbuffer, now);
+ emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
emit_byte (logbuffer, TYPE_CLASS);
emit_ptr (logbuffer, klass);
emit_ptr (logbuffer, image);
- emit_value (logbuffer, 0); /* flags */
memcpy (logbuffer->cursor, name, nlen);
logbuffer->cursor += nlen;
- EXIT_LOG (logbuffer);
+
+ EXIT_LOG;
if (runtime_inited)
mono_free (name);
else
free (name);
- if (logbuffer->next)
- safe_send (prof, logbuffer);
+ send_if_needed (prof);
process_requests (prof);
static void
method_enter (MonoProfiler *prof, MonoMethod *method)
{
- uint64_t now = current_time ();
+#ifndef DISABLE_HELPER_THREAD
+ process_method_enter_coverage (prof, method);
+#endif /* DISABLE_HELPER_THREAD */
+
+ if (PROF_TLS_GET ()->call_depth++ <= max_call_depth) {
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* method */
+ );
-#ifndef DISABLE_HELPER_THREAD
- process_method_enter_coverage (prof, method);
-#endif /* DISABLE_HELPER_THREAD */
+ emit_event (logbuffer, TYPE_ENTER | TYPE_METHOD);
+ emit_method (prof, logbuffer, method);
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- LEB128_SIZE /* method */
- );
- if (logbuffer->call_depth++ > max_call_depth)
- return;
- ENTER_LOG (logbuffer, "enter");
- emit_byte (logbuffer, TYPE_ENTER | TYPE_METHOD);
- emit_time (logbuffer, now);
- emit_method (prof, logbuffer, method);
- EXIT_LOG (logbuffer);
+ EXIT_LOG;
+ }
+
+ send_if_needed (prof);
process_requests (prof);
}
static void
method_leave (MonoProfiler *prof, MonoMethod *method)
{
- uint64_t now;
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- LEB128_SIZE /* method */
- );
- if (--logbuffer->call_depth > max_call_depth)
- return;
- now = current_time ();
- ENTER_LOG (logbuffer, "leave");
- emit_byte (logbuffer, TYPE_LEAVE | TYPE_METHOD);
- emit_time (logbuffer, now);
- emit_method (prof, logbuffer, method);
- EXIT_LOG (logbuffer);
- if (logbuffer->next)
- safe_send (prof, logbuffer);
+ if (--PROF_TLS_GET ()->call_depth <= max_call_depth) {
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* method */
+ );
+
+ emit_event (logbuffer, TYPE_LEAVE | TYPE_METHOD);
+ emit_method (prof, logbuffer, method);
+
+ EXIT_LOG;
+ }
+
+ send_if_needed (prof);
+
process_requests (prof);
}
static void
method_exc_leave (MonoProfiler *prof, MonoMethod *method)
{
- uint64_t now;
- LogBuffer *logbuffer;
- if (nocalls)
- return;
- logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- LEB128_SIZE /* method */
- );
- if (--logbuffer->call_depth > max_call_depth)
- return;
- now = current_time ();
- ENTER_LOG (logbuffer, "eleave");
- emit_byte (logbuffer, TYPE_EXC_LEAVE | TYPE_METHOD);
- emit_time (logbuffer, now);
- emit_method (prof, logbuffer, method);
- EXIT_LOG (logbuffer);
+ if (!nocalls && --PROF_TLS_GET ()->call_depth <= max_call_depth) {
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* method */
+ );
+
+ emit_event (logbuffer, TYPE_EXC_LEAVE | TYPE_METHOD);
+ emit_method (prof, logbuffer, method);
+
+ EXIT_LOG;
+ }
+
+ send_if_needed (prof);
+
process_requests (prof);
}
static void
code_buffer_new (MonoProfiler *prof, void *buffer, int size, MonoProfilerCodeBufferType type, void *data)
{
- uint64_t now;
- int nlen;
char *name;
- LogBuffer *logbuffer;
+ int nlen;
+
if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
- name = (char *)data;
+ name = (char *) data;
nlen = strlen (name) + 1;
} else {
name = NULL;
nlen = 0;
}
- logbuffer = ensure_logbuf (
+
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- LEB128_SIZE /* type */ +
+ BYTE_SIZE /* type */ +
LEB128_SIZE /* buffer */ +
LEB128_SIZE /* size */ +
(name ? (
nlen /* name */
) : 0)
);
- now = current_time ();
- ENTER_LOG (logbuffer, "code buffer");
- emit_byte (logbuffer, TYPE_JITHELPER | TYPE_RUNTIME);
- emit_time (logbuffer, now);
- emit_value (logbuffer, type);
+
+ emit_event (logbuffer, TYPE_JITHELPER | TYPE_RUNTIME);
+ emit_byte (logbuffer, type);
emit_ptr (logbuffer, buffer);
emit_value (logbuffer, size);
+
if (name) {
memcpy (logbuffer->cursor, name, nlen);
logbuffer->cursor += nlen;
}
- EXIT_LOG (logbuffer);
+
+ EXIT_LOG;
+
process_requests (prof);
}
static void
throw_exc (MonoProfiler *prof, MonoObject *object)
{
- int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces)? TYPE_EXCEPTION_BT: 0;
- uint64_t now;
+ int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces) ? TYPE_EXCEPTION_BT : 0;
FrameData data;
- LogBuffer *logbuffer;
+
if (do_bt)
collect_bt (&data);
- logbuffer = ensure_logbuf (
+
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
LEB128_SIZE /* object */ +
(do_bt ? (
- LEB128_SIZE /* flags */ +
LEB128_SIZE /* count */ +
data.count * (
LEB128_SIZE /* method */
)
) : 0)
);
- now = current_time ();
- ENTER_LOG (logbuffer, "throw");
- emit_byte (logbuffer, do_bt | TYPE_EXCEPTION);
- emit_time (logbuffer, now);
+
+ emit_event (logbuffer, do_bt | TYPE_EXCEPTION);
emit_obj (logbuffer, object);
+
if (do_bt)
emit_bt (prof, logbuffer, &data);
- EXIT_LOG (logbuffer);
+
+ EXIT_LOG;
+
process_requests (prof);
}
static void
clause_exc (MonoProfiler *prof, MonoMethod *method, int clause_type, int clause_num)
{
- uint64_t now;
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- LEB128_SIZE /* clause type */ +
+ BYTE_SIZE /* clause type */ +
LEB128_SIZE /* clause num */ +
LEB128_SIZE /* method */
);
- now = current_time ();
- ENTER_LOG (logbuffer, "clause");
- emit_byte (logbuffer, TYPE_EXCEPTION | TYPE_CLAUSE);
- emit_time (logbuffer, now);
- emit_value (logbuffer, clause_type);
+
+ emit_event (logbuffer, TYPE_EXCEPTION | TYPE_CLAUSE);
+ emit_byte (logbuffer, clause_type);
emit_value (logbuffer, clause_num);
emit_method (prof, logbuffer, method);
- EXIT_LOG (logbuffer);
+
+ EXIT_LOG;
process_requests (prof);
}
static void
monitor_event (MonoProfiler *profiler, MonoObject *object, MonoProfilerMonitorEvent event)
{
- int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces && event == MONO_PROFILER_MONITOR_CONTENTION)? TYPE_MONITOR_BT: 0;
- uint64_t now;
+ int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces && event == MONO_PROFILER_MONITOR_CONTENTION) ? TYPE_MONITOR_BT : 0;
FrameData data;
- LogBuffer *logbuffer;
+
if (do_bt)
collect_bt (&data);
- logbuffer = ensure_logbuf (
+
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
LEB128_SIZE /* object */ +
(do_bt ? (
- LEB128_SIZE /* flags */ +
LEB128_SIZE /* count */ +
data.count * (
LEB128_SIZE /* method */
)
) : 0)
);
- now = current_time ();
- ENTER_LOG (logbuffer, "monitor");
- emit_byte (logbuffer, (event << 4) | do_bt | TYPE_MONITOR);
- emit_time (logbuffer, now);
+
+ emit_event (logbuffer, (event << 4) | do_bt | TYPE_MONITOR);
emit_obj (logbuffer, object);
+
if (do_bt)
emit_bt (profiler, logbuffer, &data);
- EXIT_LOG (logbuffer);
+
+ EXIT_LOG;
+
process_requests (profiler);
}
static void
thread_start (MonoProfiler *prof, uintptr_t tid)
{
- //printf ("thread start %p\n", (void*)tid);
- init_thread ();
+ init_thread (TRUE);
+
+ ENTER_LOG;
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- EVENT_SIZE /* type */ +
- LEB128_SIZE /* tid */ +
- LEB128_SIZE /* flags */
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* tid */
);
- uint64_t now = current_time ();
- ENTER_LOG (logbuffer, "thread-start");
- emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
- emit_time (logbuffer, now);
+ emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
emit_byte (logbuffer, TYPE_THREAD);
emit_ptr (logbuffer, (void*) tid);
- emit_value (logbuffer, 0); /* flags */
- EXIT_LOG (logbuffer);
- if (logbuffer->next)
- safe_send (prof, logbuffer);
+ EXIT_LOG;
+
+ send_if_needed (prof);
process_requests (prof);
static void
thread_end (MonoProfiler *prof, uintptr_t tid)
{
- if (TLS_GET (LogBuffer, tlsbuffer)) {
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- EVENT_SIZE /* type */ +
- LEB128_SIZE /* tid */ +
- LEB128_SIZE /* flags */
- );
- uint64_t now = current_time ();
+ ENTER_LOG;
- ENTER_LOG (logbuffer, "thread-end");
- emit_byte (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
- emit_time (logbuffer, now);
- emit_byte (logbuffer, TYPE_THREAD);
- emit_ptr (logbuffer, (void*) tid);
- emit_value (logbuffer, 0); /* flags */
- EXIT_LOG (logbuffer);
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* tid */
+ );
+
+ emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+ emit_byte (logbuffer, TYPE_THREAD);
+ emit_ptr (logbuffer, (void*) tid);
- send_buffer (prof, TLS_GET (GPtrArray, tlsmethodlist), logbuffer);
+ EXIT_LOG;
- /* Don't process requests as the thread is detached from the runtime. */
- }
+ // Don't process requests as the thread is detached from the runtime.
- TLS_SET (tlsbuffer, NULL);
- TLS_SET (tlsmethodlist, NULL);
+ remove_thread (prof, PROF_TLS_GET (), TRUE);
InterlockedIncrement (&thread_ends);
}
if (result != MONO_PROFILE_OK)
return;
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- EVENT_SIZE /* type */ +
- LEB128_SIZE /* domain id */ +
- LEB128_SIZE /* flags */
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* domain id */
);
- uint64_t now = current_time ();
- ENTER_LOG (logbuffer, "domain-start");
- emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
- emit_time (logbuffer, now);
+ emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
emit_byte (logbuffer, TYPE_DOMAIN);
emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
- emit_value (logbuffer, 0); /* flags */
- EXIT_LOG (logbuffer);
- if (logbuffer->next)
- safe_send (prof, logbuffer);
+ EXIT_LOG;
+
+ send_if_needed (prof);
process_requests (prof);
static void
domain_unloaded (MonoProfiler *prof, MonoDomain *domain)
{
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- EVENT_SIZE /* type */ +
- LEB128_SIZE /* domain id */ +
- LEB128_SIZE /* flags */
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* domain id */
);
- uint64_t now = current_time ();
- ENTER_LOG (logbuffer, "domain-end");
- emit_byte (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
- emit_time (logbuffer, now);
+ emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
emit_byte (logbuffer, TYPE_DOMAIN);
emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
- emit_value (logbuffer, 0); /* flags */
- EXIT_LOG (logbuffer);
- if (logbuffer->next)
- safe_send (prof, logbuffer);
+ EXIT_LOG;
+
+ send_if_needed (prof);
process_requests (prof);
domain_name (MonoProfiler *prof, MonoDomain *domain, const char *name)
{
int nlen = strlen (name) + 1;
+
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- EVENT_SIZE /* type */ +
+ BYTE_SIZE /* type */ +
LEB128_SIZE /* domain id */ +
- LEB128_SIZE /* flags */ +
nlen /* name */
);
- uint64_t now = current_time ();
- ENTER_LOG (logbuffer, "domain-name");
- emit_byte (logbuffer, TYPE_METADATA);
- emit_time (logbuffer, now);
+ emit_event (logbuffer, TYPE_METADATA);
emit_byte (logbuffer, TYPE_DOMAIN);
emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
- emit_value (logbuffer, 0); /* flags */
memcpy (logbuffer->cursor, name, nlen);
logbuffer->cursor += nlen;
- EXIT_LOG (logbuffer);
- if (logbuffer->next)
- safe_send (prof, logbuffer);
+ EXIT_LOG;
+
+ send_if_needed (prof);
process_requests (prof);
}
static void
context_loaded (MonoProfiler *prof, MonoAppContext *context)
{
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- EVENT_SIZE /* type */ +
+ BYTE_SIZE /* type */ +
LEB128_SIZE /* context id */ +
- LEB128_SIZE /* flags */ +
LEB128_SIZE /* domain id */
);
- uint64_t now = current_time ();
- ENTER_LOG (logbuffer, "context-start");
- emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
- emit_time (logbuffer, now);
+ emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
emit_byte (logbuffer, TYPE_CONTEXT);
emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_id (context));
- emit_value (logbuffer, 0); /* flags */
emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
- EXIT_LOG (logbuffer);
- if (logbuffer->next)
- safe_send (prof, logbuffer);
+ EXIT_LOG;
+
+ send_if_needed (prof);
process_requests (prof);
static void
context_unloaded (MonoProfiler *prof, MonoAppContext *context)
{
+ ENTER_LOG;
+
LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- EVENT_SIZE /* type */ +
+ BYTE_SIZE /* type */ +
LEB128_SIZE /* context id */ +
- LEB128_SIZE /* flags */ +
LEB128_SIZE /* domain id */
);
- uint64_t now = current_time ();
- ENTER_LOG (logbuffer, "context-end");
- emit_byte (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
- emit_time (logbuffer, now);
+ emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
emit_byte (logbuffer, TYPE_CONTEXT);
emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_id (context));
- emit_value (logbuffer, 0); /* flags */
emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
- EXIT_LOG (logbuffer);
- if (logbuffer->next)
- safe_send (prof, logbuffer);
+ EXIT_LOG;
+
+ send_if_needed (prof);
process_requests (prof);
thread_name (MonoProfiler *prof, uintptr_t tid, const char *name)
{
int len = strlen (name) + 1;
- uint64_t now;
- LogBuffer *logbuffer;
- logbuffer = ensure_logbuf (
+
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
- EVENT_SIZE /* type */ +
+ BYTE_SIZE /* type */ +
LEB128_SIZE /* tid */ +
- LEB128_SIZE /* flags */ +
len /* name */
);
- now = current_time ();
- ENTER_LOG (logbuffer, "tname");
- emit_byte (logbuffer, TYPE_METADATA);
- emit_time (logbuffer, now);
+
+ emit_event (logbuffer, TYPE_METADATA);
emit_byte (logbuffer, TYPE_THREAD);
emit_ptr (logbuffer, (void*)tid);
- emit_value (logbuffer, 0); /* flags */
memcpy (logbuffer->cursor, name, len);
logbuffer->cursor += len;
- EXIT_LOG (logbuffer);
- if (logbuffer->next)
- safe_send (prof, logbuffer);
+ EXIT_LOG;
+
+ send_if_needed (prof);
process_requests (prof);
}
typedef struct {
MonoLockFreeQueueNode node;
MonoProfiler *prof;
- uint64_t elapsed;
+ uint64_t time;
uintptr_t tid;
void *ip;
int count;
InterlockedIncrement (&sample_hits);
- uint64_t now = current_time ();
-
SampleHit *sample = (SampleHit *) mono_lock_free_queue_dequeue (&profiler->sample_reuse_queue);
if (!sample) {
sample->count = 0;
mono_stack_walk_async_safe (&async_walk_stack, context, sample);
- uintptr_t elapsed = (now - profiler->startup_time) / 10000;
-
- sample->elapsed = elapsed;
+ sample->time = current_time ();
sample->tid = thread_id ();
sample->ip = ip;
if (do_debug) {
int len;
char buf [256];
- snprintf (buf, sizeof (buf), "hit at %p in thread %p after %llu ms\n", ip, (void *) thread_id (), (unsigned long long int) elapsed / 100);
+ snprintf (buf, sizeof (buf), "hit at %p in thread %p after %llu ms\n", ip, (void *) sample->tid, (unsigned long long int) ((sample->time - profiler->startup_time) / 10000 / 100));
len = strlen (buf);
ign_res (write (2, buf, len));
}
static void
dump_ubin (const char *filename, uintptr_t load_addr, uint64_t offset, uintptr_t size)
{
- uint64_t now;
- LogBuffer *logbuffer;
- int len;
- len = strlen (filename) + 1;
- now = current_time ();
- logbuffer = ensure_logbuf (
+ int len = strlen (filename) + 1;
+
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
LEB128_SIZE /* load address */ +
LEB128_SIZE /* offset */ +
LEB128_SIZE /* size */ +
nlen /* file name */
);
- emit_byte (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_UBIN);
- emit_time (logbuffer, now);
+
+ emit_event (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_UBIN);
emit_svalue (logbuffer, load_addr);
emit_uvalue (logbuffer, offset);
emit_uvalue (logbuffer, size);
memcpy (logbuffer->cursor, filename, len);
logbuffer->cursor += len;
+
+ EXIT_LOG;
}
#endif
static void
dump_usym (const char *name, uintptr_t value, uintptr_t size)
{
- LogBuffer *logbuffer;
- int len;
- len = strlen (name) + 1;
- logbuffer = ensure_logbuf (
+ int len = strlen (name) + 1;
+
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
LEB128_SIZE /* value */ +
LEB128_SIZE /* size */ +
len /* name */
);
- emit_byte (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_USYM);
+
+ emit_event (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_USYM);
emit_ptr (logbuffer, (void*)value);
emit_value (logbuffer, size);
memcpy (logbuffer->cursor, name, len);
logbuffer->cursor += len;
+
+ EXIT_LOG;
}
/* ELF code crashes on some systems. */
static void
dump_perf_hits (MonoProfiler *prof, void *buf, int size)
{
- LogBuffer *logbuffer;
int count = 1;
int mbt_count = 0;
void *end = (char*)buf + size;
/*ip = (void*)s->ip;
printf ("sample: %d, size: %d, ip: %p (%s), timestamp: %llu, nframes: %llu\n",
s->h.type, s->h.size, ip, symbol_for (ip), s->timestamp, s->nframes);*/
- logbuffer = ensure_logbuf (
+
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* type */ +
- LEB128_SIZE /* time */ +
+ BYTE_SIZE /* type */ +
LEB128_SIZE /* tid */ +
LEB128_SIZE /* count */ +
count * (
) +
LEB128_SIZE /* managed count */ +
mbt_count * (
- LEB128_SIZE /* method */ +
- LEB128_SIZE /* il offset */ +
- LEB128_SIZE /* native offset */
+ LEB128_SIZE /* method */
)
);
- emit_byte (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_HIT);
- emit_value (logbuffer, sample_type);
- emit_uvalue (logbuffer, s->timestamp - prof->startup_time);
+
+ emit_event (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_HIT);
+ emit_byte (logbuffer, sample_type);
/*
* No useful thread ID to write here, since throughout the
* profiler we use pthread_self () but the ID we get from
emit_ptr (logbuffer, 0);
emit_value (logbuffer, count);
emit_ptr (logbuffer, (void*)(uintptr_t)s->ip);
- add_code_pointer (s->ip);
/* no support here yet for the managed backtrace */
emit_uvalue (logbuffer, mbt_count);
+
+ EXIT_LOG;
+
+ add_code_pointer (s->ip);
buf = (char*)buf + s->h.size;
samples++;
}
}
static void
-counters_emit (MonoProfiler *profiler, gboolean threadless)
+counters_emit (MonoProfiler *profiler)
{
MonoCounterAgent *agent;
- LogBuffer *logbuffer;
int len = 0;
int size =
EVENT_SIZE /* event */ +
size +=
LEB128_SIZE /* section */ +
strlen (mono_counter_get_name (agent->counter)) + 1 /* name */ +
- LEB128_SIZE /* type */ +
- LEB128_SIZE /* unit */ +
- LEB128_SIZE /* variance */ +
+ BYTE_SIZE /* type */ +
+ BYTE_SIZE /* unit */ +
+ BYTE_SIZE /* variance */ +
LEB128_SIZE /* index */
;
return;
}
- logbuffer = ensure_logbuf (size);
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (size);
- ENTER_LOG (logbuffer, "counters");
- emit_byte (logbuffer, TYPE_SAMPLE_COUNTERS_DESC | TYPE_SAMPLE);
+ emit_event (logbuffer, TYPE_SAMPLE_COUNTERS_DESC | TYPE_SAMPLE);
emit_value (logbuffer, len);
+
for (agent = counters; agent; agent = agent->next) {
const char *name;
name = mono_counter_get_name (agent->counter);
emit_value (logbuffer, mono_counter_get_section (agent->counter));
emit_string (logbuffer, name, strlen (name) + 1);
- emit_value (logbuffer, mono_counter_get_type (agent->counter));
- emit_value (logbuffer, mono_counter_get_unit (agent->counter));
- emit_value (logbuffer, mono_counter_get_variance (agent->counter));
+ emit_byte (logbuffer, mono_counter_get_type (agent->counter));
+ emit_byte (logbuffer, mono_counter_get_unit (agent->counter));
+ emit_byte (logbuffer, mono_counter_get_variance (agent->counter));
emit_value (logbuffer, agent->index);
agent->emitted = 1;
}
- EXIT_LOG (logbuffer);
- if (threadless)
- safe_send_threadless (profiler, logbuffer);
- else
- safe_send (profiler, logbuffer);
+ EXIT_LOG;
mono_os_mutex_unlock (&counters_mutex);
}
static void
-counters_sample (MonoProfiler *profiler, uint64_t timestamp, gboolean threadless)
+counters_sample (MonoProfiler *profiler, uint64_t timestamp)
{
MonoCounterAgent *agent;
MonoCounter *counter;
- LogBuffer *logbuffer;
int type;
int buffer_size;
void *buffer;
if (!counters_initialized)
return;
- counters_emit (profiler, threadless);
+ counters_emit (profiler);
buffer_size = 8;
buffer = calloc (1, buffer_size);
mono_os_mutex_lock (&counters_mutex);
size =
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */
+ EVENT_SIZE /* event */
;
for (agent = counters; agent; agent = agent->next) {
size +=
LEB128_SIZE /* index */ +
- LEB128_SIZE /* type */ +
+ BYTE_SIZE /* type */ +
mono_counter_get_size (agent->counter) /* value */
;
}
LEB128_SIZE /* stop marker */
;
- logbuffer = ensure_logbuf (size);
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (size);
+
+ emit_event_time (logbuffer, TYPE_SAMPLE_COUNTERS | TYPE_SAMPLE, timestamp);
- ENTER_LOG (logbuffer, "counters");
- emit_byte (logbuffer, TYPE_SAMPLE_COUNTERS | TYPE_SAMPLE);
- emit_uvalue (logbuffer, timestamp);
for (agent = counters; agent; agent = agent->next) {
size_t size;
}
emit_uvalue (logbuffer, agent->index);
- emit_uvalue (logbuffer, type);
+ emit_byte (logbuffer, type);
switch (type) {
case MONO_COUNTER_INT:
#if SIZEOF_VOID_P == 4
free (buffer);
emit_value (logbuffer, 0);
- EXIT_LOG (logbuffer);
- if (threadless)
- safe_send_threadless (profiler, logbuffer);
- else
- safe_send (profiler, logbuffer);
+ EXIT_LOG;
mono_os_mutex_unlock (&counters_mutex);
}
static PerfCounterAgent *perfcounters = NULL;
static void
-perfcounters_emit (MonoProfiler *profiler, gboolean threadless)
+perfcounters_emit (MonoProfiler *profiler)
{
PerfCounterAgent *pcagent;
- LogBuffer *logbuffer;
int len = 0;
int size =
EVENT_SIZE /* event */ +
LEB128_SIZE /* section */ +
strlen (pcagent->category_name) + 1 /* category name */ +
strlen (pcagent->name) + 1 /* name */ +
- LEB128_SIZE /* type */ +
- LEB128_SIZE /* unit */ +
- LEB128_SIZE /* variance */ +
+ BYTE_SIZE /* type */ +
+ BYTE_SIZE /* unit */ +
+ BYTE_SIZE /* variance */ +
LEB128_SIZE /* index */
;
if (!len)
return;
- logbuffer = ensure_logbuf (size);
+ ENTER_LOG;
- ENTER_LOG (logbuffer, "perfcounters");
- emit_byte (logbuffer, TYPE_SAMPLE_COUNTERS_DESC | TYPE_SAMPLE);
+ LogBuffer *logbuffer = ensure_logbuf (size);
+
+ emit_event (logbuffer, TYPE_SAMPLE_COUNTERS_DESC | TYPE_SAMPLE);
emit_value (logbuffer, len);
+
for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
if (pcagent->emitted)
continue;
emit_value (logbuffer, MONO_COUNTER_PERFCOUNTERS);
emit_string (logbuffer, pcagent->category_name, strlen (pcagent->category_name) + 1);
emit_string (logbuffer, pcagent->name, strlen (pcagent->name) + 1);
- emit_value (logbuffer, MONO_COUNTER_LONG);
- emit_value (logbuffer, MONO_COUNTER_RAW);
- emit_value (logbuffer, MONO_COUNTER_VARIABLE);
+ emit_byte (logbuffer, MONO_COUNTER_LONG);
+ emit_byte (logbuffer, MONO_COUNTER_RAW);
+ emit_byte (logbuffer, MONO_COUNTER_VARIABLE);
emit_value (logbuffer, pcagent->index);
pcagent->emitted = 1;
}
- EXIT_LOG (logbuffer);
- if (threadless)
- safe_send_threadless (profiler, logbuffer);
- else
- safe_send (profiler, logbuffer);
+ EXIT_LOG;
}
static gboolean
}
static void
-perfcounters_sample (MonoProfiler *profiler, uint64_t timestamp, gboolean threadless)
+perfcounters_sample (MonoProfiler *profiler, uint64_t timestamp)
{
PerfCounterAgent *pcagent;
- LogBuffer *logbuffer;
int size;
if (!counters_initialized)
mono_perfcounter_foreach (perfcounters_foreach, perfcounters);
- perfcounters_emit (profiler, threadless);
+ perfcounters_emit (profiler);
size =
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */
+ EVENT_SIZE /* event */
;
for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
size +=
LEB128_SIZE /* index */ +
- LEB128_SIZE /* type */ +
+ BYTE_SIZE /* type */ +
LEB128_SIZE /* value */
;
}
LEB128_SIZE /* stop marker */
;
- logbuffer = ensure_logbuf (size);
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (size);
+
+ emit_event_time (logbuffer, TYPE_SAMPLE_COUNTERS | TYPE_SAMPLE, timestamp);
- ENTER_LOG (logbuffer, "perfcounters");
- emit_byte (logbuffer, TYPE_SAMPLE_COUNTERS | TYPE_SAMPLE);
- emit_uvalue (logbuffer, timestamp);
for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
if (pcagent->deleted || !pcagent->updated)
continue;
emit_uvalue (logbuffer, pcagent->index);
- emit_uvalue (logbuffer, MONO_COUNTER_LONG);
+ emit_byte (logbuffer, MONO_COUNTER_LONG);
emit_svalue (logbuffer, pcagent->value);
pcagent->updated = 0;
}
emit_value (logbuffer, 0);
- EXIT_LOG (logbuffer);
- if (threadless)
- safe_send_threadless (profiler, logbuffer);
- else
- safe_send (profiler, logbuffer);
+ EXIT_LOG;
mono_os_mutex_unlock (&counters_mutex);
}
static void
-counters_and_perfcounters_sample (MonoProfiler *prof, gboolean threadless)
+counters_and_perfcounters_sample (MonoProfiler *prof)
{
- static uint64_t start = -1;
- uint64_t now;
-
- if (start == -1)
- start = current_time ();
+ uint64_t now = current_time ();
- now = current_time ();
- counters_sample (prof, (now - start) / 1000/ 1000, threadless);
- perfcounters_sample (prof, (now - start) / 1000/ 1000, threadless);
+ counters_sample (prof, now);
+ perfcounters_sample (prof, now);
}
#define COVERAGE_DEBUG(x) if (debug_coverage) {x}
static GPtrArray *coverage_data = NULL;
static int previous_offset = 0;
-typedef struct _MethodNode MethodNode;
-struct _MethodNode {
+typedef struct {
MonoLockFreeQueueNode node;
MonoMethod *method;
-};
+} MethodNode;
-typedef struct _CoverageEntry CoverageEntry;
-struct _CoverageEntry {
+typedef struct {
int offset;
int counter;
char *filename;
int line;
int column;
-};
+} CoverageEntry;
static void
free_coverage_entry (gpointer data, gpointer userdata)
MonoImage *image;
char *class_name;
const char *image_name, *method_name, *sig, *first_filename;
- LogBuffer *logbuffer;
guint i;
previous_offset = 0;
sig = sig ? sig : "";
method_name = method_name ? method_name : "";
- logbuffer = ensure_logbuf (
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
strlen (image_name) + 1 /* image name */ +
strlen (class_name) + 1 /* class name */ +
LEB128_SIZE /* method id */ +
LEB128_SIZE /* entries */
);
- ENTER_LOG (logbuffer, "coverage-methods");
- emit_byte (logbuffer, TYPE_COVERAGE_METHOD | TYPE_COVERAGE);
+ emit_event (logbuffer, TYPE_COVERAGE_METHOD | TYPE_COVERAGE);
emit_string (logbuffer, image_name, strlen (image_name) + 1);
emit_string (logbuffer, class_name, strlen (class_name) + 1);
emit_string (logbuffer, method_name, strlen (method_name) + 1);
emit_uvalue (logbuffer, method_id);
emit_value (logbuffer, coverage_data->len);
- EXIT_LOG (logbuffer);
- safe_send (prof, logbuffer);
+ EXIT_LOG;
+
+ send_if_needed (prof);
for (i = 0; i < coverage_data->len; i++) {
CoverageEntry *entry = (CoverageEntry *)coverage_data->pdata[i];
- logbuffer = ensure_logbuf (
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
LEB128_SIZE /* method id */ +
LEB128_SIZE /* offset */ +
LEB128_SIZE /* line */ +
LEB128_SIZE /* column */
);
- ENTER_LOG (logbuffer, "coverage-statement");
- emit_byte (logbuffer, TYPE_COVERAGE_STATEMENT | TYPE_COVERAGE);
+ emit_event (logbuffer, TYPE_COVERAGE_STATEMENT | TYPE_COVERAGE);
emit_uvalue (logbuffer, method_id);
emit_uvalue (logbuffer, entry->offset);
emit_uvalue (logbuffer, entry->counter);
emit_uvalue (logbuffer, entry->line);
emit_uvalue (logbuffer, entry->column);
- EXIT_LOG (logbuffer);
- safe_send (prof, logbuffer);
+ EXIT_LOG;
+
+ send_if_needed (prof);
}
method_id++;
const char *assembly_name;
int number_of_methods, partially_covered;
guint fully_covered;
- LogBuffer *logbuffer;
image = mono_class_get_image (klass);
assembly_name = mono_image_get_name (image);
/* We don't handle partial covered yet */
partially_covered = 0;
- logbuffer = ensure_logbuf (
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
strlen (assembly_name) + 1 /* assembly name */ +
strlen (class_name) + 1 /* class name */ +
LEB128_SIZE /* partially covered */
);
- ENTER_LOG (logbuffer, "coverage-class");
- emit_byte (logbuffer, TYPE_COVERAGE_CLASS | TYPE_COVERAGE);
+ emit_event (logbuffer, TYPE_COVERAGE_CLASS | TYPE_COVERAGE);
emit_string (logbuffer, assembly_name, strlen (assembly_name) + 1);
emit_string (logbuffer, class_name, strlen (class_name) + 1);
emit_uvalue (logbuffer, number_of_methods);
emit_uvalue (logbuffer, fully_covered);
emit_uvalue (logbuffer, partially_covered);
- EXIT_LOG (logbuffer);
- safe_send (prof, logbuffer);
+ EXIT_LOG;
+
+ send_if_needed (prof);
g_free (class_name);
}
MonoAssembly *assembly = (MonoAssembly *)value;
MonoProfiler *prof = (MonoProfiler *)userdata;
MonoImage *image = mono_assembly_get_image (assembly);
- LogBuffer *logbuffer;
const char *name, *guid, *filename;
int number_of_methods = 0, partially_covered = 0;
guint fully_covered = 0;
get_coverage_for_image (image, &number_of_methods, &fully_covered, &partially_covered);
- logbuffer = ensure_logbuf (
+ ENTER_LOG;
+
+ LogBuffer *logbuffer = ensure_logbuf (
EVENT_SIZE /* event */ +
strlen (name) + 1 /* name */ +
strlen (guid) + 1 /* guid */ +
LEB128_SIZE /* partially covered */
);
- ENTER_LOG (logbuffer, "coverage-assemblies");
- emit_byte (logbuffer, TYPE_COVERAGE_ASSEMBLY | TYPE_COVERAGE);
+ emit_event (logbuffer, TYPE_COVERAGE_ASSEMBLY | TYPE_COVERAGE);
emit_string (logbuffer, name, strlen (name) + 1);
emit_string (logbuffer, guid, strlen (guid) + 1);
emit_string (logbuffer, filename, strlen (filename) + 1);
emit_uvalue (logbuffer, number_of_methods);
emit_uvalue (logbuffer, fully_covered);
emit_uvalue (logbuffer, partially_covered);
- EXIT_LOG (logbuffer);
- safe_send (prof, logbuffer);
+ EXIT_LOG;
+
+ send_if_needed (prof);
}
static void
in_shutdown = 1;
#ifndef DISABLE_HELPER_THREAD
- counters_and_perfcounters_sample (prof, FALSE);
+ counters_and_perfcounters_sample (prof);
dump_coverage (prof);
}
#endif
- if (TLS_GET (LogBuffer, tlsbuffer))
- send_buffer (prof, TLS_GET (GPtrArray, tlsmethodlist), TLS_GET (LogBuffer, tlsbuffer));
-
- TLS_SET (tlsbuffer, NULL);
- TLS_SET (tlsmethodlist, NULL);
+ /*
+ * Ensure that we empty the LLS completely, even if some nodes are
+ * not immediately removed upon calling mono_lls_remove (), by
+ * iterating until the head is NULL.
+ */
+ while (profiler_thread_list.head) {
+ MONO_LLS_FOREACH_SAFE (&profiler_thread_list, MonoProfilerThread, thread) {
+ remove_thread (prof, thread, FALSE);
+ } MONO_LLS_FOREACH_SAFE_END
+ }
InterlockedWrite (&prof->run_dumper_thread, 0);
mono_os_sem_post (&prof->dumper_queue_sem);
cleanup_reusable_samples (prof);
+ g_assert (!InterlockedRead (&buffer_rwlock_count) && "Why is the reader count still non-zero?");
+ g_assert (!InterlockedReadPointer (&buffer_rwlock_exclusive) && "Why does someone still hold the exclusive lock?");
+
#if defined (HAVE_SYS_ZLIB)
if (prof->gzfile)
gzclose (prof->gzfile);
mono_os_mutex_destroy (&coverage_mutex);
}
+ PROF_TLS_FREE ();
+
free (prof);
}
int command_socket;
int len;
char buf [64];
- MonoThread *thread = NULL;
mono_threads_attach_tools_thread ();
mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler helper");
+ init_thread (FALSE);
+
//fprintf (stderr, "Server listening\n");
command_socket = -1;
while (1) {
}
#endif
- counters_and_perfcounters_sample (prof, TRUE);
+ counters_and_perfcounters_sample (prof);
+
+ buffer_lock_excl ();
+
+ sync_point (prof, SYNC_POINT_PERIODIC);
+
+ buffer_unlock_excl ();
tv.tv_sec = 1;
tv.tv_usec = 0;
if (FD_ISSET (prof->pipes [0], &rfds)) {
char c;
read (prof->pipes [0], &c, 1);
- if (thread)
- mono_thread_detach (thread);
if (do_debug)
fprintf (stderr, "helper shutdown\n");
#if USE_PERF_EVENTS
}
}
#endif
- safe_send_threadless (prof, ensure_logbuf (0));
+ safe_send_threadless (prof);
return NULL;
}
#if USE_PERF_EVENTS
continue;
if (FD_ISSET (perf_data [i].perf_fd, &rfds)) {
read_perf_mmap (prof, i);
- safe_send_threadless (prof, ensure_logbuf (0));
+ send_if_needed_threadless (prof);
}
}
}
continue;
}
buf [len] = 0;
- if (strcmp (buf, "heapshot\n") == 0) {
+ if (strcmp (buf, "heapshot\n") == 0 && hs_mode_ondemand) {
+ // Rely on the finalization callbacks invoking process_requests ().
heapshot_requested = 1;
- //fprintf (stderr, "perform heapshot\n");
- if (InterlockedRead (&runtime_inited) && !thread) {
- thread = mono_thread_attach (mono_get_root_domain ());
- /*fprintf (stderr, "attached\n");*/
- }
- if (thread) {
- process_requests (prof);
- mono_thread_detach (thread);
- thread = NULL;
- }
+ mono_gc_finalize_notify ();
}
continue;
}
}
#endif
+static void
+free_writer_entry (gpointer p)
+{
+ mono_lock_free_free (p, WRITER_ENTRY_BLOCK_SIZE);
+}
+
static gboolean
handle_writer_queue_entry (MonoProfiler *prof)
{
WriterQueueEntry *entry;
if ((entry = (WriterQueueEntry *) mono_lock_free_queue_dequeue (&prof->writer_queue))) {
- LogBuffer *method_buffer = NULL;
- gboolean new_methods = FALSE;
+ if (!entry->methods)
+ goto no_methods;
- if (entry->methods->len)
- method_buffer = create_buffer ();
+ LogBuffer *buf = NULL;
/*
* Encode the method events in a temporary log buffer that we
* flush to disk before the main buffer, ensuring that all
* methods have metadata emitted before they're referenced.
+ *
+ * We use a 'proper' thread-local buffer for this as opposed
+ * to allocating and freeing a buffer by hand because the call
+ * to mono_method_full_name () below may trigger class load
+ * events when it retrieves the signature of the method. So a
+ * thread-local buffer needs to exist when such events occur.
*/
for (guint i = 0; i < entry->methods->len; i++) {
- MethodInfo *info = (MethodInfo *)g_ptr_array_index (entry->methods, i);
+ MethodInfo *info = (MethodInfo *) g_ptr_array_index (entry->methods, i);
if (mono_conc_hashtable_lookup (prof->method_table, info->method))
- continue;
-
- new_methods = TRUE;
+ goto free_info; // This method already has metadata emitted.
/*
* Other threads use this hash table to get a general
void *cstart = info->ji ? mono_jit_info_get_code_start (info->ji) : NULL;
int csize = info->ji ? mono_jit_info_get_code_size (info->ji) : 0;
- method_buffer = ensure_logbuf_inner (method_buffer,
+ buf = ensure_logbuf_unsafe (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* time */ +
LEB128_SIZE /* method */ +
LEB128_SIZE /* start */ +
LEB128_SIZE /* size */ +
nlen /* name */
);
- emit_byte (method_buffer, TYPE_JIT | TYPE_METHOD);
- emit_time (method_buffer, info->time);
- emit_method_inner (method_buffer, info->method);
- emit_ptr (method_buffer, cstart);
- emit_value (method_buffer, csize);
+ emit_event_time (buf, TYPE_JIT | TYPE_METHOD, info->time);
+ emit_method_inner (buf, info->method);
+ emit_ptr (buf, cstart);
+ emit_value (buf, csize);
- memcpy (method_buffer->cursor, name, nlen);
- method_buffer->cursor += nlen;
+ memcpy (buf->cursor, name, nlen);
+ buf->cursor += nlen;
mono_free (name);
+
+ free_info:
free (info);
}
g_ptr_array_free (entry->methods, TRUE);
- if (new_methods) {
- for (LogBuffer *iter = method_buffer; iter; iter = iter->next)
- iter->thread_id = 0;
-
- dump_buffer (prof, method_buffer);
- } else if (method_buffer)
- free_buffer (method_buffer, method_buffer->size);
+ if (buf) {
+ dump_buffer_threadless (prof, buf);
+ init_buffer_state (PROF_TLS_GET ());
+ }
+ no_methods:
dump_buffer (prof, entry->buffer);
- mono_thread_hazardous_try_free (entry, free);
+ mono_thread_hazardous_try_free (entry, free_writer_entry);
return TRUE;
}
dump_header (prof);
+ MonoProfilerThread *thread = init_thread (FALSE);
+
while (InterlockedRead (&prof->run_writer_thread)) {
mono_os_sem_wait (&prof->writer_queue_sem, MONO_SEM_FLAGS_NONE);
handle_writer_queue_entry (prof);
/* Drain any remaining entries on shutdown. */
while (handle_writer_queue_entry (prof));
+ free_buffer (thread->buffer, thread->buffer->size);
+ deinit_thread (thread);
+
mono_thread_info_detach ();
return NULL;
void *address = sample->frames [i].base_address;
if (!method) {
- g_assert (domain);
- g_assert (address);
+ g_assert (domain && "What happened to the domain pointer?");
+ g_assert (address && "What happened to the instruction pointer?");
MonoJitInfo *ji = mono_jit_info_table_find (domain, (char *) address);
}
}
- LogBuffer *logbuffer = ensure_logbuf (
+ LogBuffer *logbuffer = ensure_logbuf_unsafe (
EVENT_SIZE /* event */ +
- LEB128_SIZE /* type */ +
- LEB128_SIZE /* time */ +
+ BYTE_SIZE /* type */ +
LEB128_SIZE /* tid */ +
LEB128_SIZE /* count */ +
1 * (
) +
LEB128_SIZE /* managed count */ +
sample->count * (
- LEB128_SIZE /* method */ +
- LEB128_SIZE /* il offset */ +
- LEB128_SIZE /* native offset */
+ LEB128_SIZE /* method */
)
);
- emit_byte (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_HIT);
- emit_value (logbuffer, sample_type);
- emit_uvalue (logbuffer, prof->startup_time + sample->elapsed * 10000);
+ emit_event_time (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_HIT, sample->time);
+ emit_byte (logbuffer, sample_type);
emit_ptr (logbuffer, (void *) sample->tid);
emit_value (logbuffer, 1);
/* new in data version 6 */
emit_uvalue (logbuffer, sample->count);
- for (int i = 0; i < sample->count; ++i) {
+ for (int i = 0; i < sample->count; ++i)
emit_method (prof, logbuffer, sample->frames [i].method);
- emit_svalue (logbuffer, 0); /* il offset will always be 0 from now on */
- emit_svalue (logbuffer, sample->frames [i].offset);
- }
mono_thread_hazardous_try_free (sample, reuse_sample_hit);
dump_unmanaged_coderefs (prof);
- if (logbuffer->next)
- safe_send_threadless (prof, logbuffer);
+ send_if_needed_threadless (prof);
}
return FALSE;
mono_threads_attach_tools_thread ();
mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler dumper");
+ MonoProfilerThread *thread = init_thread (FALSE);
+
while (InterlockedRead (&prof->run_dumper_thread)) {
mono_os_sem_wait (&prof->dumper_queue_sem, MONO_SEM_FLAGS_NONE);
handle_dumper_queue_entry (prof);
/* Drain any remaining entries on shutdown. */
while (handle_dumper_queue_entry (prof));
- safe_send_threadless (prof, ensure_logbuf (0));
+ safe_send_threadless (prof);
+ deinit_thread (thread);
mono_thread_info_detach ();
static void
runtime_initialized (MonoProfiler *profiler)
{
+ InterlockedWrite (&runtime_inited, 1);
+
#ifndef DISABLE_HELPER_THREAD
if (hs_mode_ondemand || need_helper_thread) {
if (!start_helper_thread (profiler))
start_writer_thread (profiler);
start_dumper_thread (profiler);
- InterlockedWrite (&runtime_inited, 1);
+ mono_counters_register ("Sample hits", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &sample_hits);
+ mono_counters_register ("Sample flushes", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &sample_flushes);
+ mono_counters_register ("Sample events allocated", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &sample_allocations);
+ mono_counters_register ("Log buffers allocated", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &buffer_allocations);
+ mono_counters_register ("Thread start events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &thread_starts);
+ mono_counters_register ("Thread stop events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &thread_ends);
+ mono_counters_register ("Domain load events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &domain_loads);
+ mono_counters_register ("Domain unload events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &domain_unloads);
+ mono_counters_register ("Context load events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &context_loads);
+ mono_counters_register ("Context unload events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &context_unloads);
+ mono_counters_register ("Assembly load events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &assembly_loads);
+ mono_counters_register ("Assembly unload events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &assembly_unloads);
+ mono_counters_register ("Image load events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &image_loads);
+ mono_counters_register ("Image unload events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &image_unloads);
+ mono_counters_register ("Class load events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &class_loads);
+ mono_counters_register ("Class unload events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &class_unloads);
+
#ifndef DISABLE_HELPER_THREAD
counters_init (profiler);
- counters_sample (profiler, 0, FALSE);
+ counters_sample (profiler, 0);
#endif
/* ensure the main thread data and startup are available soon */
- safe_send (profiler, ensure_logbuf (0));
+ safe_send (profiler);
}
static MonoProfiler*
#endif
+ g_assert (sizeof (WriterQueueEntry) * 2 < LOCK_FREE_ALLOC_SB_USABLE_SIZE (WRITER_ENTRY_BLOCK_SIZE));
+
+ // FIXME: We should free this stuff too.
+ mono_lock_free_allocator_init_size_class (&prof->writer_entry_size_class, sizeof (WriterQueueEntry), WRITER_ENTRY_BLOCK_SIZE);
+ mono_lock_free_allocator_init_allocator (&prof->writer_entry_allocator, &prof->writer_entry_size_class);
+
mono_lock_free_queue_init (&prof->writer_queue);
mono_os_sem_init (&prof->writer_queue_sem, 0);
MONO_PROFILE_ENTER_LEAVE|MONO_PROFILE_JIT_COMPILATION|MONO_PROFILE_EXCEPTIONS|
MONO_PROFILE_MONITOR_EVENTS|MONO_PROFILE_MODULE_EVENTS|MONO_PROFILE_GC_ROOTS|
MONO_PROFILE_INS_COVERAGE|MONO_PROFILE_APPDOMAIN_EVENTS|MONO_PROFILE_CONTEXT_EVENTS|
- MONO_PROFILE_ASSEMBLY_EVENTS;
+ MONO_PROFILE_ASSEMBLY_EVENTS|MONO_PROFILE_GC_FINALIZATION;
max_allocated_sample_hits = mono_cpu_count () * 1000;
- mono_counters_register ("Sample hits", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &sample_hits);
- mono_counters_register ("Sample flushes", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &sample_flushes);
- mono_counters_register ("Sample events allocated", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &sample_allocations);
- mono_counters_register ("Log buffers allocated", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &buffer_allocations);
- mono_counters_register ("Thread start events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &thread_starts);
- mono_counters_register ("Thread stop events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &thread_ends);
- mono_counters_register ("Domain load events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &domain_loads);
- mono_counters_register ("Domain unload events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &domain_unloads);
- mono_counters_register ("Context load events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &context_loads);
- mono_counters_register ("Context unload events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &context_unloads);
- mono_counters_register ("Assembly load events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &assembly_loads);
- mono_counters_register ("Assembly unload events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &assembly_unloads);
- mono_counters_register ("Image load events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &image_loads);
- mono_counters_register ("Image unload events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &image_unloads);
- mono_counters_register ("Class load events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &class_loads);
- mono_counters_register ("Class unload events", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &class_unloads);
-
p = desc;
if (strncmp (p, "log", 3))
usage (1);
utils_init (fast_time);
+ PROF_TLS_INIT ();
+
prof = create_profiler (filename, filters);
- if (!prof)
+ if (!prof) {
+ PROF_TLS_FREE ();
return;
+ }
- init_thread ();
+ mono_lls_init (&profiler_thread_list, NULL);
+
+ init_thread (TRUE);
mono_profiler_install (prof, log_shutdown);
mono_profiler_install_gc (gc_event, gc_resize);
mono_profiler_install_allocation (gc_alloc);
mono_profiler_install_gc_moves (gc_moves);
mono_profiler_install_gc_roots (gc_handle, gc_roots);
+ mono_profiler_install_gc_finalize (finalize_begin, finalize_object_begin, finalize_object_end, finalize_end);
mono_profiler_install_appdomain (NULL, domain_loaded, domain_unloaded, NULL);
mono_profiler_install_appdomain_name (domain_name);
mono_profiler_install_context (context_loaded, context_unloaded);
}
mono_profiler_set_events ((MonoProfileFlags)events);
-
- TLS_INIT (tlsbuffer);
- TLS_INIT (tlsmethodlist);
}
#define LOG_HEADER_ID 0x4D505A01
#define LOG_VERSION_MAJOR 0
#define LOG_VERSION_MINOR 4
-#define LOG_DATA_VERSION 12
+#define LOG_DATA_VERSION 13
/*
* Changes in data versions:
* version 2: added offsets in heap walk
added TYPE_GC_HANDLE_{CREATED,DESTROYED}_BT
TYPE_JIT events are no longer guaranteed to have code start/size info (can be zero)
* version 12: added MONO_COUNTER_PROFILER
+ * version 13: added MONO_GC_EVENT_{PRE_STOP_WORLD_LOCKED,POST_START_WORLD_UNLOCKED}
+ added TYPE_META + TYPE_SYNC_POINT
+ removed il and native offset in TYPE_SAMPLE_HIT
+ methods in backtraces are now encoded as proper method pointers
+ removed flags in backtrace format
+ removed flags in metadata events
+ changed the following fields to a single byte rather than leb128
+ TYPE_GC_EVENT: event_type, generation
+ TYPE_HEAP_ROOT: root_type
+ TYPE_JITHELPER: type
+ TYPE_SAMPLE_HIT: sample_type
+ TYPE_CLAUSE: clause_type
+ TYPE_SAMPLE_COUNTERS_DESC: type, unit, variance
+ TYPE_SAMPLE_COUNTERS: type
+ added time fields to all events that were missing one
+ TYPE_HEAP_OBJECT
+ TYPE_HEAP_ROOT
+ TYPE_SAMPLE_USYM
+ TYPE_SAMPLE_COUNTERS_DESC
+ TYPE_COVERAGE_METHOD
+ TYPE_COVERAGE_STATEMENT
+ TYPE_COVERAGE_CLASS
+ TYPE_COVERAGE_ASSEMBLY
+ moved the time field in TYPE_SAMPLE_HIT to right after the event byte, now encoded as a regular time field
+ changed the time field in TYPE_SAMPLE_COUNTERS to be encoded as a regular time field (in nanoseconds)
+ added TYPE_GC_FINALIZE_{START,END,OBJECT_START,OBJECT_END}
*/
enum {
TYPE_SAMPLE,
TYPE_RUNTIME,
TYPE_COVERAGE,
+ TYPE_META,
/* extended type for TYPE_HEAP */
TYPE_HEAP_START = 0 << 4,
TYPE_HEAP_END = 1 << 4,
TYPE_GC_HANDLE_DESTROYED = 5 << 4,
TYPE_GC_HANDLE_CREATED_BT = 6 << 4,
TYPE_GC_HANDLE_DESTROYED_BT = 7 << 4,
+ TYPE_GC_FINALIZE_START = 8 << 4,
+ TYPE_GC_FINALIZE_END = 9 << 4,
+ TYPE_GC_FINALIZE_OBJECT_START = 10 << 4,
+ TYPE_GC_FINALIZE_OBJECT_END = 11 << 4,
/* extended type for TYPE_METHOD */
TYPE_LEAVE = 1 << 4,
TYPE_ENTER = 2 << 4,
TYPE_COVERAGE_METHOD = 1 << 4,
TYPE_COVERAGE_STATEMENT = 2 << 4,
TYPE_COVERAGE_CLASS = 3 << 4,
+ /* extended type for TYPE_META */
+ TYPE_SYNC_POINT = 0 << 4,
TYPE_END
};
+typedef enum {
+ SYNC_POINT_PERIODIC,
+ SYNC_POINT_WORLD_STOP,
+ SYNC_POINT_WORLD_START
+} MonoProfilerSyncPointType;
+
// Sampling sources
// Unless you have compiled with --enable-perf-events, only SAMPLE_CYCLES is available
enum {
process_id (void)
{
#ifdef HOST_WIN32
- return 0; /* FIXME */
+ return GetCurrentProcessId ();
#else
return (uintptr_t)getpid ();
#endif
/* gcc 4.2.1 from xcode4 crashes on sgen_card_table_get_card_address () when this is enabled */
#if defined(PLATFORM_MACOSX)
-#define GCC_VERSION (__GNUC__ * 10000 \
- + __GNUC_MINOR__ * 100 \
- + __GNUC_PATCHLEVEL__)
-#if GCC_VERSION <= 40300
+#if MONO_GNUC_VERSION <= 40300
#undef PREFETCH_CARDS
#endif
#endif
var heads = new Bridge [FAN_OUT];
for (int i = 0; i < FAN_OUT; ++i)
heads [i] = new Bridge ();
- var multiplexer = new Bridge [FAN_OUT];
- for (int i = 0; i < FAN_OUT; ++i)
- {
- heads [i].Links.Add (multiplexer);
- multiplexer [i] = new Bridge ();
+
+ // We make five identical multiplexers to verify Tarjan-bridge can merge them together correctly.
+ var MULTIPLEXER_COUNT = 5;
+ Bridge[] multiplexer0 = null;
+ for(int m = 0; m < MULTIPLEXER_COUNT; m++) {
+ var multiplexer = new Bridge [FAN_OUT];
+ if (m == 0) {
+ multiplexer0 = multiplexer;
+ for (int i = 0; i < FAN_OUT; ++i)
+ {
+ heads [i].Links.Add (multiplexer);
+ multiplexer [i] = new Bridge ();
+ }
+ } else {
+ for (int i = 0; i < FAN_OUT; ++i)
+ {
+ heads [i].Links.Add (multiplexer);
+ multiplexer [i] = multiplexer0 [i];
+ }
+ }
}
- Console.WriteLine ("-double fan done-");
+
+ Console.WriteLine ("-double fan x5 done-");
}
/*
break;
case STATE_OUT:
if (InterlockedCompareExchange (&nodes [i].state, STATE_BUSY, STATE_OUT) == STATE_OUT) {
- result = mono_lls_find (&lls, hp, i, HAZARD_FREE_SAFE_CTX);
+ result = mono_lls_find (&lls, hp, i);
assert (!result);
mono_hazard_pointer_clear_all (hp, -1);
- result = mono_lls_insert (&lls, hp, &nodes [i].node, HAZARD_FREE_SAFE_CTX);
+ result = mono_lls_insert (&lls, hp, &nodes [i].node);
mono_hazard_pointer_clear_all (hp, -1);
assert (nodes [i].state == STATE_BUSY);
break;
case STATE_IN:
if (InterlockedCompareExchange (&nodes [i].state, STATE_BUSY, STATE_IN) == STATE_IN) {
- result = mono_lls_find (&lls, hp, i, HAZARD_FREE_SAFE_CTX);
+ result = mono_lls_find (&lls, hp, i);
assert (result);
assert (mono_hazard_pointer_get_val (hp, 1) == &nodes [i].node);
mono_hazard_pointer_clear_all (hp, -1);
- result = mono_lls_remove (&lls, hp, &nodes [i].node, HAZARD_FREE_SAFE_CTX);
+ result = mono_lls_remove (&lls, hp, &nodes [i].node);
mono_hazard_pointer_clear_all (hp, -1);
++thread_data->num_removes;
mono_threads_init (&thread_callbacks, 0);
- mono_lls_init (&lls, free_node, HAZARD_FREE_NO_LOCK);
+ mono_lls_init (&lls, free_node);
for (i = 0; i < N; ++i) {
nodes [i].node.key = i;
atomic.c \
mono-hwcap.h \
mono-hwcap.c \
+ mono-hwcap-vars.h \
bsearch.h \
bsearch.c \
mono-signal-handler.h \
endif
+if !CROSS_COMPILE
+
if X86
-arch_sources += mono-hwcap-x86.c mono-hwcap-x86.h
+arch_sources += mono-hwcap-x86.c
endif
if AMD64
-arch_sources += mono-hwcap-x86.c mono-hwcap-x86.h
+arch_sources += mono-hwcap-x86.c
endif
if ARM
-arch_sources += mono-hwcap-arm.c mono-hwcap-arm.h
+arch_sources += mono-hwcap-arm.c
endif
if ARM64
-arch_sources += mono-hwcap-arm64.c mono-hwcap-arm64.h
+arch_sources += mono-hwcap-arm64.c
endif
if MIPS
-arch_sources += mono-hwcap-mips.c mono-hwcap-mips.h
+arch_sources += mono-hwcap-mips.c
endif
if POWERPC
-arch_sources += mono-hwcap-ppc.c mono-hwcap-ppc.h
+arch_sources += mono-hwcap-ppc.c
endif
if POWERPC64
-arch_sources += mono-hwcap-ppc.c mono-hwcap-ppc.h
+arch_sources += mono-hwcap-ppc.c
endif
if SPARC
-arch_sources += mono-hwcap-sparc.c mono-hwcap-sparc.h
+arch_sources += mono-hwcap-sparc.c
endif
if SPARC64
-arch_sources += mono-hwcap-sparc.c mono-hwcap-sparc.h
+arch_sources += mono-hwcap-sparc.c
endif
if IA64
-arch_sources += mono-hwcap-ia64.c mono-hwcap-ia64.h
+arch_sources += mono-hwcap-ia64.c
endif
if S390X
-arch_sources += mono-hwcap-s390x.c mono-hwcap-s390x.h
+arch_sources += mono-hwcap-s390x.c
+endif
+
+else
+
+arch_sources += mono-hwcap-cross.c
+
endif
libmonoutils_la_SOURCES = $(monoutils_sources) $(arch_sources)
#include "config.h"
#include <glib.h>
+#include <mono/utils/mono-membar.h>
/*
The current Nexus 7 arm-v7a fails with:
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
-#include <mono/utils/mono-membar.h>
/* mingw is missing InterlockedCompareExchange64 () from winbase.h */
#if HAVE_DECL_INTERLOCKEDCOMPAREEXCHANGE64==0
/* Prefer GCC atomic ops if the target supports it (see configure.ac). */
#elif defined(USE_GCC_ATOMIC_OPS)
+/*
+ * As of this comment (August 2016), all current Clang versions get atomic
+ * intrinsics on ARM64 wrong. All GCC versions prior to 5.3.0 do, too. The bug
+ * is the same: The compiler developers thought that the acq + rel barriers
+ * that ARM64 load/store instructions can impose are sufficient to provide
+ * sequential consistency semantics. This is not the case:
+ *
+ * http://lists.infradead.org/pipermail/linux-arm-kernel/2014-February/229588.html
+ *
+ * We work around this bug by inserting full barriers around each atomic
+ * intrinsic if we detect that we're built with a buggy compiler.
+ */
+
+#if defined (HOST_ARM64) && (defined (__clang__) || MONO_GNUC_VERSION < 50300)
+#define WRAP_ATOMIC_INTRINSIC(INTRIN) \
+ ({ \
+ mono_memory_barrier (); \
+ __typeof__ (INTRIN) atomic_ret__ = (INTRIN); \
+ mono_memory_barrier (); \
+ atomic_ret__; \
+ })
+
+#define gcc_sync_val_compare_and_swap(a, b, c) WRAP_ATOMIC_INTRINSIC (__sync_val_compare_and_swap (a, b, c))
+#define gcc_sync_add_and_fetch(a, b) WRAP_ATOMIC_INTRINSIC (__sync_add_and_fetch (a, b))
+#define gcc_sync_sub_and_fetch(a, b) WRAP_ATOMIC_INTRINSIC (__sync_sub_and_fetch (a, b))
+#define gcc_sync_fetch_and_add(a, b) WRAP_ATOMIC_INTRINSIC (__sync_fetch_and_add (a, b))
+#else
+#define gcc_sync_val_compare_and_swap(a, b, c) __sync_val_compare_and_swap (a, b, c)
+#define gcc_sync_add_and_fetch(a, b) __sync_add_and_fetch (a, b)
+#define gcc_sync_sub_and_fetch(a, b) __sync_sub_and_fetch (a, b)
+#define gcc_sync_fetch_and_add(a, b) __sync_fetch_and_add (a, b)
+#endif
+
static inline gint32 InterlockedCompareExchange(volatile gint32 *dest,
gint32 exch, gint32 comp)
{
- return __sync_val_compare_and_swap (dest, comp, exch);
+ return gcc_sync_val_compare_and_swap (dest, comp, exch);
}
static inline gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp)
{
- return __sync_val_compare_and_swap (dest, comp, exch);
+ return gcc_sync_val_compare_and_swap (dest, comp, exch);
}
static inline gint32 InterlockedAdd(volatile gint32 *dest, gint32 add)
{
- return __sync_add_and_fetch (dest, add);
+ return gcc_sync_add_and_fetch (dest, add);
}
static inline gint32 InterlockedIncrement(volatile gint32 *val)
{
- return __sync_add_and_fetch (val, 1);
+ return gcc_sync_add_and_fetch (val, 1);
}
static inline gint32 InterlockedDecrement(volatile gint32 *val)
{
- return __sync_sub_and_fetch (val, 1);
+ return gcc_sync_sub_and_fetch (val, 1);
}
static inline gint32 InterlockedExchange(volatile gint32 *val, gint32 new_val)
gint32 old_val;
do {
old_val = *val;
- } while (__sync_val_compare_and_swap (val, old_val, new_val) != old_val);
+ } while (gcc_sync_val_compare_and_swap (val, old_val, new_val) != old_val);
return old_val;
}
gpointer old_val;
do {
old_val = *val;
- } while (__sync_val_compare_and_swap (val, old_val, new_val) != old_val);
+ } while (gcc_sync_val_compare_and_swap (val, old_val, new_val) != old_val);
return old_val;
}
static inline gint32 InterlockedExchangeAdd(volatile gint32 *val, gint32 add)
{
- return __sync_fetch_and_add (val, add);
+ return gcc_sync_fetch_and_add (val, add);
}
static inline gint8 InterlockedRead8(volatile gint8 *src)
{
/* Kind of a hack, but GCC doesn't give us anything better, and it's
* certainly not as bad as using a CAS loop. */
- return __sync_fetch_and_add (src, 0);
+ return gcc_sync_fetch_and_add (src, 0);
}
static inline gint16 InterlockedRead16(volatile gint16 *src)
{
- return __sync_fetch_and_add (src, 0);
+ return gcc_sync_fetch_and_add (src, 0);
}
static inline gint32 InterlockedRead(volatile gint32 *src)
{
- return __sync_fetch_and_add (src, 0);
+ return gcc_sync_fetch_and_add (src, 0);
}
static inline void InterlockedWrite8(volatile gint8 *dst, gint8 val)
gint8 old_val;
do {
old_val = *dst;
- } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
+ } while (gcc_sync_val_compare_and_swap (dst, old_val, val) != old_val);
}
static inline void InterlockedWrite16(volatile gint16 *dst, gint16 val)
gint16 old_val;
do {
old_val = *dst;
- } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
+ } while (gcc_sync_val_compare_and_swap (dst, old_val, val) != old_val);
}
static inline void InterlockedWrite(volatile gint32 *dst, gint32 val)
gint32 old_val;
do {
old_val = *dst;
- } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
+ } while (gcc_sync_val_compare_and_swap (dst, old_val, val) != old_val);
}
#if defined (TARGET_OSX) || defined (__arm__) || (defined (__mips__) && !defined (__mips64)) || (defined (__powerpc__) && !defined (__powerpc64__)) || (defined (__sparc__) && !defined (__arch64__))
static inline gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
{
- return __sync_val_compare_and_swap (dest, comp, exch);
+ return gcc_sync_val_compare_and_swap (dest, comp, exch);
}
static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
{
- return __sync_add_and_fetch (dest, add);
+ return gcc_sync_add_and_fetch (dest, add);
}
static inline gint64 InterlockedIncrement64(volatile gint64 *val)
{
- return __sync_add_and_fetch (val, 1);
+ return gcc_sync_add_and_fetch (val, 1);
}
static inline gint64 InterlockedDecrement64(volatile gint64 *val)
{
- return __sync_sub_and_fetch (val, 1);
+ return gcc_sync_sub_and_fetch (val, 1);
}
static inline gint64 InterlockedExchangeAdd64(volatile gint64 *val, gint64 add)
{
- return __sync_fetch_and_add (val, add);
+ return gcc_sync_fetch_and_add (val, add);
}
static inline gint64 InterlockedRead64(volatile gint64 *src)
{
/* Kind of a hack, but GCC doesn't give us anything better. */
- return __sync_fetch_and_add (src, 0);
+ return gcc_sync_fetch_and_add (src, 0);
}
#else
InterlockedExchange64 (dst, val);
}
-#elif defined(__ia64__)
-
-#ifdef __INTEL_COMPILER
-#include <ia64intrin.h>
-#endif
-
-static inline gint32 InterlockedCompareExchange(gint32 volatile *dest,
- gint32 exch, gint32 comp)
-{
- gint32 old;
- guint64 real_comp;
-
-#ifdef __INTEL_COMPILER
- old = _InterlockedCompareExchange (dest, exch, comp);
-#else
- /* cmpxchg4 zero extends the value read from memory */
- real_comp = (guint64)(guint32)comp;
- asm volatile ("mov ar.ccv = %2 ;;\n\t"
- "cmpxchg4.acq %0 = [%1], %3, ar.ccv\n\t"
- : "=r" (old) : "r" (dest), "r" (real_comp), "r" (exch));
-#endif
-
- return(old);
-}
-
-static inline gpointer InterlockedCompareExchangePointer(gpointer volatile *dest,
- gpointer exch, gpointer comp)
-{
- gpointer old;
-
-#ifdef __INTEL_COMPILER
- old = _InterlockedCompareExchangePointer (dest, exch, comp);
-#else
- asm volatile ("mov ar.ccv = %2 ;;\n\t"
- "cmpxchg8.acq %0 = [%1], %3, ar.ccv\n\t"
- : "=r" (old) : "r" (dest), "r" (comp), "r" (exch));
-#endif
-
- return(old);
-}
-
-static inline gint32 InterlockedIncrement(gint32 volatile *val)
-{
-#ifdef __INTEL_COMPILER
- return _InterlockedIncrement (val);
-#else
- gint32 old;
-
- do {
- old = *val;
- } while (InterlockedCompareExchange (val, old + 1, old) != old);
-
- return old + 1;
-#endif
-}
-
-static inline gint32 InterlockedDecrement(gint32 volatile *val)
-{
-#ifdef __INTEL_COMPILER
- return _InterlockedDecrement (val);
-#else
- gint32 old;
-
- do {
- old = *val;
- } while (InterlockedCompareExchange (val, old - 1, old) != old);
-
- return old - 1;
-#endif
-}
-
-static inline gint32 InterlockedExchange(gint32 volatile *dest, gint32 new_val)
-{
-#ifdef __INTEL_COMPILER
- return _InterlockedExchange (dest, new_val);
-#else
- gint32 res;
-
- do {
- res = *dest;
- } while (InterlockedCompareExchange (dest, new_val, res) != res);
-
- return res;
-#endif
-}
-
-static inline gpointer InterlockedExchangePointer(gpointer volatile *dest, gpointer new_val)
-{
-#ifdef __INTEL_COMPILER
- return (gpointer)_InterlockedExchange64 ((gint64*)dest, (gint64)new_val);
-#else
- gpointer res;
-
- do {
- res = *dest;
- } while (InterlockedCompareExchangePointer (dest, new_val, res) != res);
-
- return res;
-#endif
-}
-
-static inline gint32 InterlockedExchangeAdd(gint32 volatile *val, gint32 add)
-{
- gint32 old;
-
-#ifdef __INTEL_COMPILER
- old = _InterlockedExchangeAdd (val, add);
-#else
- do {
- old = *val;
- } while (InterlockedCompareExchange (val, old + add, old) != old);
-
- return old;
-#endif
-}
-
#else
#define WAPI_NO_ATOMIC_ASM
typedef struct {
gpointer p;
MonoHazardousFreeFunc free_func;
- HazardFreeLocking locking;
} DelayedFreeItem;
/* The hazard table */
mono_jit_info_table_add(), which doesn't have to care about hazards
because it holds the respective domain lock. */
gpointer
-get_hazardous_pointer (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index)
+mono_get_hazardous_pointer (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index)
{
gpointer p;
}
static gboolean
-try_free_delayed_free_item (HazardFreeContext context)
+try_free_delayed_free_item (void)
{
DelayedFreeItem item;
gboolean popped = mono_lock_free_array_queue_pop (&delayed_free_queue, &item);
if (!popped)
return FALSE;
- if ((context == HAZARD_FREE_ASYNC_CTX && item.locking == HAZARD_FREE_MAY_LOCK) ||
- (is_pointer_hazardous (item.p))) {
+ if (is_pointer_hazardous (item.p)) {
mono_lock_free_array_queue_push (&delayed_free_queue, &item);
return FALSE;
}
void
mono_thread_hazardous_queue_free (gpointer p, MonoHazardousFreeFunc free_func)
{
- DelayedFreeItem item = { p, free_func, HAZARD_FREE_MAY_LOCK };
+ DelayedFreeItem item = { p, free_func };
InterlockedIncrement (&hazardous_pointer_count);
void
mono_thread_hazardous_try_free_all (void)
{
- while (try_free_delayed_free_item (HAZARD_FREE_SAFE_CTX))
+ while (try_free_delayed_free_item ())
;
}
{
int i;
for (i = 0; i < 10; ++i)
- try_free_delayed_free_item (HAZARD_FREE_SAFE_CTX);
+ try_free_delayed_free_item ();
}
void
typedef void (*MonoHazardousFreeFunc) (gpointer p);
-typedef enum {
- HAZARD_FREE_MAY_LOCK,
- HAZARD_FREE_NO_LOCK,
-} HazardFreeLocking;
-
-typedef enum {
- HAZARD_FREE_SAFE_CTX,
- HAZARD_FREE_ASYNC_CTX,
-} HazardFreeContext;
-
MONO_API gboolean mono_thread_hazardous_try_free (gpointer p, MonoHazardousFreeFunc free_func);
-void mono_thread_hazardous_queue_free (gpointer p, MonoHazardousFreeFunc free_func);
+MONO_API void mono_thread_hazardous_queue_free (gpointer p, MonoHazardousFreeFunc free_func);
void mono_thread_hazardous_try_free_all (void);
void mono_thread_hazardous_try_free_some (void);
-MonoThreadHazardPointers* mono_hazard_pointer_get (void);
-gpointer get_hazardous_pointer (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index);
+MONO_API MonoThreadHazardPointers* mono_hazard_pointer_get (void);
+gpointer mono_get_hazardous_pointer (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index);
#define mono_hazard_pointer_set(hp,i,v) \
do { g_assert ((i) >= 0 && (i) < HAZARD_POINTER_COUNT); \
for (;;) {
gboolean success;
- desc = (Descriptor *) get_hazardous_pointer ((gpointer * volatile)&desc_avail, hp, 1);
+ desc = (Descriptor *) mono_get_hazardous_pointer ((gpointer * volatile)&desc_avail, hp, 1);
if (desc) {
Descriptor *next = desc->next;
success = (InterlockedCompareExchangePointer ((gpointer * volatile)&desc_avail, next, desc) == desc);
for (;;) {
MonoLockFreeQueueNode *next;
- tail = (MonoLockFreeQueueNode *) get_hazardous_pointer ((gpointer volatile*)&q->tail, hp, 0);
+ tail = (MonoLockFreeQueueNode *) mono_get_hazardous_pointer ((gpointer volatile*)&q->tail, hp, 0);
mono_memory_read_barrier ();
/*
* We never dereference next so we don't need a
for (;;) {
MonoLockFreeQueueNode *tail, *next;
- head = (MonoLockFreeQueueNode *) get_hazardous_pointer ((gpointer volatile*)&q->head, hp, 0);
+ head = (MonoLockFreeQueueNode *) mono_get_hazardous_pointer ((gpointer volatile*)&q->head, hp, 0);
tail = (MonoLockFreeQueueNode*)q->tail;
mono_memory_read_barrier ();
next = head->next;
#define MONO_COLD
#endif
+#if defined (__GNUC__) && defined (__GNUC_MINOR__) && defined (__GNUC_PATCHLEVEL__)
+#define MONO_GNUC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#endif
+
#endif /* __UTILS_MONO_COMPILER_H__*/
hp = mono_hazard_pointer_get ();
retry:
- table = (conc_table *)get_hazardous_pointer ((gpointer volatile*)&hash_table->table, hp, 0);
+ table = (conc_table *)mono_get_hazardous_pointer ((gpointer volatile*)&hash_table->table, hp, 0);
table_mask = table->table_size - 1;
kvs = table->kvs;
i = hash & table_mask;
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
-#include "mono/utils/mono-hwcap-arm.h"
+#include "mono/utils/mono-hwcap.h"
#if defined(HAVE_SYS_AUXV_H) && !defined(PLATFORM_ANDROID)
#include <sys/auxv.h>
#include <stdio.h>
#endif
-gboolean mono_hwcap_arm_is_v5 = FALSE;
-gboolean mono_hwcap_arm_is_v6 = FALSE;
-gboolean mono_hwcap_arm_is_v7 = FALSE;
-gboolean mono_hwcap_arm_has_vfp = FALSE;
-gboolean mono_hwcap_arm_has_vfp3 = FALSE;
-gboolean mono_hwcap_arm_has_vfp3_d16 = FALSE;
-gboolean mono_hwcap_arm_has_thumb = FALSE;
-gboolean mono_hwcap_arm_has_thumb2 = FALSE;
-
void
mono_hwcap_arch_init (void)
{
}
#endif
}
-
-void
-mono_hwcap_print(FILE *f)
-{
- g_fprintf (f, "mono_hwcap_arm_is_v5 = %i\n", mono_hwcap_arm_is_v5);
- g_fprintf (f, "mono_hwcap_arm_is_v6 = %i\n", mono_hwcap_arm_is_v6);
- g_fprintf (f, "mono_hwcap_arm_is_v7 = %i\n", mono_hwcap_arm_is_v7);
- g_fprintf (f, "mono_hwcap_arm_has_vfp = %i\n", mono_hwcap_arm_has_vfp);
- g_fprintf (f, "mono_hwcap_arm_has_vfp3 = %i\n", mono_hwcap_arm_has_vfp3);
- g_fprintf (f, "mono_hwcap_arm_has_vfp3_d16 = %i\n", mono_hwcap_arm_has_vfp3_d16);
- g_fprintf (f, "mono_hwcap_arm_has_thumb = %i\n", mono_hwcap_arm_has_thumb);
- g_fprintf (f, "mono_hwcap_arm_has_thumb2 = %i\n", mono_hwcap_arm_has_thumb2);
-}
+++ /dev/null
-#ifndef __MONO_UTILS_HWCAP_ARM_H__
-#define __MONO_UTILS_HWCAP_ARM_H__
-
-#include "mono/utils/mono-hwcap.h"
-
-extern gboolean mono_hwcap_arm_is_v5;
-extern gboolean mono_hwcap_arm_is_v6;
-extern gboolean mono_hwcap_arm_is_v7;
-extern gboolean mono_hwcap_arm_has_vfp;
-extern gboolean mono_hwcap_arm_has_vfp3;
-extern gboolean mono_hwcap_arm_has_vfp3_d16;
-extern gboolean mono_hwcap_arm_has_thumb;
-extern gboolean mono_hwcap_arm_has_thumb2;
-
-#endif /* __MONO_UTILS_HWCAP_ARM_H__ */
/*
- * mono-hwcap-arm64.c: ARM hardware feature detection
+ * mono-hwcap-arm64.c: ARM64 hardware feature detection
*
* Copyright 2013 Xamarin Inc
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
-#include "mono/utils/mono-hwcap-arm64.h"
+#include "mono/utils/mono-hwcap.h"
-#if defined(MONO_CROSS_COMPILE)
void
mono_hwcap_arch_init (void)
{
}
-#else
-void
-mono_hwcap_arch_init (void)
-{
-}
-#endif
-
-void
-mono_hwcap_print(FILE *f)
-{
-}
+++ /dev/null
-#ifndef __MONO_UTILS_HWCAP_ARM64_H__
-#define __MONO_UTILS_HWCAP_ARM64_H__
-
-#include "mono/utils/mono-hwcap.h"
-
-#endif
--- /dev/null
+/*
+ * mono-hwcap-cross.c: No-op hardware feature detection
+ *
+ * Author:
+ * Alex Rønne Petersen (alexrp@xamarin.com)
+ *
+ * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+ */
+
+#include "mono/utils/mono-hwcap.h"
+
+void
+mono_hwcap_arch_init (void)
+{
+}
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
-#include "mono/utils/mono-hwcap-ia64.h"
+#include "mono/utils/mono-hwcap.h"
void
mono_hwcap_arch_init (void)
-{
- /* Nothing needed here yet. */
-}
-
-void
-mono_hwcap_print (FILE *f)
{
}
+++ /dev/null
-#ifndef __MONO_UTILS_HWCAP_IA64_H__
-#define __MONO_UTILS_HWCAP_IA64_H__
-
-#include "mono/utils/mono-hwcap.h"
-
-/* Nothing needed here yet. */
-
-#endif /* __MONO_UTILS_HWCAP_IA64_H__ */
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
-#include "mono/utils/mono-hwcap-mips.h"
+#include "mono/utils/mono-hwcap.h"
void
mono_hwcap_arch_init (void)
-{
- /* Nothing needed here yet. */
-}
-
-void
-mono_hwcap_print (FILE *f)
{
}
+++ /dev/null
-#ifndef __MONO_UTILS_HWCAP_MIPS_H__
-#define __MONO_UTILS_HWCAP_MIPS_H__
-
-#include "mono/utils/mono-hwcap.h"
-
-/* Nothing needed here yet. */
-
-#endif /* __MONO_UTILS_HWCAP_MIPS_H__ */
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
-#include "mono/utils/mono-hwcap-ppc.h"
+#include "mono/utils/mono-hwcap.h"
#if defined(__linux__) && defined(HAVE_SYS_AUXV_H)
#include <string.h>
#include <sys/auxv.h>
#endif
-gboolean mono_hwcap_ppc_has_icache_snoop = FALSE;
-gboolean mono_hwcap_ppc_is_isa_2x = FALSE;
-gboolean mono_hwcap_ppc_is_isa_64 = FALSE;
-gboolean mono_hwcap_ppc_has_move_fpr_gpr = FALSE;
-gboolean mono_hwcap_ppc_has_multiple_ls_units = FALSE;
-
void
mono_hwcap_arch_init (void)
{
}
#endif
}
-
-void
-mono_hwcap_print (FILE* f)
-{
- g_fprintf (f, "mono_hwcap_ppc_has_icache_snoop = %i\n", mono_hwcap_ppc_has_icache_snoop);
- g_fprintf (f, "mono_hwcap_ppc_is_isa_2x = %i\n", mono_hwcap_ppc_is_isa_2x);
- g_fprintf (f, "mono_hwcap_ppc_is_isa_64 = %i\n", mono_hwcap_ppc_is_isa_64);
- g_fprintf (f, "mono_hwcap_ppc_has_move_fpr_gpr = %i\n", mono_hwcap_ppc_has_move_fpr_gpr);
- g_fprintf (f, "mono_hwcap_ppc_has_multiple_ls_units = %i\n", mono_hwcap_ppc_has_multiple_ls_units);
-}
+++ /dev/null
-#ifndef __MONO_UTILS_HWCAP_PPC_H__
-#define __MONO_UTILS_HWCAP_PPC_H__
-
-#include "mono/utils/mono-hwcap.h"
-
-extern gboolean mono_hwcap_ppc_has_icache_snoop;
-extern gboolean mono_hwcap_ppc_is_isa_2x;
-extern gboolean mono_hwcap_ppc_is_isa_64;
-extern gboolean mono_hwcap_ppc_has_move_fpr_gpr;
-extern gboolean mono_hwcap_ppc_has_multiple_ls_units;
-
-#endif /* __MONO_UTILS_HWCAP_PPC_H__ */
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
-#include "mono/utils/mono-hwcap-s390x.h"
+#include "mono/utils/mono-hwcap.h"
+
#include <signal.h>
-facilityList_t facs;
+typedef struct {
+ uint8_t n3:1; // 000 - N3 instructions
+ uint8_t zi:1; // 001 - z/Arch installed
+ uint8_t za:1; // 002 - z/Arch active
+ uint8_t date:1; // 003 - DAT-enhancement
+ uint8_t idtes:1; // 004 - IDTE-segment tables
+ uint8_t idter:1; // 005 - IDTE-region tables
+ uint8_t asnlx:1; // 006 - ASN-LX reuse
+ uint8_t stfle:1; // 007 - STFLE
+ uint8_t edat1:1; // 008 - EDAT 1
+ uint8_t srs:1; // 009 - Sense-Running-Status
+ uint8_t csske:1; // 010 - Conditional SSKE
+ uint8_t ctf:1; // 011 - Configuration-topology
+ uint8_t ibm01:1; // 012 - Assigned to IBM
+ uint8_t ipter:1; // 013 - IPTE-range
+ uint8_t nqks:1; // 014 - Nonquiescing key-setting
+ uint8_t ibm02:1; // 015 - Assigned to IBM
+ uint8_t etf2:1; // 016 - Extended translation 2
+ uint8_t msa:1; // 017 - Message security assist 1
+ uint8_t ld:1; // 018 - Long displacement
+ uint8_t ldh:1; // 019 - Long displacement high perf
+ uint8_t mas:1; // 020 - HFP multiply-add-subtract
+ uint8_t eif:1; // 021 - Extended immediate
+ uint8_t etf3:1; // 022 - Extended translation 3
+ uint8_t hux:1; // 023 - HFP unnormalized extension
+ uint8_t etf2e:1; // 024 - Extended translation enhanced 2
+ uint8_t stckf:1; // 025 - Store clock fast
+ uint8_t pe:1; // 026 - Parsing enhancement
+ uint8_t mvcos:1; // 027 - Move with optional specs
+ uint8_t tods:1; // 028 - TOD steering
+ uint8_t x000:1; // 029 - Undefined
+ uint8_t etf3e:1; // 030 - ETF3 enhancement
+ uint8_t ecput:1; // 031 - Extract CPU time
+ uint8_t csst:1; // 032 - Compare swap and store
+ uint8_t csst2:1; // 033 - Compare swap and store 2
+ uint8_t gie:1; // 034 - General instructions extension
+ uint8_t ee:1; // 035 - Execute extensions
+ uint8_t em:1; // 036 - Enhanced monitor
+ uint8_t fpe:1; // 037 - Floating point extension
+ uint8_t x001:1; // 038 - Undefined
+ uint8_t ibm03:1; // 039 - Assigned to IBM
+ uint8_t spp:1; // 040 - Set program parameters
+ uint8_t fpse:1; // 041 - FP support enhancement
+ uint8_t dfp:1; // 042 - DFP
+ uint8_t dfph:1; // 043 - DFP high performance
+ uint8_t pfpo:1; // 044 - PFPO instruction
+ uint8_t multi:1; // 045 - Multiple inc load/store on CC 1
+ uint8_t ibm04:1; // 046 - Assigned to IBM
+ uint8_t cmpsce:1; // 047 - CMPSC enhancement
+ uint8_t dfpzc:1; // 048 - DFP zoned conversion
+ uint8_t misc:1; // 049 - Multiple inc load and trap
+ uint8_t ctx:1; // 050 - Constrained transactional-execution
+ uint8_t ltlb:1; // 051 - Local TLB clearing
+ uint8_t ia:1; // 052 - Interlocked access
+ uint8_t lsoc2:1; // 053 - Load/store on CC 2
+ uint8_t x002:1; // 054 - Undefined
+ uint8_t ibm05:1; // 055 - Assigned to IBM
+ uint8_t x003:1; // 056 - Undefined
+ uint8_t msa5:1; // 057 - Message security assist 5
+ uint8_t x004:1; // 058 - Undefined
+ uint8_t x005:1; // 059 - Undefined
+ uint8_t x006:1; // 060 - Undefined
+ uint8_t x007:1; // 061 - Undefined
+ uint8_t ibm06:1; // 062 - Assigned to IBM
+ uint8_t x008:1; // 063 - Undefined
+ uint8_t x009:1; // 064 - Undefined
+ uint8_t ibm07:1; // 065 - Assigned to IBM
+ uint8_t rrbm:1; // 066 - Reset reference bits multiple
+ uint8_t cmc:1; // 067 - CPU measurement counter
+ uint8_t cms:1; // 068 - CPU Measurement sampling
+ uint8_t ibm08:1; // 069 - Assigned to IBM
+ uint8_t ibm09:1; // 070 - Assigned to IBM
+ uint8_t ibm10:1; // 071 - Assigned to IBM
+ uint8_t ibm11:1; // 072 - Assigned to IBM
+ uint8_t txe:1; // 073 - Transactional execution
+ uint8_t sthy:1; // 074 - Store hypervisor information
+ uint8_t aefsi:1; // 075 - Access exception fetch/store indication
+ uint8_t msa3:1; // 076 - Message security assist 3
+ uint8_t msa4:1; // 077 - Message security assist 4
+ uint8_t edat2:1; // 078 - Enhanced DAT 2
+ uint8_t x010:1; // 079 - Undefined
+ uint8_t dfppc:1; // 080 - DFP packed conversion
+ uint8_t x011:7; // 081-87 - Undefined
+ uint8_t x012[5]; // 088-127 - Undefined
+ uint8_t ibm12:1; // 128 - Assigned to IBM
+ uint8_t vec:1; // 129 - Vector facility
+ uint8_t x013:6; // 130-135 - Undefined
+ uint8_t x014:6; // 136-141 - Undefined
+ uint8_t sccm:1; // 142 - Store CPU counter multiple
+ uint8_t ibm13:1; // 143 - Assigned to IBM
+ uint8_t x015[14]; // 144-256 Undefined
+} __attribute__ ((packed)) __attribute__ ((aligned(8))) facilityList_t;
void
mono_hwcap_arch_init (void)
{
- int lFacs = sizeof(facs) / 8;
+ facilityList_t facs;
+ int lFacs = sizeof (facs) / 8;
- __asm__ (" lgfr 0,%1\n"
- " .insn s,0xb2b00000,%0\n"
- : "=m" (facs) : "r" (lFacs) : "0", "cc");
-}
+ __asm__ __volatile__ (
+ "lgfr\t0,%1\n\t"
+ ".insn\ts,0xb2b00000,%0\n\t"
+ : "=m" (facs)
+ : "r" (lFacs)
+ : "0", "cc"
+ );
-void
-mono_hwcap_print (FILE *f)
-{
+ mono_hwcap_s390x_has_fpe = facs.fpe;
+ mono_hwcap_s390x_has_vec = facs.vec;
}
+++ /dev/null
-#ifndef __MONO_UTILS_HWCAP_S390X_H__
-#define __MONO_UTILS_HWCAP_S390X_H__
-
-#include "mono/utils/mono-hwcap.h"
-
-typedef struct __FACLIST__ {
- uint8_t n3:1; // 000 - N3 instructions
- uint8_t zi:1; // 001 - z/Arch installed
- uint8_t za:1; // 002 - z/Arch active
- uint8_t date:1; // 003 - DAT-enhancement
- uint8_t idtes:1; // 004 - IDTE-segment tables
- uint8_t idter:1; // 005 - IDTE-region tables
- uint8_t asnlx:1; // 006 - ASN-LX reuse
- uint8_t stfle:1; // 007 - STFLE
- uint8_t edat1:1; // 008 - EDAT 1
- uint8_t srs:1; // 009 - Sense-Running-Status
- uint8_t csske:1; // 010 - Conditional SSKE
- uint8_t ctf:1; // 011 - Configuration-topology
- uint8_t ibm01:1; // 012 - Assigned to IBM
- uint8_t ipter:1; // 013 - IPTE-range
- uint8_t nqks:1; // 014 - Nonquiescing key-setting
- uint8_t ibm02:1; // 015 - Assigned to IBM
- uint8_t etf2:1; // 016 - Extended translation 2
- uint8_t msa:1; // 017 - Message security assist 1
- uint8_t ld:1; // 018 - Long displacement
- uint8_t ldh:1; // 019 - Long displacement high perf
- uint8_t mas:1; // 020 - HFP multiply-add-subtract
- uint8_t eif:1; // 021 - Extended immediate
- uint8_t etf3:1; // 022 - Extended translation 3
- uint8_t hux:1; // 023 - HFP unnormalized extension
- uint8_t etf2e:1; // 024 - Extended translation enhanced 2
- uint8_t stckf:1; // 025 - Store clock fast
- uint8_t pe:1; // 026 - Parsing enhancement
- uint8_t mvcos:1; // 027 - Move with optional specs
- uint8_t tods:1; // 028 - TOD steering
- uint8_t x000:1; // 029 - Undefined
- uint8_t etf3e:1; // 030 - ETF3 enhancement
- uint8_t ecput:1; // 031 - Extract CPU time
- uint8_t csst:1; // 032 - Compare swap and store
- uint8_t csst2:1; // 033 - Compare swap and store 2
- uint8_t gie:1; // 034 - General instructions extension
- uint8_t ee:1; // 035 - Execute extensions
- uint8_t em:1; // 036 - Enhanced monitor
- uint8_t fpe:1; // 037 - Floating point extension
- uint8_t x001:1; // 038 - Undefined
- uint8_t ibm03:1; // 039 - Assigned to IBM
- uint8_t spp:1; // 040 - Set program parameters
- uint8_t fpse:1; // 041 - FP support enhancement
- uint8_t dfp:1; // 042 - DFP
- uint8_t dfph:1; // 043 - DFP high performance
- uint8_t pfpo:1; // 044 - PFPO instruction
- uint8_t multi:1; // 045 - Multiple inc load/store on CC 1
- uint8_t ibm04:1; // 046 - Assigned to IBM
- uint8_t cmpsce:1; // 047 - CMPSC enhancement
- uint8_t dfpzc:1; // 048 - DFP zoned conversion
- uint8_t misc:1; // 049 - Multiple inc load and trap
- uint8_t ctx:1; // 050 - Constrained transactional-execution
- uint8_t ltlb:1; // 051 - Local TLB clearing
- uint8_t ia:1; // 052 - Interlocked access
- uint8_t lsoc2:1; // 053 - Load/store on CC 2
- uint8_t x002:1; // 054 - Undefined
- uint8_t ibm05:1; // 055 - Assigned to IBM
- uint8_t x003:1; // 056 - Undefined
- uint8_t msa5:1; // 057 - Message security assist 5
- uint8_t x004:1; // 058 - Undefined
- uint8_t x005:1; // 059 - Undefined
- uint8_t x006:1; // 060 - Undefined
- uint8_t x007:1; // 061 - Undefined
- uint8_t ibm06:1; // 062 - Assigned to IBM
- uint8_t x008:1; // 063 - Undefined
- uint8_t x009:1; // 064 - Undefined
- uint8_t ibm07:1; // 065 - Assigned to IBM
- uint8_t rrbm:1; // 066 - Reset reference bits multiple
- uint8_t cmc:1; // 067 - CPU measurement counter
- uint8_t cms:1; // 068 - CPU Measurement sampling
- uint8_t ibm08:1; // 069 - Assigned to IBM
- uint8_t ibm09:1; // 070 - Assigned to IBM
- uint8_t ibm10:1; // 071 - Assigned to IBM
- uint8_t ibm11:1; // 072 - Assigned to IBM
- uint8_t txe:1; // 073 - Transactional execution
- uint8_t sthy:1; // 074 - Store hypervisor information
- uint8_t aefsi:1; // 075 - Access exception fetch/store indication
- uint8_t msa3:1; // 076 - Message security assist 3
- uint8_t msa4:1; // 077 - Message security assist 4
- uint8_t edat2:1; // 078 - Enhanced DAT 2
- uint8_t x010:1; // 079 - Undefined
- uint8_t dfppc:1; // 080 - DFP packed conversion
- uint8_t x011:7; // 081-87 - Undefined
- uint8_t x012[5]; // 088-127 - Undefined
- uint8_t ibm12:1; // 128 - Assigned to IBM
- uint8_t vec:1; // 129 - Vector facility
- uint8_t x013:6; // 130-135 - Undefined
- uint8_t x014:6; // 136-141 - Undefined
- uint8_t sccm:1; // 142 - Store CPU counter multiple
- uint8_t ibm13:1; // 143 - Assigned to IBM
- uint8_t x015[14]; // 144-256 Undefined
-} __attribute__ ((packed)) __attribute__ ((aligned(8))) facilityList_t;
-
-extern facilityList_t facs;
-
-#endif /* __MONO_UTILS_HWCAP_S390X_H__ */
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
-#include "mono/utils/mono-hwcap-sparc.h"
+#include "mono/utils/mono-hwcap.h"
#include <string.h>
-
#if !defined(__linux__)
#include <sys/systeminfo.h>
#else
#include <unistd.h>
#endif
-gboolean mono_hwcap_sparc_is_v9 = FALSE;
-
void
mono_hwcap_arch_init (void)
{
char buf [1024];
#if !defined(__linux__)
- if (!sysinfo (SI_ISALIST, buf, 1024))
- g_assert_not_reached ();
+ g_assert (sysinfo (SI_ISALIST, buf, 1024));
#else
/* If the page size is 8192, we're on a 64-bit SPARC, which
* in turn means a v9 or better.
mono_hwcap_sparc_is_v9 = strstr (buf, "sparcv9");
}
-
-void
-mono_hwcap_print (FILE *f)
-{
- g_fprintf (f, "mono_hwcap_sparc_is_v9 = %i\n", mono_hwcap_sparc_is_v9);
-}
--- /dev/null
+#include "config.h"
+
+#if defined (TARGET_ARM)
+
+MONO_HWCAP_VAR(arm_is_v5)
+MONO_HWCAP_VAR(arm_is_v6)
+MONO_HWCAP_VAR(arm_is_v7)
+MONO_HWCAP_VAR(arm_has_vfp)
+MONO_HWCAP_VAR(arm_has_vfp3)
+MONO_HWCAP_VAR(arm_has_vfp3_d16)
+MONO_HWCAP_VAR(arm_has_thumb)
+MONO_HWCAP_VAR(arm_has_thumb2)
+
+#elif defined (TARGET_ARM64)
+
+// Nothing here yet.
+
+#elif defined (TARGET_IA64)
+
+// Nothing here yet.
+
+#elif defined (TARGET_MIPS)
+
+// Nothing here yet.
+
+#elif defined (TARGET_POWERPC) || defined (TARGET_POWERPC64)
+
+MONO_HWCAP_VAR(ppc_has_icache_snoop)
+MONO_HWCAP_VAR(ppc_is_isa_2x)
+MONO_HWCAP_VAR(ppc_is_isa_64)
+MONO_HWCAP_VAR(ppc_has_move_fpr_gpr)
+MONO_HWCAP_VAR(ppc_has_multiple_ls_units)
+
+#elif defined (TARGET_S390X)
+
+MONO_HWCAP_VAR(s390x_has_fpe)
+MONO_HWCAP_VAR(s390x_has_vec)
+
+#elif defined (TARGET_SPARC) || defined (TARGET_SPARC64)
+
+MONO_HWCAP_VAR(sparc_is_v9)
+
+#elif defined (TARGET_X86) || defined (TARGET_AMD64)
+
+MONO_HWCAP_VAR(x86_is_xen)
+MONO_HWCAP_VAR(x86_has_cmov)
+MONO_HWCAP_VAR(x86_has_fcmov)
+MONO_HWCAP_VAR(x86_has_sse1)
+MONO_HWCAP_VAR(x86_has_sse2)
+MONO_HWCAP_VAR(x86_has_sse3)
+MONO_HWCAP_VAR(x86_has_ssse3)
+MONO_HWCAP_VAR(x86_has_sse41)
+MONO_HWCAP_VAR(x86_has_sse42)
+MONO_HWCAP_VAR(x86_has_sse4a)
+
+#endif
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
-#include "mono/utils/mono-hwcap-x86.h"
+#include "mono/utils/mono-hwcap.h"
#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif
-
#if defined(_MSC_VER)
#include <intrin.h>
#endif
-gboolean mono_hwcap_x86_is_xen = FALSE;
-gboolean mono_hwcap_x86_has_cmov = FALSE;
-gboolean mono_hwcap_x86_has_fcmov = FALSE;
-gboolean mono_hwcap_x86_has_sse1 = FALSE;
-gboolean mono_hwcap_x86_has_sse2 = FALSE;
-gboolean mono_hwcap_x86_has_sse3 = FALSE;
-gboolean mono_hwcap_x86_has_ssse3 = FALSE;
-gboolean mono_hwcap_x86_has_sse41 = FALSE;
-gboolean mono_hwcap_x86_has_sse42 = FALSE;
-gboolean mono_hwcap_x86_has_sse4a = FALSE;
-
static gboolean
cpuid (int id, int *p_eax, int *p_ebx, int *p_ecx, int *p_edx)
{
mono_hwcap_x86_is_xen = !access ("/proc/xen", F_OK);
#endif
}
-
-void
-mono_hwcap_print (FILE *f)
-{
- g_fprintf (f, "mono_hwcap_x86_is_xen = %i\n", mono_hwcap_x86_is_xen);
- g_fprintf (f, "mono_hwcap_x86_has_cmov = %i\n", mono_hwcap_x86_has_cmov);
- g_fprintf (f, "mono_hwcap_x86_has_fcmov = %i\n", mono_hwcap_x86_has_fcmov);
- g_fprintf (f, "mono_hwcap_x86_has_sse1 = %i\n", mono_hwcap_x86_has_sse1);
- g_fprintf (f, "mono_hwcap_x86_has_sse2 = %i\n", mono_hwcap_x86_has_sse2);
- g_fprintf (f, "mono_hwcap_x86_has_sse3 = %i\n", mono_hwcap_x86_has_sse3);
- g_fprintf (f, "mono_hwcap_x86_has_ssse3 = %i\n", mono_hwcap_x86_has_ssse3);
- g_fprintf (f, "mono_hwcap_x86_has_sse41 = %i\n", mono_hwcap_x86_has_sse41);
- g_fprintf (f, "mono_hwcap_x86_has_sse42 = %i\n", mono_hwcap_x86_has_sse42);
- g_fprintf (f, "mono_hwcap_x86_has_sse4a = %i\n", mono_hwcap_x86_has_sse4a);
-}
+++ /dev/null
-#ifndef __MONO_UTILS_HWCAP_X86_H__
-#define __MONO_UTILS_HWCAP_X86_H__
-
-#include "mono/utils/mono-hwcap.h"
-
-extern gboolean mono_hwcap_x86_is_xen;
-extern gboolean mono_hwcap_x86_has_cmov;
-extern gboolean mono_hwcap_x86_has_fcmov;
-extern gboolean mono_hwcap_x86_has_sse1;
-extern gboolean mono_hwcap_x86_has_sse2;
-extern gboolean mono_hwcap_x86_has_sse3;
-extern gboolean mono_hwcap_x86_has_ssse3;
-extern gboolean mono_hwcap_x86_has_sse41;
-extern gboolean mono_hwcap_x86_has_sse42;
-extern gboolean mono_hwcap_x86_has_sse4a;
-
-#endif /* __MONO_UTILS_HWCAP_X86_H__ */
#include "mono/utils/mono-hwcap.h"
+#define MONO_HWCAP_VAR(NAME) gboolean mono_hwcap_ ## NAME = FALSE;
+#include "mono/utils/mono-hwcap-vars.h"
+#undef MONO_HWCAP_VAR
+
static gboolean hwcap_inited = FALSE;
void
if (hwcap_inited)
return;
-#ifdef MONO_CROSS_COMPILE
- /*
- * If we're cross-compiling, we want to be as
- * conservative as possible so that we produce
- * code that's portable. Default to that.
- */
- if (!conservative)
- conservative = "1";
-#endif
-
if (!conservative || strncmp (conservative, "1", 1))
mono_hwcap_arch_init ();
if (verbose && !strncmp (verbose, "1", 1))
- mono_hwcap_print (stdout);
+ mono_hwcap_print ();
+}
+
+void
+mono_hwcap_print (void)
+{
+ g_print ("[mono-hwcap] Detected following hardware capabilities:\n\n");
+
+#define MONO_HWCAP_VAR(NAME) g_print ("\t" #NAME " = %s\n", mono_hwcap_ ## NAME ? "yes" : "no");
+#include "mono/utils/mono-hwcap-vars.h"
+#undef MONO_HWCAP_VAR
+
+ g_print ("\n");
}
#include "mono/utils/mono-compiler.h"
+#define MONO_HWCAP_VAR(NAME) extern gboolean mono_hwcap_ ## NAME;
+#include "mono/utils/mono-hwcap-vars.h"
+#undef MONO_HWCAP_VAR
+
/* Call this function to perform hardware feature detection. Until
* this function has been called, all feature variables will be
* FALSE as a default.
* result in an inconsistent state of the variables. Further,
* feature variables should not be read *while* this function is
* executing.
- *
- * To get at feature variables, include the appropriate header,
- * e.g. mono-hwcap-x86.h for x86(-64).
*/
void mono_hwcap_init (void);
/* Implemented in mono-hwcap-$TARGET.c. Do not call. */
void mono_hwcap_arch_init (void);
-/* Print detected features to the given file. */
-void mono_hwcap_print (FILE *f);
+/* Print detected features to stdout. */
+void mono_hwcap_print (void);
/* Please note: If you're going to use the Linux auxiliary vector
* to detect CPU features, don't use any of the constant names in
}
gpointer
-get_hazardous_pointer_with_mask (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index)
+mono_lls_get_hazardous_pointer_with_mask (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index)
{
gpointer p;
/*
Initialize @list and will use @free_node_func to release memory.
If @free_node_func is null the caller is responsible for releasing node memory.
-If @free_node_func may lock, @free_node_func_locking must be
-HAZARD_FREE_MAY_LOCK; otherwise, HAZARD_FREE_NO_LOCK. It is ignored if
-@free_node_func is null.
*/
void
-mono_lls_init (MonoLinkedListSet *list, void (*free_node_func)(void *), HazardFreeLocking free_node_func_locking)
+mono_lls_init (MonoLinkedListSet *list, void (*free_node_func)(void *))
{
list->head = NULL;
list->free_node_func = free_node_func;
- list->locking = free_node_func_locking;
}
/*
Search @list for element with key @key.
-@context specifies whether the function is being called from a lock-free (i.e.
-signal handler or world stopped) context. It is only relevant if a node free
-function was given.
The nodes next, cur and prev are returned in @hp.
Returns true if a node with key @key was found.
*/
gboolean
-mono_lls_find (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, uintptr_t key, HazardFreeContext context)
+mono_lls_find (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, uintptr_t key)
{
MonoLinkedListSetNode *cur, *next;
MonoLinkedListSetNode **prev;
*/
mono_hazard_pointer_set (hp, 2, prev);
- cur = (MonoLinkedListSetNode *) get_hazardous_pointer_with_mask ((gpointer*)prev, hp, 1);
+ cur = (MonoLinkedListSetNode *) mono_lls_get_hazardous_pointer_with_mask ((gpointer*)prev, hp, 1);
while (1) {
if (cur == NULL)
return FALSE;
- next = (MonoLinkedListSetNode *) get_hazardous_pointer_with_mask ((gpointer*)&cur->next, hp, 0);
+ next = (MonoLinkedListSetNode *) mono_lls_get_hazardous_pointer_with_mask ((gpointer*)&cur->next, hp, 0);
cur_key = cur->key;
/*
/*
Insert @value into @list.
-@context specifies whether the function is being called from a lock-free (i.e.
-signal handler or world stopped) context. It is only relevant if a node free
-function was given.
The nodes value, cur and prev are returned in @hp.
Return true if @value was inserted by this call. If it returns FALSE, it's the caller
resposibility to release memory.
*/
gboolean
-mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value, HazardFreeContext context)
+mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value)
{
MonoLinkedListSetNode *cur, **prev;
/*We must do a store barrier before inserting
mono_memory_barrier ();
while (1) {
- if (mono_lls_find (list, hp, value->key, context))
+ if (mono_lls_find (list, hp, value->key))
return FALSE;
cur = (MonoLinkedListSetNode *) mono_hazard_pointer_get_val (hp, 1);
prev = (MonoLinkedListSetNode **) mono_hazard_pointer_get_val (hp, 2);
/*
Search @list for element with key @key and remove it.
-@context specifies whether the function is being called from a lock-free (i.e.
-signal handler or world stopped) context. It is only relevant if a node free
-function was given.
The nodes next, cur and prev are returned in @hp
Returns true if @value was removed by this call.
*/
gboolean
-mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value, HazardFreeContext context)
+mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value)
{
MonoLinkedListSetNode *cur, **prev, *next;
while (1) {
- if (!mono_lls_find (list, hp, value->key, context))
+ if (!mono_lls_find (list, hp, value->key))
return FALSE;
next = (MonoLinkedListSetNode *) mono_hazard_pointer_get_val (hp, 0);
mono_memory_write_barrier ();
mono_hazard_pointer_clear (hp, 1);
if (list->free_node_func)
- mono_thread_hazardous_try_free (value, list->free_node_func);
+ mono_thread_hazardous_queue_free (value, list->free_node_func);
} else
- mono_lls_find (list, hp, value->key, context);
+ mono_lls_find (list, hp, value->key);
return TRUE;
}
}
typedef struct {
MonoLinkedListSetNode *head;
void (*free_node_func)(void *);
- HazardFreeLocking locking;
} MonoLinkedListSet;
You must manually clean the hazard pointer table after using them.
*/
-void
-mono_lls_init (MonoLinkedListSet *list, void (*free_node_func)(void *), HazardFreeLocking free_node_func_locking);
+MONO_API void
+mono_lls_init (MonoLinkedListSet *list, void (*free_node_func)(void *));
-gboolean
-mono_lls_find (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, uintptr_t key, HazardFreeContext context);
+MONO_API gboolean
+mono_lls_find (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, uintptr_t key);
-gboolean
-mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value, HazardFreeContext context);
+MONO_API gboolean
+mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value);
-gboolean
-mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value, HazardFreeContext context);
+MONO_API gboolean
+mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value);
-gpointer
-get_hazardous_pointer_with_mask (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index);
+MONO_API gpointer
+mono_lls_get_hazardous_pointer_with_mask (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index);
static inline gboolean
mono_lls_filter_accept_all (gpointer elem)
restart__ = FALSE; \
MonoLinkedListSetNode **prev__ = &list__->head; \
mono_hazard_pointer_set (hp__, 2, prev__); \
- MonoLinkedListSetNode *cur__ = (MonoLinkedListSetNode *) get_hazardous_pointer_with_mask ((gpointer *) prev__, hp__, 1); \
+ MonoLinkedListSetNode *cur__ = (MonoLinkedListSetNode *) mono_lls_get_hazardous_pointer_with_mask ((gpointer *) prev__, hp__, 1); \
while (1) { \
if (!cur__) { \
break; \
} \
- MonoLinkedListSetNode *next__ = (MonoLinkedListSetNode *) get_hazardous_pointer_with_mask ((gpointer *) &cur__->next, hp__, 0); \
+ MonoLinkedListSetNode *next__ = (MonoLinkedListSetNode *) mono_lls_get_hazardous_pointer_with_mask ((gpointer *) &cur__->next, hp__, 0); \
uintptr_t ckey__ = cur__->key; \
mono_memory_read_barrier (); \
if (*prev__ != cur__) { \
mono_memory_barrier ();
}
-static inline void mono_memory_write_barrier (void)
-{
- mono_memory_barrier ();
-}
-#elif defined(__ia64__)
-static inline void mono_memory_barrier (void)
-{
- __asm__ __volatile__ ("mf" : : : "memory");
-}
-
-static inline void mono_memory_read_barrier (void)
-{
- mono_memory_barrier ();
-}
-
static inline void mono_memory_write_barrier (void)
{
mono_memory_barrier ();
return start_info.handle;
}
-/*
- * mono_threads_platform_resume_created:
- *
- * Resume a newly created thread created using CREATE_SUSPENDED.
- */
-void
-mono_threads_platform_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
-{
- mono_coop_sem_post (&info->create_suspended_sem);
-}
-
gboolean
mono_threads_platform_yield (void)
{
gint32 priority;
MonoCoopSem registered;
gboolean suspend;
- HANDLE suspend_event;
HANDLE handle;
} ThreadStartInfo;
LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
DWORD result;
gboolean suspend = start_info->suspend;
- HANDLE suspend_event = start_info->suspend_event;
MonoThreadInfo *info;
+ int res;
info = mono_thread_info_attach (&result);
info->runtime_thread = TRUE;
- info->create_suspended = suspend;
start_info->handle = info->handle;
mono_threads_platform_set_priority(info, start_info->priority);
+ if (suspend) {
+ info->create_suspended = TRUE;
+ mono_coop_sem_init (&info->create_suspended_sem, 0);
+ }
+
mono_coop_sem_post (&(start_info->registered));
if (suspend) {
- WaitForSingleObject (suspend_event, INFINITE); /* caller will suspend the thread before setting the event. */
- CloseHandle (suspend_event);
+ res = mono_coop_sem_wait (&info->create_suspended_sem, MONO_SEM_FLAGS_NONE);
+ g_assert (res != -1);
+
+ mono_coop_sem_destroy (&info->create_suspended_sem);
}
result = start_func (t_arg);
start_info.suspend = creation_flags & CREATE_SUSPENDED;
start_info.priority = tp->priority;
creation_flags &= ~CREATE_SUSPENDED;
- if (start_info.suspend) {
- start_info.suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
- if (!start_info.suspend_event)
- return NULL;
- }
result = CreateThread (NULL, tp->stack_size, inner_start_thread, &start_info, creation_flags, &thread_id);
if (result) {
/* A new handle has been opened when attaching
* the thread, so we don't need this one */
CloseHandle (result);
-
- if (start_info.suspend) {
- g_assert (SuspendThread (start_info.handle) != (DWORD)-1);
- SetEvent (start_info.suspend_event);
- }
- } else if (start_info.suspend) {
- CloseHandle (start_info.suspend_event);
}
if (out_tid)
*out_tid = thread_id;
return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
}
-void
-mono_threads_platform_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
-{
- HANDLE handle;
-
- handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
- g_assert (handle);
- ResumeThread (handle);
- CloseHandle (handle);
-}
-
#if HAVE_DECL___READFSDWORD==0
static MONO_ALWAYS_INLINE unsigned long long
__readfsdword (unsigned long offset)
{
MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
- if (!mono_lls_find (&thread_list, hp, (uintptr_t)id, HAZARD_FREE_ASYNC_CTX)) {
+ if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
mono_hazard_pointer_clear_all (hp, -1);
return NULL;
}
{
MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
- if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX)) {
+ if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
mono_hazard_pointer_clear_all (hp, -1);
return FALSE;
}
gboolean res;
THREADS_DEBUG ("removing info %p\n", info);
- res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX);
+ res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
mono_hazard_pointer_clear_all (hp, -1);
return res;
}
mono_os_sem_init (&global_suspend_semaphore, 1);
mono_os_sem_init (&suspend_semaphore, 0);
- mono_lls_init (&thread_list, NULL, HAZARD_FREE_NO_LOCK);
+ mono_lls_init (&thread_list, NULL);
mono_thread_smr_init ();
mono_threads_platform_init ();
mono_threads_suspend_init ();
MonoNativeThreadId tid = mono_thread_info_get_tid (info);
/* Have to special case this, as the normal suspend/resume pair are racy, they don't work if he resume is received before the suspend */
info->create_suspended = FALSE;
- mono_threads_platform_resume_created (info, tid);
+ mono_coop_sem_post (&info->create_suspended_sem);
return TRUE;
}
void
mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize);
-gboolean
+MONO_API gboolean
mono_thread_info_yield (void);
gint
void mono_threads_platform_register (THREAD_INFO_TYPE *info);
void mono_threads_platform_unregister (THREAD_INFO_TYPE *info);
HANDLE mono_threads_platform_create_thread (MonoThreadStart start, gpointer arg, MonoThreadParm *, MonoNativeThreadId *out_tid);
-void mono_threads_platform_resume_created (THREAD_INFO_TYPE *info, MonoNativeThreadId tid);
void mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize);
gboolean mono_threads_platform_yield (void);
void mono_threads_platform_exit (int exit_code);