path = external/cecil-legacy
url = git://github.com/mono/cecil.git
branch = mono-legacy-0.9.5
+[submodule "external/boringssl"]
+ path = external/boringssl
+ url = git://github.com/mono/boringssl.git
+ branch = mono
System.Data.dll \
System.Runtime.Serialization.dll \
System.Xml.dll \
- System.Xml.Linq.dll
+ System.Xml.Linq.dll \
+ Mono.Posix.dll
check-profiler-stress:
@$(MAKE) validate-benchmarker RESET_VERSIONS=1
using System;
+using System.Collections.Generic;
using System.Diagnostics;
+using System.Globalization;
using System.IO;
using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Xml;
+using Mono.Unix.Native;
using Newtonsoft.Json;
// Shut up CLS compliance warnings from Json.NET.
namespace Mono.Profiling.Tests.Stress {
// https://github.com/xamarin/benchmarker/blob/master/tools/libdbmodel/Benchmark.cs
- class Benchmark {
+ sealed class Benchmark {
+
public string Name { get; set; }
public string TestDirectory { get; set; }
public bool OnlyExplicit { get; set; }
}
}
+ sealed class TestResult {
+
+ public Benchmark Benchmark { get; set; }
+ public ProcessStartInfo StartInfo { get; set; }
+ public Stopwatch Stopwatch { get; set; } = new Stopwatch ();
+ public int? ExitCode { get; set; }
+ public StringBuilder StandardOutput { get; set; } = new StringBuilder ();
+ public StringBuilder StandardError { get; set; } = new StringBuilder ();
+ }
+
static class Program {
+ static readonly TimeSpan _timeout = TimeSpan.FromHours (6);
+
+ static string FilterInvalidXmlChars (string text) {
+ return Regex.Replace (text, @"[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]", string.Empty);
+ }
+
static int Main ()
{
var depDir = Path.Combine ("..", "external", "benchmarker");
var rand = new Random ();
var cpus = Environment.ProcessorCount;
- var successes = 0;
- var failures = 0;
+ var results = new List<TestResult> (benchmarks.Length);
var sw = Stopwatch.StartNew ();
WorkingDirectory = Path.Combine (testDir, bench.TestDirectory),
FileName = monoPath,
Arguments = $"--debug --profile=log:{profOptions} " + string.Join (" ", bench.CommandLine),
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
};
info.EnvironmentVariables.Clear ();
Console.WriteLine ($"[{sw.Elapsed.ToString ("G")}] {progress} Running {bench.Name} with profiler options: {profOptions}");
Console.ResetColor ();
- var sw2 = Stopwatch.StartNew ();
+ var result = new TestResult {
+ Benchmark = bench,
+ StartInfo = info,
+ };
+
+ using (var proc = new Process ()) {
+ proc.StartInfo = info;
+
+ proc.OutputDataReceived += (sender, args) => {
+ if (args.Data != null)
+ result.StandardOutput.AppendLine (args.Data);
+ };
+
+ proc.ErrorDataReceived += (sender, args) => {
+ if (args.Data != null)
+ result.StandardError.AppendLine (args.Data);
+ };
+
+ result.Stopwatch.Start ();
- using (var proc = Process.Start (info)) {
- proc.WaitForExit ();
- sw2.Stop ();
+ proc.Start ();
- Console.WriteLine ();
+ proc.BeginOutputReadLine ();
+ proc.BeginErrorReadLine ();
- if (proc.ExitCode != 0)
- failures++;
- else
- successes++;
+ if (!proc.WaitForExit ((int) _timeout.TotalMilliseconds)) {
+ // Force a thread dump.
+ Syscall.kill (proc.Id, Signum.SIGQUIT);
+ Thread.Sleep (1000);
- Console.ForegroundColor = proc.ExitCode != 0 ? ConsoleColor.Red : ConsoleColor.Green;
- Console.WriteLine ($"[{sw.Elapsed.ToString ("G")}] {progress} {bench.Name} took {sw2.Elapsed.ToString ("G")} and exited with code: {proc.ExitCode}");
+ try {
+ proc.Kill ();
+ } catch (Exception) {
+ }
+ } else
+ result.ExitCode = proc.ExitCode;
+
+ result.Stopwatch.Stop ();
+ }
+
+ var resultStr = result.ExitCode == null ? "timed out" : $"exited with code: {result.ExitCode}";
+
+ Console.ForegroundColor = result.ExitCode != 0 ? ConsoleColor.Red : ConsoleColor.Green;
+ Console.WriteLine ($"[{sw.Elapsed.ToString ("G")}] {progress} {bench.Name} took {result.Stopwatch.Elapsed.ToString ("G")} and {resultStr}");
+ Console.ResetColor ();
+
+ if (result.ExitCode != 0) {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine ("===== stdout =====");
Console.ResetColor ();
+
+ Console.WriteLine (result.StandardOutput.ToString ());
+
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine ("===== stderr =====");
+ Console.ResetColor ();
+
+ Console.WriteLine (result.StandardError.ToString ());
}
+
+ results.Add (result);
}
sw.Stop ();
- Console.ForegroundColor = failures != 0 ? ConsoleColor.Red : ConsoleColor.Green;
- Console.WriteLine ($"[{sw.Elapsed.ToString ("G")}] Finished with {successes}/{benchmarks.Length} passing tests");
+ var successes = results.Count (r => r.ExitCode == 0);
+ var failures = results.Count (r => r.ExitCode != null && r.ExitCode != 0);
+ var timeouts = results.Count (r => r.ExitCode == null);
+
+ var settings = new XmlWriterSettings {
+ NewLineOnAttributes = true,
+ Indent = true,
+ };
+
+ using (var writer = XmlWriter.Create ("TestResult-profiler-stress.xml", settings)) {
+ writer.WriteStartDocument ();
+ writer.WriteComment ("This file represents the results of running a test suite");
+
+ writer.WriteStartElement ("test-results");
+ writer.WriteAttributeString ("name", "profiler-stress-tests.dummy");
+ writer.WriteAttributeString ("total", results.Count.ToString ());
+ writer.WriteAttributeString ("failures", failures.ToString ());
+ writer.WriteAttributeString ("not-run", "0");
+ writer.WriteAttributeString ("date", DateTime.Now.ToString ("yyyy-MM-dd"));
+ writer.WriteAttributeString ("time", DateTime.Now.ToString ("HH:mm:ss"));
+
+ writer.WriteStartElement ("environment");
+ writer.WriteAttributeString ("nunit-version", "2.4.8.0");
+ writer.WriteAttributeString ("clr-version", Environment.Version.ToString ());
+ writer.WriteAttributeString ("os-version", Environment.OSVersion.ToString ());
+ writer.WriteAttributeString ("platform", Environment.OSVersion.Platform.ToString ());
+ writer.WriteAttributeString ("cwd", Environment.CurrentDirectory);
+ writer.WriteAttributeString ("machine-name", Environment.MachineName);
+ writer.WriteAttributeString ("user", Environment.UserName);
+ writer.WriteAttributeString ("user-domain", Environment.UserDomainName);
+ writer.WriteEndElement ();
+
+ writer.WriteStartElement ("culture-info");
+ writer.WriteAttributeString ("current-culture", CultureInfo.CurrentCulture.Name);
+ writer.WriteAttributeString ("current-uiculture", CultureInfo.CurrentUICulture.Name);
+ writer.WriteEndElement ();
+
+ writer.WriteStartElement ("test-suite");
+ writer.WriteAttributeString ("name", "profiler-stress-tests.dummy");
+ writer.WriteAttributeString ("success", (failures + timeouts == 0).ToString ());
+ writer.WriteAttributeString ("time", ((int) sw.Elapsed.TotalSeconds).ToString ());
+ writer.WriteAttributeString ("asserts", (failures + timeouts).ToString ());
+ writer.WriteStartElement ("results");
+
+ writer.WriteStartElement ("test-suite");
+ writer.WriteAttributeString ("name", "MonoTests");
+ writer.WriteAttributeString ("success", (failures + timeouts == 0).ToString ());
+ writer.WriteAttributeString ("time", ((int) sw.Elapsed.TotalSeconds).ToString ());
+ writer.WriteAttributeString ("asserts", (failures + timeouts).ToString ());
+ writer.WriteStartElement ("results");
+
+ writer.WriteStartElement ("test-suite");
+ writer.WriteAttributeString ("name", "profiler-stress");
+ writer.WriteAttributeString ("success", (failures + timeouts == 0).ToString ());
+ writer.WriteAttributeString ("time", ((int) sw.Elapsed.TotalSeconds).ToString ());
+ writer.WriteAttributeString ("asserts", (failures + timeouts).ToString ());
+ writer.WriteStartElement ("results");
+
+ foreach (var result in results) {
+ var timeoutStr = result.ExitCode == null ? "_timeout" : string.Empty;
+
+ writer.WriteStartElement ("test-case");
+ writer.WriteAttributeString ("name", $"MonoTests.profiler-stress.{result.Benchmark.Name}{timeoutStr}");
+ writer.WriteAttributeString ("executed", "True");
+ writer.WriteAttributeString ("success", (result.ExitCode == 0).ToString ());
+ writer.WriteAttributeString ("time", ((int) result.Stopwatch.Elapsed.TotalSeconds).ToString ());
+ writer.WriteAttributeString ("asserts", result.ExitCode == 0 ? "0" : "1");
+
+ if (result.ExitCode != 0) {
+ writer.WriteStartElement ("failure");
+
+ writer.WriteStartElement ("message");
+ writer.WriteCData (FilterInvalidXmlChars (result.StandardOutput.ToString ()));
+ writer.WriteEndElement ();
+
+ writer.WriteStartElement ("stack-trace");
+ writer.WriteCData (FilterInvalidXmlChars (result.StandardError.ToString ()));
+ writer.WriteEndElement ();
+
+ writer.WriteEndElement ();
+ }
+
+ writer.WriteEndElement ();
+ }
+
+ writer.WriteEndElement ();
+ writer.WriteEndElement ();
+
+ writer.WriteEndElement ();
+ writer.WriteEndElement ();
+
+ writer.WriteEndElement ();
+ writer.WriteEndElement ();
+
+ writer.WriteEndElement ();
+
+ writer.WriteEndDocument ();
+ }
+
+ var failureStr = failures + timeouts != 0 ? $" ({failures} failures, {timeouts} timeouts)" : string.Empty;
+
+ Console.ForegroundColor = failures + timeouts != 0 ? ConsoleColor.Red : ConsoleColor.Green;
+ Console.WriteLine ($"[{sw.Elapsed.ToString ("G")}] Finished with {successes}/{results.Count} passing tests{failureStr}");
Console.ResetColor ();
- return failures;
+ return failures + timeouts;
}
}
}
AC_MSG_RESULT(no)
])
-AC_MSG_CHECKING(for deprecated __attribute__)
-AC_TRY_COMPILE([
- int doit (void) __attribute__ ((deprecated));
- int doit (void) { return 0; }
-], [
- return 0;
-], [
- have_deprecated=yes
- AC_MSG_RESULT(yes)
-], [
- have_deprecated=no
- AC_MSG_RESULT(no)
-])
-
dnl
dnl Boehm GC configuration
dnl
AC_ARG_ENABLE(bcl-opt, [ --disable-bcl-opt BCL is compiled with no optimizations (allows accurate BCL debugging)], test_bcl_opt=$enableval, test_bcl_opt=yes)
-AC_ARG_ENABLE(perf-events, [ --enable-perf-events Enable using `perf` for profiling on Linux], test_perf_events=$enableval, test_perf_events=no)
-if test "x$test_perf_events" = "xyes"; then
- AC_DEFINE(ENABLE_PERF_EVENTS, 1, [Enable using `perf` for profiling on Linux])
- AC_SUBST(ENABLE_PERF_EVENTS)
-fi
-
AC_MSG_CHECKING([if big-arrays are to be enabled])
AC_ARG_ENABLE(big-arrays, [ --enable-big-arrays Enable the allocation and indexing of arrays greater than Int32.MaxValue], enable_big_arrays=$enableval, enable_big_arrays=no)
if test "x$enable_big_arrays" = "xyes" ; then
case "$BTLS_PLATFORM" in
i386)
btls_arch=i386
- btls_cflags="-m32 -arch i386"
+ btls_cflags="-m32"
+ case $host_os in
+ darwin*)
+ btls_cflags="$btls_cflags -arch i386"
+ esac
;;
x86_64)
btls_arch=x86_64
scripts/mono-find-provides
scripts/mono-find-requires
mono/Makefile
+mono/btls/Makefile
mono/utils/Makefile
mono/metadata/Makefile
mono/dis/Makefile
--- /dev/null
+Subproject commit 432738a3c938b4f751307301c6aa07f2027a8864
\f[I]output.mlpd\f[].
When no option is specified, it is equivalent to using:
.PP
-\f[B]--profile=log:calls,alloc,output=output.mlpd,maxframes=8,calldepth=100\f[]
+\f[B]--profile=log:calls,alloc,output=output.mlpd,maxframes=32,calldepth=100\f[]
.PP
The following options can be used to modify this default behaviour.
Each option is separated from the next by a \f[B],\f[] character,
to the control port
.RE
.IP \[bu] 2
-\f[I]sample[=TYPE[/FREQ]]\f[]: collect statistical samples of the
+\f[I]sample[=FREQ]\f[]: collect statistical samples of the
program behaviour.
The default is to collect a 100 times per second (100 Hz) the
instruction pointer.
-This is equivalent to the value \[lq]cycles/100\[rq].
+This is equivalent to the value \[lq]100\[rq].
A value of zero for \f[I]FREQ\f[] effectively disables sampling.
-On some systems, like with recent Linux kernels, it is possible to
-cause the sampling to happen for other events provided by the
-performance counters of the cpu.
-In this case, \f[I]TYPE\f[] can be one of:
-.RS 2
-.IP \[bu] 2
-\f[I]cycles\f[]: processor cycles
-.IP \[bu] 2
-\f[I]instr\f[]: executed instructions
-.IP \[bu] 2
-\f[I]cacherefs\f[]: cache references
-.IP \[bu] 2
-\f[I]cachemiss\f[]: cache misses
-.IP \[bu] 2
-\f[I]branches\f[]: executed branches
-.IP \[bu] 2
-\f[I]branchmiss\f[]: mispredicted branches
-.RE
-.IP \[bu] 2
-\f[I]time=TIMER\f[]: use the TIMER timestamp mode.
-TIMER can have the following values:
-.RS 2
-.IP \[bu] 2
-\f[I]fast\f[]: a usually faster but possibly more inaccurate timer
-.RE
.IP \[bu] 2
\f[I]maxframes=NUM\f[]: when a stack trace needs to be performed,
collect \f[I]NUM\f[] frames at the most.
-The default is 8.
+The default is 32.
.IP \[bu] 2
\f[I]maxsamples=NUM\f[]: stop allocating reusable sample events
once \f[I]NUM\f[] events have been allocated (a value of zero for
\f[I]heapshot\f[]: perform a heapshot as soon as possible
.RE
.IP \[bu] 2
-\f[I]counters\f[]: sample counters values every 1 second. This allow
-a really lightweight way to have insight in some of the runtime key
-metrics. Counters displayed in non verbose mode are : Methods from AOT,
-Methods JITted using mono JIT, Methods JITted using LLVM, Total time
-spent JITting (sec), User Time, System Time, Total Time, Working Set,
-Private Bytes, Virtual Bytes, Page Faults and CPU Load Average (1min,
-5min and 15min).
+\f[I]nocounters\f[]: disables sampling of runtime and performance
+counters, which is normally done every 1 second.
.IP \[bu] 2
\f[I]coverage\f[]: collect code coverage data. This implies enabling
the \f[I]calls\f[] option.
+.IP \[bu] 2
+\f[I]onlycoverage\f[]: can only be used with \f[I]coverage\f[]. This
+disables most other events so that the profiler mostly only collects
+coverage data.
.RE
.SS Analyzing the profile data
.PP
The stack trace info will be available if method enter/leave events
have been recorded or if stack trace collection wasn't explicitly
disabled with the \f[I]maxframes=0\f[] profiler option.
-Note that the profiler will collect up to 8 frames by default at
-specific events when the \f[I]nocalls\f[] option is used, so in
-that case, if more stack frames are required in mprof-report, a
-bigger value for maxframes when profiling must be used, too.
.PP
The \f[I]--traces\f[] option also controls the reverse reference
feature in the heapshot report: for each class it reports how many
needs to be inspected.
The \f[I]MODE\f[] parameter of the \f[I]heapshot\f[] option can be
used to reduce the frequency of the heap shots.
-.IP "\f[I]Reduce the timestamp overhead\f[]" 4
-.Sp
-On many operating systems or architectures what actually slows down
-profiling is the function provided by the system to get timestamp
-information.
-The \f[I]time=fast\f[] profiler option can be usually used to speed
-up this operation, but, depending on the system, time accounting
-may have some level of approximation (though statistically the data
-should be still fairly valuable).
.SS Dealing with the size of the data files
.PP
When collecting a lot of information about a profiled program, huge
NO_THREAD_SUSPEND_RESUME=1
NO_MULTIPLE_APPDOMAINS=1
NO_PROCESS_START=1
+NO_MONO_SECURITY=1
# The binding generator (bwatch) still needs to execute processes,
# so we need a System.dll that can do that.
#NO_PROCESS_START=1
+NO_MONO_SECURITY=1
monotouch_dirs := \
$(mobile_static_dirs)
-monotouch_watch_dirs := $(monotouch_dirs)
+monotouch_watch_dirs := $(filter-out Mono.Security Mono.Data.Tds,$(monotouch_dirs))
monotouch_tv_dirs := $(monotouch_dirs)
monotouch_runtime_dirs := \
System.XML \
Mono.CSharp
-monotouch_watch_runtime_dirs := $(monotouch_runtime_dirs)
+monotouch_watch_runtime_dirs := $(filter-out Mono.Security Mono.Data.Tds,$(monotouch_runtime_dirs))
monotouch_tv_runtime_dirs := $(monotouch_runtime_dirs)
xammac_4_5_dirs := \
System.Data.Linq \
System.Net.Http \
System.Net.Http.WebRequest \
+ Mono.Btls.Interface \
System.Runtime.InteropServices.RuntimeInformation \
System.Reflection.Context \
System.Net.Http.WinHttpHandler \
net_4_x_parallel_dirs := \
PEAPI \
I18N \
+ Mono.Btls.Interface \
Mono.Http \
Mono.Cairo \
Mono.Cecil \
--- /dev/null
+thisdir = class/Mono.Btls.Interface
+SUBDIRS =
+include ../../build/rules.make
+
+LIBRARY = Mono.Btls.Interface.dll
+LIB_REFS = System Mono.Security
+LIB_MCS_FLAGS = -unsafe -nowarn:1030 -keyfile:../mono.pub -delaysign -d:SECURITY_DEP
+
+include ../../build/library.make
+
+$(the_lib): ../Mono.Security/Makefile
+
--- /dev/null
+./Properties/AssemblyInfo.cs
+../../build/common/Consts.cs
+../../build/common/Locale.cs
+../../build/common/MonoTODOAttribute.cs
+
+Mono.Btls.Interface/BtlsObject.cs
+Mono.Btls.Interface/BtlsProvider.cs
+Mono.Btls.Interface/BtlsX509.cs
+Mono.Btls.Interface/BtlsX509Chain.cs
+Mono.Btls.Interface/BtlsX509Error.cs
+Mono.Btls.Interface/BtlsX509Format.cs
+Mono.Btls.Interface/BtlsX509Lookup.cs
+Mono.Btls.Interface/BtlsX509Name.cs
+Mono.Btls.Interface/BtlsX509Purpose.cs
+Mono.Btls.Interface/BtlsX509Store.cs
+Mono.Btls.Interface/BtlsX509StoreCtx.cs
+Mono.Btls.Interface/BtlsX509StoreManager.cs
+Mono.Btls.Interface/BtlsX509StoreType.cs
+Mono.Btls.Interface/BtlsX509TrustKind.cs
+Mono.Btls.Interface/BtlsX509VerifyFlags.cs
+Mono.Btls.Interface/BtlsX509VerifyParam.cs
+Mono.Btls.Interface/VersionInfo.cs
--- /dev/null
+//
+// BtlsObject.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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;
+
+namespace Mono.Btls.Interface
+{
+ public abstract class BtlsObject : IDisposable
+ {
+ MonoBtlsObject instance;
+
+ internal MonoBtlsObject Instance {
+ get {
+ if (!IsValid)
+ throw new ObjectDisposedException (GetType ().Name);
+ return instance;
+ }
+ }
+
+ internal BtlsObject (MonoBtlsObject instance)
+ {
+ this.instance = instance;
+ }
+
+ public bool IsValid {
+ get { return instance != null && instance.IsValid; }
+ }
+
+ protected void Dispose (bool disposing)
+ {
+ if (disposing) {
+ if (instance != null) {
+ instance.Dispose ();
+ instance = null;
+ }
+ }
+ }
+
+ public void Dispose ()
+ {
+ Dispose (true);
+ GC.SuppressFinalize (this);
+ }
+
+ ~BtlsObject ()
+ {
+ Dispose (false);
+ }
+ }
+}
+
--- /dev/null
+//
+// BtlsProvider.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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 Mono.Security.Interface;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Mono.Btls.Interface
+{
+ public static class BtlsProvider
+ {
+ public static bool IsSupported ()
+ {
+ return MonoBtlsProvider.IsSupported ();
+ }
+
+ public static MonoTlsProvider GetProvider ()
+ {
+ return new MonoBtlsProvider ();
+ }
+
+ public static BtlsX509 CreateNative (byte[] data, BtlsX509Format format)
+ {
+ var x509 = MonoBtlsX509.LoadFromData (data, (MonoBtlsX509Format)format);
+ return new BtlsX509 (x509);
+ }
+
+ public static X509Certificate CreateCertificate (byte[] data, BtlsX509Format format, bool disallowFallback = false)
+ {
+ return MonoBtlsProvider.CreateCertificate (data, (MonoBtlsX509Format)format, disallowFallback);
+ }
+
+ public static X509Certificate2 CreateCertificate2 (byte[] data, BtlsX509Format format, bool disallowFallback = false)
+ {
+ return MonoBtlsProvider.CreateCertificate2 (data, (MonoBtlsX509Format)format, disallowFallback);
+ }
+
+ public static X509Certificate2 CreateCertificate2 (byte[] data, string password, bool disallowFallback = false)
+ {
+ return MonoBtlsProvider.CreateCertificate2 (data, password, disallowFallback);
+ }
+
+ public static BtlsX509Chain CreateNativeChain ()
+ {
+ return new BtlsX509Chain (new MonoBtlsX509Chain ());
+ }
+
+ public static BtlsX509Store CreateNativeStore ()
+ {
+ return new BtlsX509Store (new MonoBtlsX509Store ());
+ }
+
+ public static BtlsX509StoreCtx CreateNativeStoreCtx ()
+ {
+ return new BtlsX509StoreCtx (new MonoBtlsX509StoreCtx ());
+ }
+
+ public static X509Chain CreateChain ()
+ {
+ return MonoBtlsProvider.CreateChain ();
+ }
+
+ public static string GetSystemStoreLocation ()
+ {
+ return MonoBtlsProvider.GetSystemStoreLocation ();
+ }
+
+ public static BtlsX509VerifyParam GetVerifyParam_SslClient ()
+ {
+ return new BtlsX509VerifyParam (MonoBtlsX509VerifyParam.GetSslClient ());
+ }
+
+ public static BtlsX509VerifyParam GetVerifyParam_SslServer ()
+ {
+ return new BtlsX509VerifyParam (MonoBtlsX509VerifyParam.GetSslServer ());
+ }
+
+ public static X509Chain GetManagedChain (BtlsX509Chain chain)
+ {
+ return MonoBtlsProvider.GetManagedChain (chain.Instance);
+ }
+ }
+}
+
--- /dev/null
+//
+// BtlsX509.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.IO;
+using System.Security.Cryptography;
+
+namespace Mono.Btls.Interface
+{
+ public class BtlsX509 : BtlsObject
+ {
+ new internal MonoBtlsX509 Instance {
+ get { return (MonoBtlsX509)base.Instance; }
+ }
+
+ internal BtlsX509 (MonoBtlsX509 x509)
+ : base (x509)
+ {
+ }
+
+ public BtlsX509Name GetSubjectName ()
+ {
+ return new BtlsX509Name (Instance.GetSubjectName ());
+ }
+
+ public BtlsX509Name GetIssuerName ()
+ {
+ return new BtlsX509Name (Instance.GetIssuerName ());
+ }
+
+ public string GetSubjectNameString ()
+ {
+ return Instance.GetSubjectNameString ();
+ }
+
+ public string GetIssuerNameString ()
+ {
+ return Instance.GetIssuerNameString ();
+ }
+
+ public byte[] GetRawData (BtlsX509Format format)
+ {
+ return Instance.GetRawData ((MonoBtlsX509Format)format);
+ }
+
+ public byte[] GetCertHash ()
+ {
+ return Instance.GetCertHash ();
+ }
+
+ public DateTime GetNotBefore ()
+ {
+ return Instance.GetNotBefore ();
+ }
+
+ public DateTime GetNotAfter ()
+ {
+ return Instance.GetNotAfter ();
+ }
+
+ public byte[] GetPublicKeyData ()
+ {
+ return Instance.GetPublicKeyData ();
+ }
+
+ public byte[] GetSerialNumber (bool mono_style)
+ {
+ return Instance.GetSerialNumber (mono_style);
+ }
+
+ public int GetVersion ()
+ {
+ return Instance.GetVersion ();
+ }
+
+ public Oid GetSignatureAlgorithm ()
+ {
+ return Instance.GetSignatureAlgorithm ();
+ }
+
+ public AsnEncodedData GetPublicKeyAsn1 ()
+ {
+ return Instance.GetPublicKeyAsn1 ();
+ }
+
+ public AsnEncodedData GetPublicKeyParameters ()
+ {
+ return Instance.GetPublicKeyParameters ();
+ }
+
+ public long GetSubjectNameHash ()
+ {
+ using (var name = GetSubjectName ())
+ return name.GetHash ();
+ }
+
+ public void Print (Stream stream)
+ {
+ using (var bio = MonoBtlsBio.CreateMonoStream (stream))
+ Instance.Print (bio);
+ }
+
+ public void ExportAsPEM (Stream stream, bool includeHumanReadableForm)
+ {
+ using (var bio = MonoBtlsBio.CreateMonoStream (stream))
+ Instance.ExportAsPEM (bio, includeHumanReadableForm);
+ }
+ }
+}
+
--- /dev/null
+//
+// BtlsX509Chain.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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;
+
+namespace Mono.Btls.Interface
+{
+ public class BtlsX509Chain : BtlsObject
+ {
+ new internal MonoBtlsX509Chain Instance {
+ get { return (MonoBtlsX509Chain)base.Instance; }
+ }
+
+ internal BtlsX509Chain (MonoBtlsX509Chain chain)
+ : base (chain)
+ {
+ }
+
+ public int Count {
+ get { return Instance.Count; }
+ }
+
+ public BtlsX509 this[int index] {
+ get {
+ var x509 = Instance.GetCertificate (index);
+ return new BtlsX509 (x509.Copy ());
+ }
+ }
+
+ public void Add (BtlsX509 x509)
+ {
+ Instance.AddCertificate (x509.Instance);
+ }
+ }
+}
+
--- /dev/null
+//
+// BtlsX509Error.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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;
+namespace Mono.Btls.Interface
+{
+ // Keep in sync with NativeBoringX509Error
+ public enum BtlsX509Error
+ {
+ OK = 0,
+ /* illegal error (for uninitialized values, to avoid X509_V_OK): 1 */
+
+ UNABLE_TO_GET_ISSUER_CERT = 2,
+ UNABLE_TO_GET_CRL = 3,
+ UNABLE_TO_DECRYPT_CERT_SIGNATURE = 4,
+ UNABLE_TO_DECRYPT_CRL_SIGNATURE = 5,
+ UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY = 6,
+ CERT_SIGNATURE_FAILURE = 7,
+ CRL_SIGNATURE_FAILURE = 8,
+ CERT_NOT_YET_VALID = 9,
+ CERT_HAS_EXPIRED = 10,
+ CRL_NOT_YET_VALID = 11,
+ CRL_HAS_EXPIRED = 12,
+ ERROR_IN_CERT_NOT_BEFORE_FIELD = 13,
+ ERROR_IN_CERT_NOT_AFTER_FIELD = 14,
+ ERROR_IN_CRL_LAST_UPDATE_FIELD = 15,
+ ERROR_IN_CRL_NEXT_UPDATE_FIELD = 16,
+ OUT_OF_MEM = 17,
+ DEPTH_ZERO_SELF_SIGNED_CERT = 18,
+ SELF_SIGNED_CERT_IN_CHAIN = 19,
+ UNABLE_TO_GET_ISSUER_CERT_LOCALLY = 20,
+ UNABLE_TO_VERIFY_LEAF_SIGNATURE = 21,
+ CERT_CHAIN_TOO_LONG = 22,
+ CERT_REVOKED = 23,
+ INVALID_CA = 24,
+ PATH_LENGTH_EXCEEDED = 25,
+ INVALID_PURPOSE = 26,
+ CERT_UNTRUSTED = 27,
+ CERT_REJECTED = 28,
+ /* These are 'informational' when looking for issuer cert */
+ SUBJECT_ISSUER_MISMATCH = 29,
+ AKID_SKID_MISMATCH = 30,
+ AKID_ISSUER_SERIAL_MISMATCH = 31,
+ KEYUSAGE_NO_CERTSIGN = 32,
+
+ UNABLE_TO_GET_CRL_ISSUER = 33,
+ UNHANDLED_CRITICAL_EXTENSION = 34,
+ KEYUSAGE_NO_CRL_SIGN = 35,
+ UNHANDLED_CRITICAL_CRL_EXTENSION = 36,
+ INVALID_NON_CA = 37,
+ PROXY_PATH_LENGTH_EXCEEDED = 38,
+ KEYUSAGE_NO_DIGITAL_SIGNATURE = 39,
+ PROXY_CERTIFICATES_NOT_ALLOWED = 40,
+
+ INVALID_EXTENSION = 41,
+ INVALID_POLICY_EXTENSION = 42,
+ NO_EXPLICIT_POLICY = 43,
+ DIFFERENT_CRL_SCOPE = 44,
+ UNSUPPORTED_EXTENSION_FEATURE = 45,
+
+ UNNESTED_RESOURCE = 46,
+
+ PERMITTED_VIOLATION = 47,
+ EXCLUDED_VIOLATION = 48,
+ SUBTREE_MINMAX = 49,
+ UNSUPPORTED_CONSTRAINT_TYPE = 51,
+ UNSUPPORTED_CONSTRAINT_SYNTAX = 52,
+ UNSUPPORTED_NAME_SYNTAX = 53,
+ CRL_PATH_VALIDATION_ERROR = 54,
+
+ /* Suite B mode algorithm violation */
+ SUITE_B_INVALID_VERSION = 56,
+ SUITE_B_INVALID_ALGORITHM = 57,
+ SUITE_B_INVALID_CURVE = 58,
+ SUITE_B_INVALID_SIGNATURE_ALGORITHM = 59,
+ SUITE_B_LOS_NOT_ALLOWED = 60,
+ SUITE_B_CANNOT_SIGN_P_384_WITH_P_256 = 61,
+
+ /* Host, email and IP check errors */
+ HOSTNAME_MISMATCH = 62,
+ EMAIL_MISMATCH = 63,
+ IP_ADDRESS_MISMATCH = 64,
+
+ /* The application is not happy */
+ APPLICATION_VERIFICATION = 50
+ }
+}
+
--- /dev/null
+//
+// BtlsX509Format.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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;
+
+namespace Mono.Btls.Interface
+{
+ // Keep in sync with NativeBoringX509Format
+ public enum BtlsX509Format
+ {
+ DER = 1,
+ PEM = 2
+ }
+}
+
--- /dev/null
+//
+// BtlsX509Lookup.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.IO;
+using System.Security.Cryptography;
+
+namespace Mono.Btls.Interface
+{
+ public class BtlsX509Lookup : BtlsObject
+ {
+ new internal MonoBtlsX509Lookup Instance {
+ get { return (MonoBtlsX509Lookup)base.Instance; }
+ }
+
+ internal BtlsX509Lookup (MonoBtlsX509Lookup lookup)
+ : base (lookup)
+ {
+ }
+
+ public void Initialize ()
+ {
+ Instance.Initialize ();
+ }
+
+ public void Shutdown ()
+ {
+ Instance.Shutdown ();
+ }
+
+ public BtlsX509 LookupBySubject (BtlsX509Name name)
+ {
+ var x509 = Instance.LookupBySubject (name.Instance);
+ if (x509 == null)
+ return null;
+
+ return new BtlsX509 (x509);
+ }
+
+ public void LoadFile (string file, BtlsX509Format type)
+ {
+ Instance.LoadFile (file, (MonoBtlsX509FileType)type);
+ }
+
+ public void AddDirectory (string dir, BtlsX509Format type)
+ {
+ Instance.AddDirectory (dir, (MonoBtlsX509FileType)type);
+ }
+ }
+}
--- /dev/null
+//
+// BtlsX509Name.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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;
+
+namespace Mono.Btls.Interface
+{
+ public class BtlsX509Name : BtlsObject
+ {
+ new internal MonoBtlsX509Name Instance {
+ get { return (MonoBtlsX509Name)base.Instance; }
+ }
+
+ internal BtlsX509Name (MonoBtlsX509Name name)
+ : base (name)
+ {
+ }
+
+ public string GetString ()
+ {
+ return Instance.GetString ();
+ }
+
+ public byte[] GetRawData (bool use_canon_enc)
+ {
+ return Instance.GetRawData (use_canon_enc);
+ }
+
+ public long GetHash ()
+ {
+ return Instance.GetHash ();
+ }
+
+ public long GetHashOld ()
+ {
+ return Instance.GetHashOld ();
+ }
+ }
+}
+
--- /dev/null
+//
+// BtlsX509Purpose.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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;
+namespace Mono.Btls.Interface
+{
+ // Keep in sync with NativeBoringX509Purpose
+ public enum BtlsX509Purpose
+ {
+ SSL_CLIENT = 1,
+ SSL_SERVER = 2,
+ NS_SSL_SERVER = 3,
+ SMIME_SIGN = 4,
+ SMIME_ENCRYPT = 5,
+ CRL_SIGN = 6,
+ ANY = 7,
+ OCSP_HELPER = 8,
+ TIMESTAMP_SIGN = 9,
+ }
+}
+
--- /dev/null
+//
+// BtlsX509Store.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.Security.Cryptography.X509Certificates;
+
+namespace Mono.Btls.Interface
+{
+ public class BtlsX509Store : BtlsObject
+ {
+ new internal MonoBtlsX509Store Instance {
+ get { return (MonoBtlsX509Store)base.Instance; }
+ }
+
+ internal BtlsX509Store (MonoBtlsX509Store store)
+ : base (store)
+ {
+ }
+
+ public void LoadLocations (string file, string path)
+ {
+ Instance.LoadLocations (file, path);
+ }
+
+ public void AddTrustedRoots ()
+ {
+ Instance.AddTrustedRoots ();
+ }
+
+ public void AddCertificate (BtlsX509 x509)
+ {
+ Instance.AddCertificate (x509.Instance);
+ }
+
+ public int GetCount ()
+ {
+ return Instance.GetCount ();
+ }
+
+ public void AddLookup (X509CertificateCollection certificates, BtlsX509TrustKind trust)
+ {
+ Instance.AddCollection (certificates, (MonoBtlsX509TrustKind)trust);
+ }
+
+ static MonoBtlsX509FileType GetFileType (BtlsX509Format format)
+ {
+ switch (format) {
+ case BtlsX509Format.DER:
+ return MonoBtlsX509FileType.ASN1;
+ case BtlsX509Format.PEM:
+ return MonoBtlsX509FileType.PEM;
+ default:
+ throw new NotSupportedException ();
+ }
+ }
+
+ public void AddDirectoryLookup (string dir, BtlsX509Format format)
+ {
+ Instance.AddDirectoryLookup (dir, GetFileType (format));
+ }
+
+ public void AddFileLookup (string file, BtlsX509Format format)
+ {
+ Instance.AddFileLookup (file, GetFileType (format));
+ }
+
+ }
+}
+
--- /dev/null
+//
+// BtlsX509StoreCtx.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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;
+namespace Mono.Btls.Interface
+{
+ public class BtlsX509StoreCtx : BtlsObject
+ {
+ new internal MonoBtlsX509StoreCtx Instance {
+ get { return (MonoBtlsX509StoreCtx)base.Instance; }
+ }
+
+ internal BtlsX509StoreCtx (MonoBtlsX509StoreCtx ctx)
+ : base (ctx)
+ {
+ }
+
+ public void Initialize (BtlsX509Store store, BtlsX509Chain chain)
+ {
+ Instance.Initialize (store.Instance, chain.Instance);
+ }
+
+ public void SetVerifyParam (BtlsX509VerifyParam param)
+ {
+ Instance.SetVerifyParam (param.Instance);
+ }
+
+ public int Verify ()
+ {
+ return Instance.Verify ();
+ }
+
+ public BtlsX509Error GetError ()
+ {
+ return (BtlsX509Error)Instance.GetError ();
+ }
+
+ public Exception GetException ()
+ {
+ return Instance.GetException ();
+ }
+
+ public BtlsX509Chain GetChain ()
+ {
+ return new BtlsX509Chain (Instance.GetChain ());
+ }
+ }
+}
+
--- /dev/null
+//
+// BtlsX509StoreManager.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.IO;
+using System.Security.Cryptography;
+
+namespace Mono.Btls.Interface
+{
+ public static class BtlsX509StoreManager
+ {
+ public static bool HasStore (BtlsX509StoreType type)
+ {
+ return MonoBtlsX509StoreManager.HasStore ((MonoBtlsX509StoreType)type);
+ }
+
+ public static string GetStorePath (BtlsX509StoreType type)
+ {
+ return MonoBtlsX509StoreManager.GetStorePath ((MonoBtlsX509StoreType)type);
+ }
+ }
+}
--- /dev/null
+//
+// BtlsX509StoreType.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.IO;
+using System.Security.Cryptography;
+
+namespace Mono.Btls.Interface
+{
+ // Keep in sync with MonoBtlsX509StoreType
+ public enum BtlsX509StoreType
+ {
+ Custom,
+ MachineTrustedRoots,
+ MachineIntermediateCA,
+ MachineUntrusted,
+ UserTrustedRoots,
+ UserIntermediateCA,
+ UserUntrusted
+ }
+}
--- /dev/null
+//
+// BtlsX509TrustKind.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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;
+namespace Mono.Btls.Interface
+{
+ // Keep in sync with MonoBtlsX509TrustKind
+ [Flags]
+ public enum BtlsX509TrustKind
+ {
+ DEFAULT = 0,
+ TRUST_CLIENT = 1,
+ TRUST_SERVER = 2,
+ TRUST_ALL = 4,
+ REJECT_CLIENT = 32,
+ REJECT_SERVER = 64,
+ REJECT_ALL = 128
+ }
+}
+
--- /dev/null
+//
+// BtlsX509VerifyFlags.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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;
+namespace Mono.Btls.Interface
+{
+ // Keep in sync with NativeBoringX509VerifyFlags
+ public enum BtlsX509VerifyFlags
+ {
+ DEFAULT = 0,
+ CRL_CHECK = 1,
+ CRL_CHECK_ALL = 2,
+ X509_STRIC = 4
+ }
+}
+
--- /dev/null
+//
+// BtlsX509VerifyParam.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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;
+namespace Mono.Btls.Interface
+{
+ public class BtlsX509VerifyParam : BtlsObject
+ {
+ new internal MonoBtlsX509VerifyParam Instance {
+ get { return (MonoBtlsX509VerifyParam)base.Instance; }
+ }
+
+ internal BtlsX509VerifyParam (MonoBtlsX509VerifyParam param)
+ : base (param)
+ {
+ }
+
+ public BtlsX509VerifyParam Copy ()
+ {
+ return new BtlsX509VerifyParam (Instance.Copy ());
+ }
+
+ public void SetName (string name)
+ {
+ Instance.SetName (name);
+ }
+
+ public void SetHost (string name)
+ {
+ Instance.SetHost (name);
+ }
+
+ public void AddHost (string name)
+ {
+ Instance.AddHost (name);
+ }
+
+ public BtlsX509VerifyFlags GetFlags ()
+ {
+ return (BtlsX509VerifyFlags)Instance.GetFlags ();
+ }
+
+ public void SetFlags (BtlsX509VerifyFlags flags)
+ {
+ Instance.SetFlags ((ulong)flags);
+ }
+
+ public void SetPurpose (BtlsX509Purpose purpose)
+ {
+ Instance.SetPurpose ((MonoBtlsX509Purpose)purpose);
+ }
+
+ public int GetDepth ()
+ {
+ return Instance.GetDepth ();
+ }
+
+ public void SetDepth (int depth)
+ {
+ Instance.SetDepth (depth);
+ }
+
+ public void SetTime (DateTime time)
+ {
+ Instance.SetTime (time);
+ }
+ }
+}
+
--- /dev/null
+//
+// VersionInfo.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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;
+namespace Mono.Btls.Interface
+{
+ public static class VersionInfo
+ {
+ public const string Version = "1.0.0";
+ }
+}
+
--- /dev/null
+//
+// AssemblyInfo.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.Resources;
+using System.Security;
+using System.Security.Permissions;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about the system assembly
+
+[assembly: AssemblyVersion (Consts.FxVersion)]
+
+[assembly: AssemblyCompany ("MONO development team")]
+[assembly: AssemblyCopyright ("(c) 2016 Xamarin")]
+[assembly: AssemblyDescription ("Mono.Btls.Interface")]
+[assembly: AssemblyProduct ("MONO CLI")]
+[assembly: AssemblyTitle ("Mono.Btls.Interface")]
+[assembly: CLSCompliant (true)]
+[assembly: ComVisible (false)]
+[assembly: NeutralResourcesLanguage ("en-US")]
+
--- /dev/null
+Mono.Btls.Interface
+===================
+
+The purpose of this assembly is to allow test suites such as xamarin/web-tests
+(https://github.com/xamarin/web-tests/tree/stable) to test parts of BTLS without
+making System.dll internals visible.
+
+It should not be considered a stable and maintained API for third parties.
+
ifdef VALID_TEST_PROFILE
TEST_HELPERS_SOURCES = \
- ../test-helpers/NetworkHelpers.cs
+ ../test-helpers/NetworkHelpers.cs \
+ Test/TypeLoadClass.cs
test-local: dtest-app.exe dtest-excfilter.exe
--- /dev/null
+
+class TypeLoadClass
+{
+ static TypeLoadClass ()
+ {
+ }
+}
+
+class TypeLoadClass2
+{
+}
\ No newline at end of file
}
}
-class TypeLoadClass {
-}
-
-class TypeLoadClass2 {
-}
-
public class SentinelClass : MarshalByRefObject {
}
public ProcessStartInfo info;
}
+class LocalReflectClass
+{
+ public static void RunMe ()
+ {
+ var reflectMe = new someClass ();
+ var temp = reflectMe; // Breakpoint location
+ reflectMe.someMethod ();
+ }
+
+ class someClass : ContextBoundObject
+ {
+ public object someField;
+
+ public void someMethod ()
+ {
+ }
+ }
+}
+
// Class used for line number info testing, don't change its layout
public class LineNumbers
{
}
}
-class LocalReflectClass
-{
- public static void RunMe ()
- {
- var reflectMe = new someClass ();
- reflectMe.someMethod ();
- }
-
- class someClass : ContextBoundObject
- {
- public object someField;
-
- public void someMethod ()
- {
- }
- }
-}
MethodMirror m = entry_point.DeclaringType.Assembly.GetType ("LocalReflectClass").GetMethod ("RunMe");
Assert.IsNotNull (m);
- //Console.WriteLine ("X: " + name + " " + m.ILOffsets.Count + " " + m.Locations.Count);
+
+// foreach (var x in m.Locations) {
+// Console.WriteLine (x);
+// }
+
var offset = -1;
int method_base_linum = m.Locations [0].LineNumber;
foreach (var location in m.Locations)
e = single_step (e.Thread);
var frame = e.Thread.GetFrames ()[0];
- Value variable = frame.GetValue (frame.Method.GetLocal ("reflectMe"));
+
+ Assert.IsNotNull (frame);
+ var field = frame.Method.GetLocal ("reflectMe");
+ Assert.IsNotNull (field);
+ Value variable = frame.GetValue (field);
ObjectMirror thisObj = (ObjectMirror)variable;
TypeMirror thisType = thisObj.Type;
return;
string srcfile = (e as BreakpointEvent).Method.DeclaringType.GetSourceFiles (true)[0];
+ srcfile = srcfile.Replace ("dtest-app.cs", "TypeLoadClass.cs");
+ Assert.IsTrue (srcfile.Contains ("TypeLoadClass.cs"));
var req = vm.CreateTypeLoadRequest ();
req.SourceFileFilter = new string [] { srcfile.ToUpper () };
<Compile Include=".\Mono.Security.Interface\HashAlgorithmType.cs" />\r
<Compile Include=".\Mono.Security.Interface\IBufferOffsetSize.cs" />\r
<Compile Include=".\Mono.Security.Interface\IMonoSslStream.cs" />\r
- <Compile Include=".\Mono.Security.Interface\IMonoTlsContext.cs" />\r
<Compile Include=".\Mono.Security.Interface\IMonoTlsEventSink.cs" />\r
<Compile Include=".\Mono.Security.Interface\MonoTlsConnectionInfo.cs" />\r
<Compile Include=".\Mono.Security.Interface\MonoTlsProvider.cs" />\r
+++ /dev/null
-//
-// IMonoTlsContext.cs
-//
-// Author:
-// Martin Baulig <martin.baulig@xamarin.com>
-//
-// Copyright (c) 2015 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.Security.Cryptography;
-using System.Security.Cryptography.X509Certificates;
-
-namespace Mono.Security.Interface
-{
- interface IMonoTlsContext : IDisposable
- {
- bool IsServer {
- get;
- }
-
- bool IsValid {
- get;
- }
-
- void Initialize (IMonoTlsEventSink eventSink);
-
- bool HasCredentials {
- get;
- }
-
- void SetCertificate (X509Certificate certificate, AsymmetricAlgorithm privateKey);
-
- int GenerateNextToken (IBufferOffsetSize incoming, out IBufferOffsetSize outgoing);
-
- int EncryptMessage (ref IBufferOffsetSize incoming);
-
- int DecryptMessage (ref IBufferOffsetSize incoming);
-
- bool ReceivedCloseNotify {
- get;
- }
-
- byte[] CreateCloseNotify ();
-
- byte[] CreateHelloRequest ();
-
- X509Certificate GetRemoteCertificate (out X509CertificateCollection remoteCertificateStore);
-
- bool VerifyRemoteCertificate ();
-
- MonoTlsConnectionInfo GetConnectionInfo ();
- }
-}
-
#endregion
#region Certificate Validation
-
- /*
- * Allows a TLS provider to provide a custom system certificiate validator.
- */
- internal virtual bool HasCustomSystemCertificateValidator {
- get { return false; }
- }
-
/*
* If @serverMode is true, then we're a server and want to validate a certificate
* that we received from a client.
* Returns `true` if certificate validation has been performed and `false` to invoke the
* default system validator.
*/
- internal virtual bool InvokeSystemCertificateValidator (
+ internal abstract bool ValidateCertificate (
ICertificateValidator2 validator, string targetHost, bool serverMode,
X509CertificateCollection certificates, bool wantsChain, ref X509Chain chain,
- out bool success, ref MonoSslPolicyErrors errors, ref int status11)
- {
- throw new InvalidOperationException ();
- }
-
-#endregion
-
-#region Manged SSPI
-
- /*
- * The managed SSPI implementation from the new TLS code.
- */
-
- internal abstract bool SupportsTlsContext {
- get;
- }
-
- internal abstract IMonoTlsContext CreateTlsContext (
- string hostname, bool serverMode, TlsProtocols protocolFlags,
- X509Certificate serverCertificate, X509CertificateCollection clientCertificates,
- bool remoteCertRequired, MonoEncryptionPolicy encryptionPolicy,
- MonoTlsSettings settings);
-
+ ref MonoSslPolicyErrors errors, ref int status11);
#endregion
}
}
using Mono.Security.Cryptography;
using Mono.Security.X509.Extensions;
+using SSCX = System.Security.Cryptography.X509Certificates;
+
namespace Mono.Security.X509 {
#if INSIDE_CORLIB
private X509CertificateCollection _certificates;
private ArrayList _crls;
private bool _crl;
+ private bool _newFormat;
private string _name;
- internal X509Store (string path, bool crl)
+ internal X509Store (string path, bool crl, bool newFormat)
{
_storePath = path;
_crl = crl;
+ _newFormat = newFormat;
}
// properties
{
CheckStore (_storePath, true);
+ if (_newFormat) {
+ ImportNewFormat (certificate);
+ return;
+ }
+
string filename = Path.Combine (_storePath, GetUniqueName (certificate));
if (!File.Exists (filename)) {
filename = Path.Combine (_storePath, GetUniqueNameWithSerial (certificate));
{
CheckStore (_storePath, true);
+ if (_newFormat)
+ throw new NotSupportedException ();
+
string filename = Path.Combine (_storePath, GetUniqueName (crl));
if (!File.Exists (filename)) {
using (FileStream fs = File.Create (filename)) {
public void Remove (X509Certificate certificate)
{
+ if (_newFormat) {
+ RemoveNewFormat (certificate);
+ return;
+ }
+
string filename = Path.Combine (_storePath, GetUniqueNameWithSerial (certificate));
if (File.Exists (filename)) {
File.Delete (filename);
public void Remove (X509Crl crl)
{
+ if (_newFormat)
+ throw new NotSupportedException ();
+
string filename = Path.Combine (_storePath, GetUniqueName (crl));
if (File.Exists (filename)) {
File.Delete (filename);
}
}
+ // new format
+
+ void ImportNewFormat (X509Certificate certificate)
+ {
+#if INSIDE_CORLIB
+ throw new NotSupportedException ();
+#else
+ using (var sscxCert = new SSCX.X509Certificate (certificate.RawData)) {
+ var hash = SSCX.X509Helper2.GetSubjectNameHash (sscxCert);
+ var filename = Path.Combine (_storePath, string.Format ("{0:x8}.0", hash));
+ if (!File.Exists (filename)) {
+ using (FileStream fs = File.Create (filename))
+ SSCX.X509Helper2.ExportAsPEM (sscxCert, fs, true);
+ ClearCertificates ();
+ }
+ }
+#endif
+ }
+
+ void RemoveNewFormat (X509Certificate certificate)
+ {
+#if INSIDE_CORLIB
+ throw new NotSupportedException ();
+#else
+ using (var sscxCert = new SSCX.X509Certificate (certificate.RawData)) {
+ var hash = SSCX.X509Helper2.GetSubjectNameHash (sscxCert);
+ var filename = Path.Combine (_storePath, string.Format ("{0:x8}.0", hash));
+ if (File.Exists (filename)) {
+ File.Delete (filename);
+ ClearCertificates ();
+ }
+ }
+#endif
+ }
+
// private stuff
private string GetUniqueNameWithSerial (X509Certificate certificate)
static private string _userPath;
static private string _localMachinePath;
+ static private string _newUserPath;
+ static private string _newLocalMachinePath;
static private X509Stores _userStore;
static private X509Stores _machineStore;
+ static private X509Stores _newUserStore;
+ static private X509Stores _newMachineStore;
private X509StoreManager ()
{
internal static string CurrentUserPath {
get {
if (_userPath == null) {
- _userPath = Path.Combine(
- Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
+ _userPath = Path.Combine (
+ Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
".mono");
- _userPath = Path.Combine(_userPath, "certs");
+ _userPath = Path.Combine (_userPath, "certs");
}
return _userPath;
}
}
}
+ internal static string NewCurrentUserPath {
+ get {
+ if (_newUserPath == null) {
+ _newUserPath = Path.Combine (
+ Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
+ ".mono");
+ _newUserPath = Path.Combine (_newUserPath, "new-certs");
+ }
+ return _newUserPath;
+ }
+ }
+
+ internal static string NewLocalMachinePath {
+ get {
+ if (_newLocalMachinePath == null) {
+ _newLocalMachinePath = Path.Combine (
+ Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData),
+ ".mono");
+ _newLocalMachinePath = Path.Combine (_newLocalMachinePath, "new-certs");
+ }
+ return _newLocalMachinePath;
+ }
+ }
+
static public X509Stores CurrentUser {
get {
if (_userStore == null)
- _userStore = new X509Stores(CurrentUserPath);
+ _userStore = new X509Stores (CurrentUserPath, false);
return _userStore;
}
static public X509Stores LocalMachine {
get {
if (_machineStore == null)
- _machineStore = new X509Stores (LocalMachinePath);
+ _machineStore = new X509Stores (LocalMachinePath, false);
return _machineStore;
}
}
+ static public X509Stores NewCurrentUser {
+ get {
+ if (_newUserStore == null)
+ _newUserStore = new X509Stores (NewCurrentUserPath, true);
+
+ return _newUserStore;
+ }
+ }
+
+ static public X509Stores NewLocalMachine {
+ get {
+ if (_newMachineStore == null)
+ _newMachineStore = new X509Stores (NewLocalMachinePath, true);
+
+ return _newMachineStore;
+ }
+ }
+
// Merged stores collections
// we need to look at both the user and the machine (entreprise)
// certificates/CRLs when building/validating a chain
class X509Stores {
private string _storePath;
+ private bool _newFormat;
private X509Store _personal;
private X509Store _other;
private X509Store _intermediate;
private X509Store _trusted;
private X509Store _untrusted;
- internal X509Stores (string path)
+ internal X509Stores (string path, bool newFormat)
{
_storePath = path;
+ _newFormat = newFormat;
}
// properties
get {
if (_personal == null) {
string path = Path.Combine (_storePath, Names.Personal);
- _personal = new X509Store (path, false);
+ _personal = new X509Store (path, false, false);
}
return _personal;
}
get {
if (_other == null) {
string path = Path.Combine (_storePath, Names.OtherPeople);
- _other = new X509Store (path, false);
+ _other = new X509Store (path, false, false);
}
return _other;
}
get {
if (_intermediate == null) {
string path = Path.Combine (_storePath, Names.IntermediateCA);
- _intermediate = new X509Store (path, true);
+ _intermediate = new X509Store (path, true, _newFormat);
}
return _intermediate;
}
get {
if (_trusted == null) {
string path = Path.Combine (_storePath, Names.TrustedRoot);
- _trusted = new X509Store (path, true);
+ _trusted = new X509Store (path, true, _newFormat);
}
return _trusted;
}
get {
if (_untrusted == null) {
string path = Path.Combine (_storePath, Names.Untrusted);
- _untrusted = new X509Store (path, false);
+ _untrusted = new X509Store (path, false, _newFormat);
}
return _untrusted;
}
if (!create && !Directory.Exists (path))
return null;
- return new X509Store (path, true);
+ return new X509Store (path, true, false);
}
// names
public const string IntermediateCA = "CA";
public const string TrustedRoot = "Trust";
public const string Untrusted = "Disallowed";
-
+
public Names () {}
}
}
./Mono.Security.Interface/HashAlgorithmType.cs
./Mono.Security.Interface/IBufferOffsetSize.cs
./Mono.Security.Interface/IMonoTlsEventSink.cs
-./Mono.Security.Interface/IMonoTlsContext.cs
./Mono.Security.Interface/IMonoSslStream.cs
./Mono.Security.Interface/MonoTlsConnectionInfo.cs
./Mono.Security.Interface/MonoTlsProvider.cs
throw new NotImplementedException ();
#else
InitializeHandle (safePipeHandle, false, false);
-#endif
IsConnected = true;
+#endif
}
~AnonymousPipeClientStream ()
~NamedPipeClientStream () {
Dispose (false);
}
-
+
+#if !MOBILE
INamedPipeClient impl;
+#endif
public void Connect ()
{
public int NumberOfServerInstances {
get {
CheckPipePropertyOperations ();
+#if MOBILE
+ throw new NotImplementedException ();
+#else
return impl.NumberOfServerInstances;
+#endif
}
}
}
public NamedPipeServerStream (string pipeName, PipeDirection direction, int maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, int inBufferSize, int outBufferSize, PipeSecurity pipeSecurity, HandleInheritability inheritability, PipeAccessRights additionalAccessRights)
: base (direction, transmissionMode, outBufferSize)
{
- var rights = ToAccessRights (direction) | additionalAccessRights;
- // FIXME: reject some rights declarations (for ACL).
#if MOBILE
throw new NotImplementedException ();
#else
+ var rights = ToAccessRights (direction) | additionalAccessRights;
+ // FIXME: reject some rights declarations (for ACL).
+
if (IsWindows)
impl = new Win32NamedPipeServer (this, pipeName, maxNumberOfServerInstances, transmissionMode,
rights, options, inBufferSize, outBufferSize,
LIB_REFS = System System.Xml System.Core System.Numerics
LIB_MCS_FLAGS = \
- -nowarn:169,219,414,649 \
+ -nowarn:219,414,649 \
-d:PLATFORM_UNIX \
-d:USEOFFSET \
-d:MONO_PARTIAL_DATA_IMPORT \
-unsafe
+ifdef NO_MONO_SECURITY
+MONO_DATA_TDS=
+else
+MONO_DATA_TDS=Mono.Data.Tds
+endif
+
ifdef MOBILE_PROFILE
-LIB_REFS += Mono.Data.Tds System.Transactions
+LIB_REFS += $(MONO_DATA_TDS) System.Transactions
LIB_MCS_FLAGS += -d:NO_CODEDOM -d:NO_OLEDB -d:NO_ODBC -d:NO_CONFIGURATION
else
-LIB_REFS += System.EnterpriseServices Mono.Data.Tds System.Configuration System.Transactions
+LIB_REFS += System.EnterpriseServices $(MONO_DATA_TDS) System.Configuration System.Transactions
BUILT_SOURCES = \
gen_OdbcConnection.cs \
gen_OleDbConnection.cs \
--- /dev/null
+//
+// SqlBulkCopy.cs
+//
+// Author:
+// Rolf Bjarne Kvinge <rolf@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.Data.Common;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.Data.SqlClient {
+ public sealed class SqlBulkCopy : IDisposable
+ {
+ const string EXCEPTION_MESSAGE = "System.Data.SqlClient.SqlBulkCopy is not supported on the current platform.";
+
+ public SqlBulkCopy (SqlConnection connection)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlBulkCopy (string connectionString)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlBulkCopy (string connectionString, SqlBulkCopyOptions copyOptions)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlBulkCopy (SqlConnection connection, SqlBulkCopyOptions copyOptions, SqlTransaction externalTransaction)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public int BatchSize {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public int BulkCopyTimeout {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public SqlBulkCopyColumnMappingCollection ColumnMappings {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public string DestinationTableName {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public bool EnableStreaming {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public int NotifyAfter {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public void Close ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public void WriteToServer (DataRow [] rows)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public void WriteToServer (DataTable table)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public void WriteToServer (IDataReader reader)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public void WriteToServer (DataTable table, DataRowState rowState)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public void WriteToServer (DbDataReader reader)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public Task WriteToServerAsync (DbDataReader reader)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public Task WriteToServerAsync (DbDataReader reader, CancellationToken cancellationToken)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ private void RowsCopied (long rowsCopied)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public event SqlRowsCopiedEventHandler SqlRowsCopied;
+
+ void IDisposable.Dispose ()
+ {
+ }
+ }
+}
}
}
- [MonoTODO]
public new Task<SqlDataReader> ExecuteReaderAsync ()
{
- throw new NotImplementedException ();
+ return ExecuteReaderAsync (CommandBehavior.Default, CancellationToken.None);
}
- [MonoTODO]
public new Task<SqlDataReader> ExecuteReaderAsync (CancellationToken cancellationToken)
{
- throw new NotImplementedException ();
+ return ExecuteReaderAsync (behavior, CancellationToken.None);
}
- [MonoTODO]
public new Task<SqlDataReader> ExecuteReaderAsync (CommandBehavior behavior)
{
- throw new NotImplementedException ();
+ return ExecuteReaderAsync (CommandBehavior.Default, CancellationToken.None);
}
- [MonoTODO]
public new Task<SqlDataReader> ExecuteReaderAsync (CommandBehavior behavior, CancellationToken cancellationToken)
{
- throw new NotImplementedException ();
+ TaskCompletionSource<SqlDataReader> source = new TaskCompletionSource<SqlDataReader>();
+
+ CancellationTokenRegistration registration = new CancellationTokenRegistration();
+ if (cancellationToken.CanBeCanceled) {
+ if (cancellationToken.IsCancellationRequested) {
+ source.SetCanceled();
+ return source.Task;
+ }
+ registration = cancellationToken.Register(CancelIgnoreFailure);
+ }
+
+ Task<SqlDataReader> returnedTask = source.Task;
+ try {
+ // TODO: RegisterForConnectionCloseNotification(ref returnedTask);
+
+ Task<SqlDataReader>.Factory.FromAsync(BeginExecuteReaderAsync, EndExecuteReader, behavior, null).ContinueWith((t) => {
+ registration.Dispose();
+ if (t.IsFaulted) {
+ Exception e = t.Exception.InnerException;
+ source.SetException(e);
+ }
+ else {
+ if (t.IsCanceled) {
+ source.SetCanceled();
+ }
+ else {
+ source.SetResult(t.Result);
+ }
+ }
+ }, TaskScheduler.Default);
+ }
+ catch (Exception e) {
+ source.SetException(e);
+ }
+
+ return returnedTask;
}
- [MonoTODO]
public Task<XmlReader> ExecuteXmlReaderAsync ()
{
- throw new NotImplementedException ();
+ return ExecuteXmlReaderAsync (CancellationToken.None);
}
- [MonoTODO]
public Task<XmlReader> ExecuteXmlReaderAsync (CancellationToken cancellationToken)
{
- throw new NotImplementedException ();
+ TaskCompletionSource<XmlReader> source = new TaskCompletionSource<XmlReader>();
+
+ CancellationTokenRegistration registration = new CancellationTokenRegistration();
+ if (cancellationToken.CanBeCanceled) {
+ if (cancellationToken.IsCancellationRequested) {
+ source.SetCanceled();
+ return source.Task;
+ }
+ registration = cancellationToken.Register(CancelIgnoreFailure);
+ }
+
+ Task<XmlReader> returnedTask = source.Task;
+ try {
+ // TODO: RegisterForConnectionCloseNotification(ref returnedTask);
+
+ Task<XmlReader>.Factory.FromAsync(BeginExecuteXmlReader, EndExecuteXmlReader, null).ContinueWith((t) => {
+ registration.Dispose();
+ if (t.IsFaulted) {
+ Exception e = t.Exception.InnerException;
+ source.SetException(e);
+ }
+ else {
+ if (t.IsCanceled) {
+ source.SetCanceled();
+ }
+ else {
+ source.SetResult(t.Result);
+ }
+ }
+ }, TaskScheduler.Default);
+ }
+ catch (Exception e) {
+ source.SetException(e);
+ }
+
+ return returnedTask;
}
public
return BeginExecuteReader (callback, stateObject, CommandBehavior.Default);
}
+ IAsyncResult BeginExecuteReaderAsync(CommandBehavior behavior, AsyncCallback callback, object stateObject)
+ {
+ return BeginExecuteReader (callback, stateObject, behavior);
+ }
+
public IAsyncResult BeginExecuteReader (AsyncCallback callback, object stateObject, CommandBehavior behavior)
{
ValidateCommand ("BeginExecuteReader", true);
#endregion // Asynchronous Methods
+#pragma warning disable 0067
+ // TODO: Not implemented
public event StatementCompletedEventHandler StatementCompleted;
+#pragma warning restore
}
}
--- /dev/null
+//
+// SqlCommand.cs
+//
+// Author:
+// Rolf Bjarne Kvinge <rolf@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.Data.Common;
+using System.Data.Sql;
+using System.Threading.Tasks;
+using System.Threading;
+using System.Xml;
+
+namespace System.Data.SqlClient {
+ public sealed class SqlCommand : DbCommand, IDbCommand, ICloneable
+ {
+ const string EXCEPTION_MESSAGE = "System.Data.SqlClient.SqlCommand is not supported on the current platform.";
+
+ public SqlCommand()
+ : this (String.Empty, null, null)
+ {
+ }
+
+ public SqlCommand (string cmdText)
+ : this (cmdText, null, null)
+ {
+ }
+
+ public SqlCommand (string cmdText, SqlConnection connection)
+ : this (cmdText, connection, null)
+ {
+ }
+
+ public SqlCommand (string cmdText, SqlConnection connection, SqlTransaction transaction)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override string CommandText {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override int CommandTimeout {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override CommandType CommandType {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public new SqlConnection Connection {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool DesignTimeVisible {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public new SqlParameterCollection Parameters {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public new SqlTransaction Transaction {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override UpdateRowSource UpdatedRowSource {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public SqlNotificationRequest Notification {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public bool NotificationAutoEnlist {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override void Cancel ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlCommand Clone ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public new SqlParameter CreateParameter ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override int ExecuteNonQuery ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public new SqlDataReader ExecuteReader ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public new SqlDataReader ExecuteReader (CommandBehavior behavior)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public new Task<SqlDataReader> ExecuteReaderAsync ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public new Task<SqlDataReader> ExecuteReaderAsync (CancellationToken cancellationToken)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public new Task<SqlDataReader> ExecuteReaderAsync (CommandBehavior behavior)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public new Task<SqlDataReader> ExecuteReaderAsync (CommandBehavior behavior, CancellationToken cancellationToken)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public Task<XmlReader> ExecuteXmlReaderAsync ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public Task<XmlReader> ExecuteXmlReaderAsync (CancellationToken cancellationToken)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override object ExecuteScalar ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public XmlReader ExecuteXmlReader ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ object ICloneable.Clone ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ }
+
+ public override void Prepare ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public void ResetCommandTimeout ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override DbParameter CreateDbParameter ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override DbDataReader ExecuteDbDataReader (CommandBehavior behavior)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override DbConnection DbConnection {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ protected override DbParameterCollection DbParameterCollection {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ protected override DbTransaction DbTransaction {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public IAsyncResult BeginExecuteNonQuery ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public IAsyncResult BeginExecuteNonQuery (AsyncCallback callback, object stateObject)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public int EndExecuteNonQuery (IAsyncResult asyncResult)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public IAsyncResult BeginExecuteReader ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public IAsyncResult BeginExecuteReader (CommandBehavior behavior)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public IAsyncResult BeginExecuteReader (AsyncCallback callback, object stateObject)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public IAsyncResult BeginExecuteReader (AsyncCallback callback, object stateObject, CommandBehavior behavior)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlDataReader EndExecuteReader (IAsyncResult asyncResult)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public IAsyncResult BeginExecuteXmlReader (AsyncCallback callback, object stateObject)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public IAsyncResult BeginExecuteXmlReader ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public XmlReader EndExecuteXmlReader (IAsyncResult asyncResult)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public event StatementCompletedEventHandler StatementCompleted;
+ }
+}
--- /dev/null
+//
+// SqlCommandBuilder.cs
+//
+// Author:
+// Rolf Bjarne Kvinge <rolf@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.Data;
+using System.Data.Common;
+using System.Data.SqlClient;
+
+namespace System.Data.SqlClient
+{
+ public class SqlCommandBuilder : DbCommandBuilder
+ {
+ const string EXCEPTION_MESSAGE = "System.Data.SqlClient.SqlCommandBuilder is not supported on the current platform.";
+
+ public SqlCommandBuilder ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlCommandBuilder (SqlDataAdapter adapter)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public static void DeriveParameters (SqlCommand command)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlCommand GetDeleteCommand ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlCommand GetInsertCommand ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlCommand GetUpdateCommand ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlCommand GetUpdateCommand (bool useColumnsForParameterNames)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlCommand GetDeleteCommand (bool useColumnsForParameterNames)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlCommand GetInsertCommand (bool useColumnsForParameterNames)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override string QuoteIdentifier (string unquotedIdentifier)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override string UnquoteIdentifier (string quotedIdentifier)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override void ApplyParameterInfo (DbParameter parameter, DataRow datarow, StatementType statementType, bool whereClause)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override string GetParameterName (int parameterOrdinal)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override string GetParameterName (string parameterName)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override string GetParameterPlaceholder (int parameterOrdinal)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override void SetRowUpdatingHandler (DbDataAdapter adapter)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override DataTable GetSchemaTable (DbCommand srcCommand)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override DbCommand InitializeCommand (DbCommand command)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlDataAdapter DataAdapter {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override string QuotePrefix {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override string QuoteSuffix {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override string CatalogSeparator {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override string SchemaSeparator {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override CatalogLocation CatalogLocation {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+ }
+}
[DefaultValue ("")]
[EditorAttribute ("Microsoft.VSDesigner.Data.SQL.Design.SqlConnectionStringEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
- [RecommendedAsConfigurable (true)]
+#pragma warning disable 618 // ignore obsolete warning about RecommendedAsConfigurable to use SettingsBindableAttribute
+ [RecommendedAsConfigurable(true)]
+#pragma warning restore 618
[RefreshProperties (RefreshProperties.All)]
public override string ConnectionString {
get {
--- /dev/null
+//
+// SqlConnection.cs
+//
+// Author:
+// Rolf Bjarne Kvinge <rolf@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.Collections;
+using System.Data.Common;
+
+namespace System.Data.SqlClient
+{
+ public sealed class SqlConnection : DbConnection, IDbConnection, ICloneable
+ {
+ const string EXCEPTION_MESSAGE = "System.Data.SqlClient.SqlConnection is not supported on the current platform.";
+
+ public SqlConnection () : this (null)
+ {
+ }
+
+ public SqlConnection (string connectionString)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlConnection (string connectionString, SqlCredential cred)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override string ConnectionString {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public SqlCredential Credentials {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public Guid ClientConnectionId {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override int ConnectionTimeout {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override string Database {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override string DataSource {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public int PacketSize {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override string ServerVersion {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override ConnectionState State {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public string WorkstationId {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public bool FireInfoMessageEventOnUserErrors {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public bool StatisticsEnabled {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ protected override DbProviderFactory DbProviderFactory {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public event SqlInfoMessageEventHandler InfoMessage;
+
+ public new SqlTransaction BeginTransaction ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public new SqlTransaction BeginTransaction (IsolationLevel iso)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlTransaction BeginTransaction (string transactionName)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlTransaction BeginTransaction (IsolationLevel iso, string transactionName)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void ChangeDatabase (string database)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void Close ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public new SqlCommand CreateCommand ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ }
+
+#if !MOBILE
+ public void EnlistDistributedTransaction (ITransaction transaction)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+#endif
+
+ object ICloneable.Clone ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override DbTransaction BeginDbTransaction (IsolationLevel isolationLevel)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override DbCommand CreateDbCommand ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void Open ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override DataTable GetSchema ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override DataTable GetSchema (String collectionName)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override DataTable GetSchema (String collectionName, string [] restrictionValues)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public static void ChangePassword (string connectionString, string newPassword)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public static void ClearAllPools ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public static void ClearPool (SqlConnection connection)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public void ResetStatistics ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public IDictionary RetrieveStatistics ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+ }
+}
--- /dev/null
+//
+// SqlDataReader.cs
+//
+// Author:
+// Rolf Bjarne Kvinge <rolf@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.Collections;
+using System.Data.Common;
+using System.Data.SqlClient;
+using System.Data.SqlTypes;
+using System.Data;
+using System.IO;
+using System.Threading.Tasks;
+using System.Threading;
+using System.Xml;
+
+
+namespace System.Data.SqlClient
+{
+ public class SqlDataReader : DbDataReader , IDataReader, IDisposable, IDataRecord
+ {
+ const string EXCEPTION_MESSAGE = "System.Data.SqlClient.SqlDataReader is not supported on the current platform.";
+
+ SqlDataReader () {}
+
+ protected bool IsCommandBehavior (CommandBehavior condition)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void Close ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override bool GetBoolean (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override byte GetByte (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override long GetBytes (int i, long dataIndex, byte [] buffer, int bufferIndex, int length)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override char GetChar (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override long GetChars (int i, long dataIndex, char [] buffer, int bufferIndex, int length)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override string GetDataTypeName (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override DateTime GetDateTime (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual DateTimeOffset GetDateTimeOffset (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual TimeSpan GetTimeSpan (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlChars GetSqlChars (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override Decimal GetDecimal (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override Double GetDouble (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override Type GetFieldType (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override Single GetFloat (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override Guid GetGuid (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override short GetInt16 (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override int GetInt32 (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override long GetInt64 (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override string GetName (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override int GetOrdinal (string name)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override DataTable GetSchemaTable ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlBinary GetSqlBinary (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlBoolean GetSqlBoolean (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlByte GetSqlByte (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlDateTime GetSqlDateTime (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlDecimal GetSqlDecimal (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlDouble GetSqlDouble (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlGuid GetSqlGuid (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlInt16 GetSqlInt16 (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlInt32 GetSqlInt32 (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlInt64 GetSqlInt64 (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlMoney GetSqlMoney (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlSingle GetSqlSingle (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlString GetSqlString (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlXml GetSqlXml (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual object GetSqlValue (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual int GetSqlValues (object [] values)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override string GetString (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override object GetValue (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override int GetValues (object [] values)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override IEnumerator GetEnumerator ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override bool IsDBNull (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override bool NextResult ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override bool Read ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override Type GetProviderSpecificFieldType (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override object GetProviderSpecificValue (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override int GetProviderSpecificValues (object [] values)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual SqlBytes GetSqlBytes (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override T GetFieldValue<T> (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual XmlReader GetXmlReader (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override Task<T> GetFieldValueAsync<T> (int i, CancellationToken cancellationToken)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override Stream GetStream (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override TextReader GetTextReader (int i)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override Task<bool> IsDBNullAsync (int i, CancellationToken cancellationToken)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override int Depth {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override int FieldCount {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool IsClosed {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override object this [int i] {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override object this [string name] {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override int RecordsAffected {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool HasRows {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override int VisibleFieldCount {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ protected SqlConnection Connection {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+ }
+}
public bool HasChanges {
get { return true; }
}
-
+
+#pragma warning disable 0067
+ [MonoTODO]
public event OnChangeEventHandler OnChange;
+#pragma warning restore
[MonoTODO]
public void AddCommandDependency(SqlCommand command)
--- /dev/null
+//
+// SqlException.cs
+//
+// Author:
+// Rolf Bjarne Kvinge <rolf@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.Data.Common;
+using System.Data.SqlClient;
+using System.Runtime.Serialization;
+
+namespace System.Data.SqlClient
+{
+ public class SqlException : DbException
+ {
+ const string EXCEPTION_MESSAGE = "System.Data.SqlClient.SqlException is not supported on the current platform.";
+
+ internal bool _doNotReconnect;
+
+ static internal SqlException CreateException(SqlErrorCollection errorCollection, string serverVersion)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ static internal SqlException CreateException(SqlErrorCollection errorCollection, string serverVersion, SqlInternalConnectionTds internalConnection, Exception innerException = null)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ static internal SqlException CreateException(SqlErrorCollection errorCollection, string serverVersion, Guid conId, Exception innerException = null)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ SqlException () {}
+
+ public override void GetObjectData (SerializationInfo si, StreamingContext context)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public byte Class {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public Guid ClientConnectionId {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public SqlErrorCollection Errors {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public int LineNumber {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override string Message {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public int Number {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public string Procedure {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public string Server {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override string Source {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public byte State {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+ }
+}
}
[DefaultValue (0)]
- public byte Precision {
+ public new byte Precision {
get { return metaParameter.Precision; }
set { metaParameter.Precision = value; }
}
[DefaultValue (0)]
- public byte Scale {
+ public new byte Scale {
get { return metaParameter.Scale; }
set { metaParameter.Scale = value; }
}
--- /dev/null
+//
+// SqlParameter.cs
+//
+// Author:
+// Rolf Bjarne Kvinge <rolf@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.Data;
+using System.Data.Common;
+using System.Data.SqlTypes;
+
+namespace System.Data.SqlClient
+{
+ public class SqlParameter : DbParameter , IDbDataParameter, IDataParameter, ICloneable
+ {
+ const string EXCEPTION_MESSAGE = "System.Data.SqlClient.SqlParameter is not supported on the current platform.";
+
+ public SqlParameter ()
+ : this (String.Empty, SqlDbType.NVarChar, 0, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
+ {
+ }
+
+ public SqlParameter (string parameterName, object value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlParameter (string parameterName, SqlDbType dbType)
+ : this (parameterName, dbType, 0, ParameterDirection.Input, false, 0, 0, null, DataRowVersion.Current, null)
+ {
+ }
+
+ public SqlParameter (string parameterName, SqlDbType dbType, int size)
+ : this (parameterName, dbType, size, ParameterDirection.Input, false, 0, 0, null, DataRowVersion.Current, null)
+ {
+ }
+
+ public SqlParameter (string parameterName, SqlDbType dbType, int size, string sourceColumn)
+ : this (parameterName, dbType, size, ParameterDirection.Input, false, 0, 0, sourceColumn, DataRowVersion.Current, null)
+ {
+ }
+
+ public SqlParameter (string parameterName, SqlDbType dbType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string sourceColumn, DataRowVersion sourceVersion, object value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlParameter (string parameterName, SqlDbType dbType, int size, ParameterDirection direction, byte precision, byte scale, string sourceColumn, DataRowVersion sourceVersion, bool sourceColumnNullMapping, Object value, string xmlSchemaCollectionDatabase, string xmlSchemaCollectionOwningSchema, string xmlSchemaCollectionName)
+ : this (parameterName, dbType, size, direction, false, precision, scale, sourceColumn, sourceVersion, value)
+ {
+ }
+
+ public override string ToString ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void ResetDbType ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public void ResetSqlDbType ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override DbType DbType {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override ParameterDirection Direction {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool IsNullable {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public int Offset {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override string ParameterName {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public byte Precision {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public byte Scale {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override int Size {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override string SourceColumn {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override DataRowVersion SourceVersion {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public SqlDbType SqlDbType {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override object Value {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public SqlCompareOptions CompareInfo {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public int LocaleId {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public object SqlValue {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool SourceColumnNullMapping {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public string XmlSchemaCollectionDatabase {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public string XmlSchemaCollectionName {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public string XmlSchemaCollectionOwningSchema {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public string UdtTypeName {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public string TypeName {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ object ICloneable.Clone ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+ }
+}
--- /dev/null
+//
+// SqlParameterCollection.cs
+//
+// Author:
+// Rolf Bjarne Kvinge <rolf@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.Collections;
+using System.Data;
+using System.Data.Common;
+using System.Data.SqlClient;
+
+namespace System.Data.SqlClient
+{
+ public class SqlParameterCollection : DbParameterCollection , IDataParameterCollection, IList, ICollection, IEnumerable
+ {
+ const string EXCEPTION_MESSAGE = "System.Data.SqlClient.SqlParameterCollection is not supported on the current platform.";
+
+ SqlParameterCollection () {}
+
+ protected override DbParameter GetParameter (int index)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override DbParameter GetParameter (string parameterName)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override void SetParameter (int index, DbParameter value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override void SetParameter (string parameterName, DbParameter value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override int Add (object value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlParameter Add (SqlParameter value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlParameter Add (string parameterName, object value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlParameter AddWithValue (string parameterName, object value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlParameter Add (string parameterName, SqlDbType sqlDbType)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlParameter Add (string parameterName, SqlDbType sqlDbType, int size)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlParameter Add (string parameterName, SqlDbType sqlDbType, int size, string sourceColumn)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void Clear ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override bool Contains (object value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override bool Contains (string value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public bool Contains (SqlParameter value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void CopyTo (Array array, int index)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override IEnumerator GetEnumerator ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override int IndexOf (object value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override int IndexOf (string parameterName)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public int IndexOf (SqlParameter value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void Insert (int index, object value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public void Insert (int index, SqlParameter value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void Remove (object value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public void Remove (SqlParameter value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void RemoveAt (int index)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void RemoveAt (string parameterName)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void AddRange (Array values)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public void AddRange (SqlParameter [] values)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public void CopyTo (SqlParameter [] array, int index)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override int Count {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool IsFixedSize {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool IsReadOnly {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool IsSynchronized {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override object SyncRoot {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public SqlParameter this [int index] {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public SqlParameter this [string parameterName] {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+ }
+}
--- /dev/null
+//
+// SqlTransaction.cs
+//
+// Author:
+// Rolf Bjarne Kvinge <rolf@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.Data;
+using System.Data.Common;
+using System.Data.SqlClient;
+
+namespace System.Data.SqlClient
+{
+ public class SqlTransaction : DbTransaction , IDbTransaction, IDisposable
+ {
+ const string EXCEPTION_MESSAGE = "System.Data.SqlClient.SqlTransaction is not supported on the current platform.";
+
+ SqlTransaction () {}
+
+ public override void Commit ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void Rollback ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public void Rollback (string transactionName)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public void Save (string savePointName)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SqlConnection Connection {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override IsolationLevel IsolationLevel {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ protected override DbConnection DbConnection {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+ }
+}
try {
da.AddToBatch (new SqlCommand ());
Assert.Fail ("#1");
+#if FEATURE_NO_BSD_SOCKETS
+ } catch (PlatformNotSupportedException) {
+#else
} catch (NotSupportedException ex) {
Assert.AreEqual (typeof (NotSupportedException), ex.GetType (), "#2");
Assert.IsNull (ex.InnerException, "#3");
Assert.IsNotNull (ex.Message, "#4");
+#endif
}
}
private const string testFailParamNameMessage = "We have to provide the same parameter name as in original .NET";
[Test] // .ctor(SqlConnection connection)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#else
[ExpectedException (typeof(ArgumentNullException))]
+#endif
public void ConstructorNotNull1 ()
{
new SqlBulkCopy ((SqlConnection)null);
}
[Test] // .ctor(string connectionString)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#else
[ExpectedException (typeof(ArgumentNullException))]
+#endif
public void ConstructorNotNull2 ()
{
new SqlBulkCopy ((string)null);
}
[Test] // .ctor(SqlConnection connection)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#else
[ExpectedException (typeof(ArgumentNullException))]
+#endif
public void ConstructorNotNull3 ()
{
try {
}
[Test] // .ctor(string connectionString)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#else
[ExpectedException (typeof(ArgumentNullException))]
+#endif
public void ConstructorNotNull4 ()
{
try {
}
[Test] // .ctor(string connectionString)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#else
[ExpectedException (typeof(ArgumentNullException))]
+#endif
public void ConstructorNotNull5 ()
{
try {
}
[Test] // .ctor(string connectionString)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#else
[ExpectedException (typeof(ArgumentNullException))]
+#endif
public void ConstructorNotNull6 ()
{
try {
public class SqlCommandBuilderTest
{
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void CatalogLocationTest ()
{
SqlCommandBuilder cb = new SqlCommandBuilder ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void CatalogLocation_Value_Invalid ()
{
SqlCommandBuilder cb = new SqlCommandBuilder ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void CatalogSeparator ()
{
SqlCommandBuilder cb = new SqlCommandBuilder ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void CatalogSeparator_Value_Invalid ()
{
string [] separators = new string [] {
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void ConflictOptionTest ()
{
SqlCommandBuilder cb = new SqlCommandBuilder ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void ConflictOption_Value_Invalid ()
{
SqlCommandBuilder cb = new SqlCommandBuilder ();
}
[Test] // QuoteIdentifier (String)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void QuoteIdentifier ()
{
SqlCommandBuilder cb;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void QuoteIdentifier_PrefixSuffix_NoMatch ()
{
SqlCommandBuilder cb;
}
[Test] // QuoteIdentifier (String)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void QuoteIdentifier_UnquotedIdentifier_Null ()
{
SqlCommandBuilder cb = new SqlCommandBuilder ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void QuotePrefix ()
{
SqlCommandBuilder cb = new SqlCommandBuilder ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void QuotePrefix_Value_Invalid ()
{
string [] prefixes = new string [] {
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void QuoteSuffix ()
{
SqlCommandBuilder cb = new SqlCommandBuilder ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void QuoteSuffix_Value_Invalid ()
{
string [] suffixes = new string [] {
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SchemaSeparator ()
{
SqlCommandBuilder cb = new SqlCommandBuilder ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SchemaSeparator_Value_Invalid ()
{
string [] separators = new string [] {
const string COMMAND_TEXT = "SELECT * FROM Authors";
[Test] // SqlCommand ()
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor1 ()
{
SqlCommand cmd = new SqlCommand ();
}
[Test] // SqlCommand (string)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor2 ()
{
SqlCommand cmd = new SqlCommand (COMMAND_TEXT);
}
[Test] // SqlCommand (string, SqlConnection)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor3 ()
{
SqlConnection conn = new SqlConnection ();
}
[Test] // SqlCommand (string, SqlConnection, SqlTransaction)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor4 ()
{
SqlConnection conn = new SqlConnection ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Clone ()
{
SqlNotificationRequest notificationReq = new SqlNotificationRequest ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void CommandText ()
{
SqlCommand cmd = new SqlCommand ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void CommandTimeout ()
{
SqlCommand cmd = new SqlCommand ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void CommandTimeout_Value_Negative ()
{
SqlCommand cmd = new SqlCommand ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void CommandType_Value_Invalid ()
{
SqlCommand cmd = new SqlCommand ();
}
[Test] // bug #324386
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Dispose ()
{
string connectionString = "Initial Catalog=a;Server=b;User ID=c;"
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void ExecuteNonQuery_Connection_Closed ()
{
string connectionString = "Initial Catalog=a;Server=b;User ID=c;"
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void ExecuteNonQuery_Connection_Null ()
{
SqlCommand cmd = new SqlCommand ("delete from whatever");
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void ExecuteReader_Connection_Closed ()
{
string connectionString = "Initial Catalog=a;Server=b;User ID=c;"
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void ExecuteReader_Connection_Null ()
{
SqlCommand cmd = new SqlCommand ("select * from whatever");
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void ExecuteScalar_Connection_Closed ()
{
string connectionString = "Initial Catalog=a;Server=b;User ID=c;"
}
[Test] // bug #412584
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void ExecuteScalar_Connection_Null ()
{
SqlCommand cmd = new SqlCommand ("select count(*) from whatever");
// FIXME: this actually doesn't match .NET behavior. It shouldn't throw NRE.
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Prepare_Connection_Null ()
{
SqlCommand cmd;
}
[Test] // bug #412586
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Prepare_Connection_Closed ()
{
string connectionString = "Initial Catalog=a;Server=b;User ID=c;"
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void ResetCommandTimeout ()
{
SqlCommand cmd = new SqlCommand ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void UpdatedRowSource ()
{
SqlCommand cmd = new SqlCommand ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void UpdatedRowSource_Value_Invalid ()
{
SqlCommand cmd = new SqlCommand ();
[Test] // bug #381100
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void ParameterCollectionTest ()
{
SqlCommand cmd = new SqlCommand();
public class SqlConnectionTest\r
{\r
[Test] // SqlConnection ()\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void Constructor1 ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test] // SqlConnection (string)\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void Constructor2 ()\r
{\r
string connectionString = "server=SQLSRV; database=Mono;";\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void Constructor2_ConnectionString_Invalid ()\r
{\r
try {\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void BeginTransaction_Connection_Closed ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ChangeDatabase_Connection_Closed ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ChangePassword_ConnectionString_Empty ()\r
{\r
try {\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ChangePassword_ConnectionString_Null ()\r
{\r
try {\r
}\r
}\r
\r
- [Test]\r
+ \r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ChangePassword_NewPassword_Empty ()\r
{\r
try {\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ChangePassword_NewPassword_ExceedMaxLength ()\r
{\r
try {\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ChangePassword_NewPassword_Null ()\r
{\r
try {\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ClearPool_Connection_Null ()\r
{\r
try {\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_Value_Invalid ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void CreateCommand ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void Dispose ()\r
{\r
SqlConnection cn = new SqlConnection ("Server=SQLSRV;Database=master;Timeout=25;Packet Size=512;Workstation ID=DUMMY");\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void GetSchema_Connection_Closed ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_AsynchronousProcessing ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_ConnectTimeout ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_ConnectTimeout_Invalid ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_Database_Synonyms ()\r
{\r
SqlConnection cn = null;\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_DataSource_Synonyms ()\r
{\r
SqlConnection cn = null;\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_MaxPoolSize ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_MaxPoolSize_Invalid ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_MinPoolSize ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_MinPoolSize_Invalid ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_MultipleActiveResultSets ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_MultipleActiveResultSets_Invalid ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_NetworkLibrary_Synonyms ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_PacketSize ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_PacketSize_Invalid ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_Password_Synonyms ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_PersistSecurityInfo_Synonyms ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_UserID_Synonyms ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_UserInstance ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_UserInstance_Invalid ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ConnectionString_OtherKeywords ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void Open_ConnectionString_Empty ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void Open_ConnectionString_Null ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void Open_ConnectionString_Whitespace ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void ServerVersion_Connection_Closed ()\r
{\r
SqlConnection cn = new SqlConnection ();\r
}\r
}\r
}\r
+\r
}
[Test] // SqlDataAdapter (SqlCommand)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor2 ()
{
SqlCommand cmd = new SqlCommand ();
}
[Test] // SqlDataAdapter (string, SqlConnection)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor3 ()
{
string selectCommandText = "SELECT * FROM Authors";
}
[Test] // SqlDataAdapter (string, SqlConnection)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor3_SelectCommandText_Null ()
{
SqlConnection selectConnection = new SqlConnection ();
}
[Test] // SqlDataAdapter (string, SqlConnection)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor3_SelectConnection_Null ()
{
string selectCommandText = "SELECT * FROM Authors";
}
[Test] // SqlDataAdapter (string, string)]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor4 ()
{
string selectCommandText = "SELECT * FROM Authors";
}
[Test] // SqlDataAdapter (string, string)]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor4_SelectCommandText_Null ()
{
string selectConnectionString = "server=SQLSRV;database=Mono";
}
[Test] // SqlDataAdapter (string, string)]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor4_SelectConnectionString_Null ()
{
string selectCommandText = "SELECT * FROM Authors";
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void DeleteCommand ()
{
SqlDataAdapter da = new SqlDataAdapter ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Dispose ()
{
SqlDataAdapter da = new SqlDataAdapter ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void InsertCommand ()
{
SqlDataAdapter da = new SqlDataAdapter ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SelectCommand ()
{
SqlDataAdapter da = new SqlDataAdapter ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void UpdateCommand ()
{
SqlDataAdapter da = new SqlDataAdapter ();
public class SqlParameterTest
{
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor1 ()
{
SqlParameter p = new SqlParameter ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor2_Value_DateTime ()
{
DateTime value = new DateTime (2004, 8, 24);
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor2_Value_DBNull ()
{
SqlParameter p = new SqlParameter ("address", DBNull.Value);
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor2_Value_Null ()
{
SqlParameter p = new SqlParameter ("address", (Object) null);
}
[Test] // .ctor (String, SqlDbType, Int32, ParameterDirection, Byte, Byte, String, DataRowVersion, Boolean, Object, String, String, String)
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Constructor7 ()
{
SqlParameter p1 = new SqlParameter ("p1Name", SqlDbType.VarChar, 20,
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void CompareInfo ()
{
SqlParameter parameter = new SqlParameter ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void InferType_Byte ()
{
Byte value = 0x0a;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void InferType_ByteArray ()
{
Byte [] value = new Byte [] { 0x0a, 0x0d };
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void InferType_DateTime ()
{
DateTime value;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void InferType_Decimal ()
{
Decimal value;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void InferType_Double ()
{
Double value;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void InferType_Enum ()
{
SqlParameter param;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void InferType_Guid ()
{
Guid value = Guid.NewGuid ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void InferType_Int16 ()
{
Int16 value;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void InferType_Int32 ()
{
Int32 value;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void InferType_Int64 ()
{
Int64 value;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void InferType_Object ()
{
Object value = new Object ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void InferType_Single ()
{
Single value = Single.MaxValue;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void InferType_String ()
{
String value = "some text";
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void LocaleId ()
{
SqlParameter parameter = new SqlParameter ();
}
[Test] // bug #320196
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void ParameterNullTest ()
{
SqlParameter param = new SqlParameter ("param", SqlDbType.Decimal);
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void ParameterType ()
{
SqlParameter p;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void InferType_Boolean ()
{
Boolean value;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void ParameterName ()
{
SqlParameter p = new SqlParameter ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void ResetDbType ()
{
SqlParameter p;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void ResetSqlDbType ()
{
//Parameter with an assigned value but no SqlDbType specified
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SourceColumn ()
{
SqlParameter p = new SqlParameter ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SourceColumnNullMapping ()
{
SqlParameter p = new SqlParameter ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlDbTypeTest ()
{
SqlParameter p = new SqlParameter ("zipcode", 3510);
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlDbTypeTest_Value_Invalid ()
{
SqlParameter p = new SqlParameter ("zipcode", 3510);
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlValue ()
{
SqlParameter parameter = new SqlParameter ();
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlTypes_SqlBinary ()
{
SqlParameter parameter;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlTypes_SqlBoolean ()
{
SqlParameter parameter;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlTypes_SqlByte ()
{
SqlParameter parameter;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlTypes_SqlDateTime ()
{
SqlParameter parameter;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlTypes_SqlDecimal ()
{
SqlParameter parameter;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlTypes_SqlDouble ()
{
SqlParameter parameter;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlTypes_SqlGuid ()
{
SqlParameter parameter;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlTypes_SqlInt16 ()
{
SqlParameter parameter;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlTypes_SqlInt32 ()
{
SqlParameter parameter;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlTypes_SqlInt64 ()
{
SqlParameter parameter;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlTypes_SqlMoney ()
{
SqlParameter parameter;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlTypes_SqlSingle ()
{
SqlParameter parameter;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlTypes_SqlString ()
{
SqlParameter parameter;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SqlTypes_SqlXml ()
{
SqlParameter parameter;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void Value ()
{
SqlParameter p;
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void XmlSchemaTest ()
{
SqlParameter p1 = new SqlParameter ();
--- /dev/null
+System.Data.SqlClient/SqlBulkCopy.cs
+System.Data.SqlClient/SqlCommand.cs
+System.Data.SqlClient/SqlCommandBuilder.cs
+System.Data.SqlClient/SqlConnection.cs
+System.Data.SqlClient/SqlDataReader.cs
+System.Data.SqlClient/SqlDecimalExtensions.cs
+System.Data.SqlClient/SqlException.cs
+System.Data.SqlClient/SqlParameter.cs
+System.Data.SqlClient/SqlParameterCollection.cs
+System.Data.SqlClient/SqlTransaction.cs
+System.Data.SqlClient/SqlXmlTextReader.cs
#include mobile_System.Data.dll.sources
+System.Data.SqlClient/SqlBulkCopy.platformnotsupported.cs
+System.Data.SqlClient/SqlCommand.platformnotsupported.cs
+System.Data.SqlClient/SqlCommandBuilder.platformnotsupported.cs
+System.Data.SqlClient/SqlConnection.platformnotsupported.cs
+System.Data.SqlClient/SqlDataReader.platformnotsupported.cs
+System.Data.SqlClient/SqlException.platformnotsupported.cs
+System.Data.SqlClient/SqlParameter.platformnotsupported.cs
+System.Data.SqlClient/SqlParameterCollection.platformnotsupported.cs
+System.Data.SqlClient/SqlTransaction.platformnotsupported.cs
SUBDIRS =
include ../../build/rules.make
+ifndef NO_MONO_SECURITY
+MONO_SECURITY=Mono.Security
+endif
+
LIBRARY = System.IdentityModel.dll
-LIB_REFS = System System.Xml System.Security Mono.Security System.Runtime.Serialization
+LIB_REFS = System System.Xml System.Security $(MONO_SECURITY) System.Runtime.Serialization
LIB_MCS_FLAGS = \
/d:NET_3_0 \
$(OTHER_LIB_MCS_FLAGS)
SUBDIRS =
include ../../build/rules.make
+ifndef NO_MONO_SECURITY
+MONO_SECURITY_DLL=$(the_libdir_base)/Mono.Security.dll
+MONO_SECURITY=Mono.Security
+endif
+
LIBRARY = System.Security.dll
-LIB_REFS = secxml/System bare/System.Xml Mono.Security
+LIB_REFS = secxml/System bare/System.Xml $(MONO_SECURITY)
LIB_MCS_FLAGS = -nowarn:618 \
-d:SECURITY_DEP \
-nowarn:414
include ../../build/library.make
-$(build_lib): $(secxml_libdir)/System.dll $(the_libdir_base)/Mono.Security.dll
+$(build_lib): $(secxml_libdir)/System.dll $(MONO_SECURITY_DLL)
$(secxml_libdir)/System.dll:
(cd ../System; $(MAKE) $@)
[assembly: StringFreezing]
[assembly: DefaultDependency (LoadHint.Always)]
+ [assembly: InternalsVisibleTo ("btls-cert-sync, PublicKey=002400000480000094000000060200000024000052534131000400000100010079159977d2d03a8e6bea7a2e74e8d1afcc93e8851974952bb480a12c9134474d04062447c37e0e68c080536fcf3c3fbe2ff9c979ce998475e506e8ce82dd5b0f350dc10e93bf2eeecf874b24770c5081dbea7447fddafa277b22de47d6ffea449674a4f9fccf84d15069089380284dbdd35f46cdff12a1bd78e4ef0065d016df")]
+ [assembly: InternalsVisibleTo ("Mono.Btls.Interface, PublicKey=002400000480000094000000060200000024000052534131000400000100010079159977d2d03a8e6bea7a2e74e8d1afcc93e8851974952bb480a12c9134474d04062447c37e0e68c080536fcf3c3fbe2ff9c979ce998475e506e8ce82dd5b0f350dc10e93bf2eeecf874b24770c5081dbea7447fddafa277b22de47d6ffea449674a4f9fccf84d15069089380284dbdd35f46cdff12a1bd78e4ef0065d016df")]
[assembly: InternalsVisibleTo ("Mono.Security, PublicKey=002400000480000094000000060200000024000052534131000400000100010079159977d2d03a8e6bea7a2e74e8d1afcc93e8851974952bb480a12c9134474d04062447c37e0e68c080536fcf3c3fbe2ff9c979ce998475e506e8ce82dd5b0f350dc10e93bf2eeecf874b24770c5081dbea7447fddafa277b22de47d6ffea449674a4f9fccf84d15069089380284dbdd35f46cdff12a1bd78e4ef0065d016df")]
Test/System/test-uri-props-manual.txt \
Test/System/test-uri-relative-props.txt
+ifndef NO_MONO_SECURITY
+MONO_SECURITY=Mono.Security
+MONO_SECURITY_REF=MonoSecurity=Mono.Security
+MONO_SECURITY_DLL=$(the_libdir_base)Mono.Security.dll
+endif
+
ifndef MOBILE_PROFILE
-TEST_LIB_REFS = System.Drawing Mono.Security System.Data System.Xml System.Core System.Configuration
+TEST_LIB_REFS = System.Drawing $(MONO_SECURITY) System.Data System.Xml System.Core System.Configuration
else
-TEST_LIB_REFS = Mono.Security System.Data System.Xml System.Core
+TEST_LIB_REFS = $(MONO_SECURITY) System.Data System.Xml System.Core
endif
TEST_MCS_FLAGS = -nowarn:618,672,219,67,169,612 \
#
ifeq (secxml/, $(intermediate))
LOCAL_MCS_FLAGS =
-LIB_REFS += bare/System.Xml MonoSecurity=Mono.Security
+LIB_REFS += bare/System.Xml $(MONO_SECURITY_REF)
LIB_MCS_FLAGS += -d:SECURITY_DEP -d:XML_DEP $(EXTERN_ALIAS_FLAGS)
endif
# Flags used to build the final version of System (when intermediate is not defined)
#
ifndef intermediate
-LIB_REFS += System.Xml MonoSecurity=Mono.Security
+LIB_REFS += System.Xml $(MONO_SECURITY_REF)
LIB_MCS_FLAGS += -d:SECURITY_DEP -d:XML_DEP $(EXTERN_ALIAS_FLAGS)
ifndef MOBILE_PROFILE
system_library_deps := \
$(secxml_libdir)/System.dll \
$(the_libdir_base)System.Xml.dll \
- $(the_libdir_base)Mono.Security.dll \
+ $(MONO_SECURITY_DLL) \
$(bare_libdir)/System.dll
ifndef MOBILE_PROFILE
artifacts = $(system_library_deps) \
$(bare_libdir)/System.Xml.dll \
- $(the_libdir_base)Mono.Security.dll \
+ $(MONO_SECURITY_DLL) \
$(the_libdir_base)System.Configuration.dll
.NOTPARALLEL: $(system_library_deps)
endif
ifneq (secxml/,$(intermediate))
-$(secxml_libdir)/System.dll: $(bare_libdir)/System.dll $(bare_libdir)/System.Xml.dll $(the_libdir_base)Mono.Security.dll $(bare_libdir)/System.dll
+$(secxml_libdir)/System.dll: $(bare_libdir)/System.dll $(bare_libdir)/System.Xml.dll $(MONO_SECURITY_DLL) $(bare_libdir)/System.dll
$(MAKE) intermediate=secxml/ $(secxml_libdir)/System.dll
else
build-sec:
--- /dev/null
+//
+// MonoBtlsBio.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Text;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+#if MONOTOUCH
+using MonoTouch;
+#endif
+
+namespace Mono.Btls
+{
+ class MonoBtlsBio : MonoBtlsObject
+ {
+ internal MonoBtlsBio (BoringBioHandle handle)
+ : base (handle)
+ {
+ }
+
+ new protected internal BoringBioHandle Handle {
+ get { return (BoringBioHandle)base.Handle; }
+ }
+
+ protected internal class BoringBioHandle : MonoBtlsHandle
+ {
+ public BoringBioHandle (IntPtr handle)
+ : base (handle, true)
+ {
+ }
+
+ protected override bool ReleaseHandle ()
+ {
+ if (handle != IntPtr.Zero) {
+ mono_btls_bio_free (handle);
+ handle = IntPtr.Zero;
+ }
+ return true;
+ }
+
+ }
+
+ public static MonoBtlsBio CreateMonoStream (Stream stream)
+ {
+ return MonoBtlsBioMono.CreateStream (stream, false);
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_bio_read (IntPtr bio, IntPtr data, int len);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_bio_write (IntPtr bio, IntPtr data, int len);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_bio_flush (IntPtr bio);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_bio_indent (IntPtr bio, uint indent, uint max_indent);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_bio_hexdump (IntPtr bio, IntPtr data, int len, uint indent);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_bio_print_errors (IntPtr bio);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_bio_free (IntPtr handle);
+
+ public int Read (byte[] buffer, int offset, int size)
+ {
+ CheckThrow ();
+ var data = Marshal.AllocHGlobal (size);
+ if (data == IntPtr.Zero)
+ throw new OutOfMemoryException ();
+
+ bool release = false;
+ try {
+ Handle.DangerousAddRef (ref release);
+ var ret = mono_btls_bio_read (Handle.DangerousGetHandle (), data, size);
+ if (ret > 0)
+ Marshal.Copy (data, buffer,offset, ret);
+ return ret;
+ } finally {
+ if (release)
+ Handle.DangerousRelease ();
+ Marshal.FreeHGlobal (data);
+ }
+ }
+
+ public int Write (byte[] buffer, int offset, int size)
+ {
+ CheckThrow ();
+ var data = Marshal.AllocHGlobal (size);
+ if (data == IntPtr.Zero)
+ throw new OutOfMemoryException ();
+
+ bool release = false;
+ try {
+ Handle.DangerousAddRef (ref release);
+ Marshal.Copy (buffer, offset, data, size);
+ return mono_btls_bio_write (Handle.DangerousGetHandle (), data, size);
+ } finally {
+ if (release)
+ Handle.DangerousRelease ();
+ Marshal.FreeHGlobal (data);
+ }
+ }
+
+ public int Flush ()
+ {
+ CheckThrow ();
+ bool release = false;
+ try {
+ Handle.DangerousAddRef (ref release);
+ return mono_btls_bio_flush (Handle.DangerousGetHandle ());
+ } finally {
+ if (release)
+ Handle.DangerousRelease ();
+ }
+ }
+
+ public int Indent (uint indent, uint max_indent)
+ {
+ CheckThrow ();
+ bool release = false;
+ try {
+ Handle.DangerousAddRef (ref release);
+ return mono_btls_bio_indent (Handle.DangerousGetHandle (), indent, max_indent);
+ } finally {
+ if (release)
+ Handle.DangerousRelease ();
+ }
+ }
+
+ public int HexDump (byte[] buffer, uint indent)
+ {
+ CheckThrow ();
+ var data = Marshal.AllocHGlobal (buffer.Length);
+ if (data == IntPtr.Zero)
+ throw new OutOfMemoryException ();
+
+ bool release = false;
+ try {
+ Handle.DangerousAddRef (ref release);
+ Marshal.Copy (buffer, 0, data, buffer.Length);
+ return mono_btls_bio_hexdump (Handle.DangerousGetHandle (), data, buffer.Length, indent);
+ } finally {
+ if (release)
+ Handle.DangerousRelease ();
+ Marshal.FreeHGlobal (data);
+ }
+ }
+
+ public void PrintErrors ()
+ {
+ CheckThrow ();
+ bool release = false;
+ try {
+ Handle.DangerousAddRef (ref release);
+ mono_btls_bio_print_errors (Handle.DangerousGetHandle ());
+ } finally {
+ if (release)
+ Handle.DangerousRelease ();
+ }
+ }
+ }
+
+ class MonoBtlsBioMemory : MonoBtlsBio
+ {
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_bio_mem_new ();
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_bio_mem_get_data (IntPtr handle, out IntPtr data);
+
+ public MonoBtlsBioMemory ()
+ : base (new BoringBioHandle (mono_btls_bio_mem_new ()))
+ {
+ }
+
+ public byte[] GetData ()
+ {
+ IntPtr data;
+ bool release = false;
+ try {
+ Handle.DangerousAddRef (ref release);
+ var size = mono_btls_bio_mem_get_data (Handle.DangerousGetHandle (), out data);
+ CheckError (size > 0);
+ var buffer = new byte[size];
+ Marshal.Copy (data, buffer, 0, size);
+ return buffer;
+ } finally {
+ if (release)
+ Handle.DangerousRelease ();
+ }
+ }
+ }
+
+ interface IMonoBtlsBioMono
+ {
+ int Read (byte[] buffer, int offset, int size, out bool wantMore);
+
+ bool Write (byte[] buffer, int offset, int size);
+
+ void Flush ();
+
+ void Close ();
+ }
+
+ class MonoBtlsBioMono : MonoBtlsBio
+ {
+ GCHandle handle;
+ IntPtr instance;
+ BioReadFunc readFunc;
+ BioWriteFunc writeFunc;
+ BioControlFunc controlFunc;
+ IntPtr readFuncPtr;
+ IntPtr writeFuncPtr;
+ IntPtr controlFuncPtr;
+ IMonoBtlsBioMono backend;
+
+ public MonoBtlsBioMono (IMonoBtlsBioMono backend)
+ : base (new BoringBioHandle (mono_btls_bio_mono_new ()))
+ {
+ this.backend = backend;
+ handle = GCHandle.Alloc (this);
+ instance = GCHandle.ToIntPtr (handle);
+ readFunc = OnRead;
+ writeFunc = OnWrite;
+ controlFunc = Control;
+ readFuncPtr = Marshal.GetFunctionPointerForDelegate (readFunc);
+ writeFuncPtr = Marshal.GetFunctionPointerForDelegate (writeFunc);
+ controlFuncPtr = Marshal.GetFunctionPointerForDelegate (controlFunc);
+ mono_btls_bio_mono_initialize (Handle.DangerousGetHandle (), instance, readFuncPtr, writeFuncPtr, controlFuncPtr);
+ }
+
+ public static MonoBtlsBioMono CreateStream (Stream stream, bool ownsStream)
+ {
+ return new MonoBtlsBioMono (new StreamBackend (stream, ownsStream));
+ }
+
+ public static MonoBtlsBioMono CreateString (StringWriter writer)
+ {
+ return new MonoBtlsBioMono (new StringBackend (writer));
+ }
+
+ enum ControlCommand
+ {
+ Flush = 1
+ }
+
+ delegate int BioReadFunc (IntPtr bio, IntPtr data, int dataLength, out int wantMore);
+ delegate int BioWriteFunc (IntPtr bio, IntPtr data, int dataLength);
+ delegate long BioControlFunc (IntPtr bio, ControlCommand command, long arg);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_bio_mono_new ();
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_bio_mono_initialize (IntPtr handle, IntPtr instance, IntPtr readFunc, IntPtr writeFunc, IntPtr controlFunc);
+
+ long Control (ControlCommand command, long arg)
+ {
+ switch (command) {
+ case ControlCommand.Flush:
+ backend.Flush ();
+ return 1;
+
+ default:
+ throw new NotImplementedException ();
+ }
+ }
+
+ int OnRead (IntPtr data, int dataLength, out int wantMore)
+ {
+ bool wantMoreBool;
+ var buffer = new byte[dataLength];
+ var ret = backend.Read (buffer, 0, dataLength, out wantMoreBool);
+ wantMore = wantMoreBool ? 1 : 0;
+ if (ret <= 0)
+ return ret;
+ Marshal.Copy (buffer, 0, data, ret);
+ return ret;
+ }
+
+#if MONOTOUCH
+ [MonoPInvokeCallback (typeof (BioReadFunc))]
+#endif
+ static int OnRead (IntPtr instance, IntPtr data, int dataLength, out int wantMore)
+ {
+ var c = (MonoBtlsBioMono)GCHandle.FromIntPtr (instance).Target;
+ try {
+ return c.OnRead (data, dataLength, out wantMore);
+ } catch (Exception ex) {
+ c.SetException (ex);
+ wantMore = 0;
+ return -1;
+ }
+ }
+
+ int OnWrite (IntPtr data, int dataLength)
+ {
+ var buffer = new byte[dataLength];
+ Marshal.Copy (data, buffer, 0, dataLength);
+ var ok = backend.Write (buffer, 0, dataLength);
+ return ok ? dataLength : -1;
+ }
+
+#if MONOTOUCH
+ [MonoPInvokeCallback (typeof (BioWriteFunc))]
+#endif
+ static int OnWrite (IntPtr instance, IntPtr data, int dataLength)
+ {
+ var c = (MonoBtlsBioMono)GCHandle.FromIntPtr (instance).Target;
+ try {
+ return c.OnWrite (data, dataLength);
+ } catch (Exception ex) {
+ c.SetException (ex);
+ return -1;
+ }
+ }
+
+#if MONOTOUCH
+ [MonoPInvokeCallback (typeof (BioControlFunc))]
+#endif
+ static long Control (IntPtr instance, ControlCommand command, long arg)
+ {
+ var c = (MonoBtlsBioMono)GCHandle.FromIntPtr (instance).Target;
+ try {
+ return c.Control (command, arg);
+ } catch (Exception ex) {
+ c.SetException (ex);
+ return -1;
+ }
+ }
+
+ protected override void Close ()
+ {
+ try {
+ if (backend != null) {
+ backend.Close ();
+ backend = null;
+ }
+ if (handle.IsAllocated)
+ handle.Free ();
+ } finally {
+ base.Close ();
+ }
+ }
+
+ class StreamBackend : IMonoBtlsBioMono
+ {
+ Stream stream;
+ bool ownsStream;
+
+ public Stream InnerStream {
+ get { return stream; }
+ }
+
+ public StreamBackend (Stream stream, bool ownsStream)
+ {
+ this.stream = stream;
+ this.ownsStream = ownsStream;
+ }
+
+ public int Read (byte[] buffer, int offset, int size, out bool wantMore)
+ {
+ wantMore = false;
+ return stream.Read (buffer, offset, size);
+ }
+
+ public bool Write (byte[] buffer, int offset, int size)
+ {
+ stream.Write (buffer, offset, size);
+ return true;
+ }
+
+ public void Flush ()
+ {
+ stream.Flush ();
+ }
+
+ public void Close ()
+ {
+ if (ownsStream && stream != null)
+ stream.Dispose ();
+ stream = null;
+ }
+ }
+
+ class StringBackend : IMonoBtlsBioMono
+ {
+ StringWriter writer;
+ Encoding encoding = new UTF8Encoding ();
+
+ public StringBackend (StringWriter writer)
+ {
+ this.writer = writer;
+ }
+
+ public int Read (byte[] buffer, int offset, int size, out bool wantMore)
+ {
+ wantMore = false;
+ return -1;
+ }
+
+ public bool Write (byte[] buffer, int offset, int size)
+ {
+ var text = encoding.GetString (buffer, offset, size);
+ writer.Write (text);
+ return true;
+ }
+
+ public void Flush ()
+ {
+ }
+
+ public void Close ()
+ {
+ }
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsContext.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+#if MONO_SECURITY_ALIAS
+extern alias MonoSecurity;
+#endif
+
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Security.Cryptography.X509Certificates;
+using System.Security.Authentication;
+using System.Runtime.InteropServices;
+
+#if MONO_SECURITY_ALIAS
+using MonoSecurity::Mono.Security.Interface;
+#else
+using Mono.Security.Interface;
+#endif
+
+using MNS = Mono.Net.Security;
+
+namespace Mono.Btls
+{
+ class MonoBtlsContext : MNS.MobileTlsContext, IMonoBtlsBioMono
+ {
+ X509Certificate remoteCertificate;
+ X509Certificate clientCertificate;
+ X509CertificateImplBtls nativeServerCertificate;
+ X509CertificateImplBtls nativeClientCertificate;
+ MonoBtlsSslCtx ctx;
+ MonoBtlsSsl ssl;
+ MonoBtlsBio bio;
+ MonoBtlsBio errbio;
+
+ MonoTlsConnectionInfo connectionInfo;
+ bool certificateValidated;
+ bool isAuthenticated;
+ bool connected;
+
+ public MonoBtlsContext (
+ MNS.MobileAuthenticatedStream parent,
+ bool serverMode, string targetHost,
+ SslProtocols enabledProtocols, X509Certificate serverCertificate,
+ X509CertificateCollection clientCertificates, bool askForClientCert)
+ : base (parent, serverMode, targetHost, enabledProtocols,
+ serverCertificate, clientCertificates, askForClientCert)
+ {
+ if (serverMode)
+ nativeServerCertificate = GetPrivateCertificate (serverCertificate);
+ }
+
+ static X509CertificateImplBtls GetPrivateCertificate (X509Certificate certificate)
+ {
+ var impl = certificate.Impl as X509CertificateImplBtls;
+ if (impl != null)
+ return (X509CertificateImplBtls)impl.Clone ();
+
+ var password = Guid.NewGuid ().ToString ();
+ var buffer = certificate.Export (X509ContentType.Pfx, password);
+
+ impl = new X509CertificateImplBtls ();
+ impl.Import (buffer, password, X509KeyStorageFlags.DefaultKeySet);
+ return impl;
+ }
+
+ new public MonoBtlsProvider Provider {
+ get { return (MonoBtlsProvider)base.Provider; }
+ }
+
+ int VerifyCallback (MonoBtlsX509StoreCtx storeCtx)
+ {
+ using (var chainImpl = new X509ChainImplBtls (storeCtx))
+ using (var managedChain = new X509Chain (chainImpl)) {
+ var leaf = managedChain.ChainElements[0].Certificate;
+ var result = ValidateCertificate (leaf, managedChain);
+ certificateValidated = true;
+ return result ? 1 : 0;
+ }
+ }
+
+ int SelectCallback ()
+ {
+ Debug ("SELECT CALLBACK!");
+
+ GetPeerCertificate ();
+ if (remoteCertificate == null)
+ throw new TlsException (AlertDescription.InternalError, "Cannot request client certificate before receiving one from the server.");
+
+ var clientCert = SelectClientCertificate (remoteCertificate, null);
+ Debug ("SELECT CALLBACK #1: {0}", clientCert);
+ if (clientCert == null)
+ return 1;
+
+ nativeClientCertificate = GetPrivateCertificate (clientCert);
+ Debug ("SELECT CALLBACK #2: {0}", nativeClientCertificate);
+ clientCertificate = new X509Certificate (nativeClientCertificate);
+ SetPrivateCertificate (nativeClientCertificate);
+ return 1;
+ }
+
+ public override void StartHandshake ()
+ {
+ InitializeConnection ();
+
+ ssl = new MonoBtlsSsl (ctx);
+
+ bio = new MonoBtlsBioMono (this);
+ ssl.SetBio (bio);
+
+ if (IsServer) {
+ SetPrivateCertificate (nativeServerCertificate);
+ } else {
+ ssl.SetServerName (TargetHost);
+ }
+ }
+
+ void SetPrivateCertificate (X509CertificateImplBtls privateCert)
+ {
+ Debug ("SetPrivateCertificate: {0}", privateCert);
+ ssl.SetCertificate (privateCert.X509);
+ ssl.SetPrivateKey (privateCert.NativePrivateKey);
+ var intermediate = privateCert.IntermediateCertificates;
+ if (intermediate == null)
+ return;
+ for (int i = 0; i < intermediate.Count; i++) {
+ var impl = (X509CertificateImplBtls)intermediate [i];
+ Debug ("SetPrivateCertificate - add intermediate: {0}", impl);
+ ssl.AddIntermediateCertificate (impl.X509);
+ }
+ }
+
+ Exception GetException (MonoBtlsSslError status)
+ {
+ var error = MonoBtlsError.GetError ();
+ if (error == null)
+ return new MonoBtlsException (status);
+
+ var text = MonoBtlsError.GetErrorString (error);
+ return new MonoBtlsException ("{0} {1}", status, text);
+ }
+
+ public override bool ProcessHandshake ()
+ {
+ var done = false;
+ while (!done) {
+ Debug ("ProcessHandshake");
+ MonoBtlsError.ClearError ();
+ var status = DoProcessHandshake ();
+ Debug ("ProcessHandshake #1: {0}", status);
+
+ switch (status) {
+ case MonoBtlsSslError.None:
+ if (connected)
+ done = true;
+ else
+ connected = true;
+ break;
+ case MonoBtlsSslError.WantRead:
+ case MonoBtlsSslError.WantWrite:
+ return false;
+ default:
+ throw GetException (status);
+ }
+ }
+
+ ssl.PrintErrors ();
+
+ return true;
+ }
+
+ MonoBtlsSslError DoProcessHandshake ()
+ {
+ if (connected)
+ return ssl.Handshake ();
+ else if (IsServer)
+ return ssl.Accept ();
+ else
+ return ssl.Connect ();
+ }
+
+ public override void FinishHandshake ()
+ {
+ InitializeSession ();
+
+ isAuthenticated = true;
+ }
+
+ void SetupCertificateStore ()
+ {
+#if MONODROID
+ ctx.CertificateStore.SetDefaultPaths ();
+ ctx.CertificateStore.AddAndroidLookup ();
+#else
+ var userPath = MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.UserTrustedRoots);
+ if (Directory.Exists (userPath))
+ ctx.CertificateStore.AddDirectoryLookup (userPath, MonoBtlsX509FileType.PEM);
+ var machinePath = MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.MachineTrustedRoots);
+ if (Directory.Exists (machinePath))
+ ctx.CertificateStore.AddDirectoryLookup (machinePath, MonoBtlsX509FileType.PEM);
+#endif
+
+ if (Settings != null && Settings.TrustAnchors != null) {
+ var trust = IsServer ? MonoBtlsX509TrustKind.TRUST_CLIENT : MonoBtlsX509TrustKind.TRUST_SERVER;
+ ctx.CertificateStore.AddCollection (Settings.TrustAnchors, trust);
+ }
+ }
+
+ void InitializeConnection ()
+ {
+ ctx = new MonoBtlsSslCtx ();
+
+#if MARTIN_DEBUG
+ errbio = MonoBtlsBio.CreateMonoStream (Console.OpenStandardError ());
+ ctx.SetDebugBio (errbio);
+#endif
+
+ SetupCertificateStore ();
+
+ if (!IsServer || AskForClientCertificate)
+ ctx.SetVerifyCallback (VerifyCallback, false);
+ if (!IsServer)
+ ctx.SetSelectCallback (SelectCallback);
+
+ var host = TargetHost;
+ if (!string.IsNullOrEmpty (host)) {
+ var pos = TargetHost.IndexOf (':');
+ if (pos > 0)
+ host = host.Substring (0, pos);
+ }
+
+ ctx.SetVerifyParam (MonoBtlsProvider.GetVerifyParam (host, IsServer));
+
+ TlsProtocolCode minProtocol, maxProtocol;
+ GetProtocolVersions (out minProtocol, out maxProtocol);
+
+ ctx.SetMinVersion ((int)minProtocol);
+ ctx.SetMaxVersion ((int)maxProtocol);
+
+ if (Settings != null && Settings.EnabledCiphers != null) {
+ var ciphers = new short [Settings.EnabledCiphers.Length];
+ for (int i = 0; i < ciphers.Length; i++)
+ ciphers [i] = (short)Settings.EnabledCiphers [i];
+ ctx.SetCiphers (ciphers, true);
+ }
+ }
+
+ void GetPeerCertificate ()
+ {
+ if (remoteCertificate != null)
+ return;
+ using (var remoteCert = ssl.GetPeerCertificate ()) {
+ if (remoteCert != null)
+ remoteCertificate = MonoBtlsProvider.CreateCertificate (remoteCert);
+ }
+ }
+
+ void InitializeSession ()
+ {
+ GetPeerCertificate ();
+
+ if (IsServer && AskForClientCertificate && !certificateValidated) {
+ if (!ValidateCertificate (null, null))
+ throw new TlsException (AlertDescription.CertificateUnknown);
+ }
+
+ var cipher = (CipherSuiteCode)ssl.GetCipher ();
+ var protocol = (TlsProtocolCode)ssl.GetVersion ();
+ Debug ("GET CONNECTION INFO: {0:x}:{0} {1:x}:{1} {2}", cipher, protocol, (TlsProtocolCode)protocol);
+
+ connectionInfo = new MonoTlsConnectionInfo {
+ CipherSuiteCode = cipher,
+ ProtocolVersion = GetProtocol (protocol)
+ };
+ }
+
+ static TlsProtocols GetProtocol (TlsProtocolCode protocol)
+ {
+ switch (protocol) {
+ case TlsProtocolCode.Tls10:
+ return TlsProtocols.Tls10;
+ case TlsProtocolCode.Tls11:
+ return TlsProtocols.Tls11;
+ case TlsProtocolCode.Tls12:
+ return TlsProtocols.Tls12;
+ default:
+ throw new NotSupportedException ();
+ }
+ }
+
+ public override void Flush ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ public override int Read (byte[] buffer, int offset, int size, out bool wantMore)
+ {
+ Debug ("Read: {0} {1} {2}", buffer.Length, offset, size);
+
+ var data = Marshal.AllocHGlobal (size);
+ if (data == IntPtr.Zero)
+ throw new OutOfMemoryException ();
+
+ try {
+ MonoBtlsError.ClearError ();
+ var status = ssl.Read (data, ref size);
+ Debug ("Read done: {0} {1}", status, size);
+
+ if (status == MonoBtlsSslError.WantRead) {
+ wantMore = true;
+ return 0;
+ } else if (status != MonoBtlsSslError.None) {
+ throw GetException (status);
+ }
+
+ if (size > 0)
+ Marshal.Copy (data, buffer, offset, size);
+
+ wantMore = false;
+ return size;
+ } finally {
+ Marshal.FreeHGlobal (data);
+ }
+ }
+
+ public override int Write (byte[] buffer, int offset, int size, out bool wantMore)
+ {
+ Debug ("Write: {0} {1} {2}", buffer.Length, offset, size);
+
+ var data = Marshal.AllocHGlobal (size);
+ if (data == IntPtr.Zero)
+ throw new OutOfMemoryException ();
+
+ try {
+ MonoBtlsError.ClearError ();
+ Marshal.Copy (buffer, offset, data, size);
+ var status = ssl.Write (data, ref size);
+ Debug ("Write done: {0} {1}", status, size);
+
+ if (status == MonoBtlsSslError.WantWrite) {
+ wantMore = true;
+ return 0;
+ } else if (status != MonoBtlsSslError.None) {
+ throw GetException (status);
+ }
+
+ wantMore = false;
+ return size;
+ } finally {
+ Marshal.FreeHGlobal (data);
+ }
+ }
+
+ public override void Close ()
+ {
+ Debug ("Close!");
+ ssl.Dispose ();
+ }
+
+ void Dispose<T> (ref T disposable)
+ where T : class, IDisposable
+ {
+ try {
+ if (disposable != null)
+ disposable.Dispose ();
+ } catch {
+ ;
+ } finally {
+ disposable = null;
+ }
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ try {
+ if (disposing) {
+ Dispose (ref remoteCertificate);
+ Dispose (ref nativeServerCertificate);
+ Dispose (ref nativeClientCertificate);
+ Dispose (ref clientCertificate);
+ Dispose (ref ctx);
+ Dispose (ref ssl);
+ Dispose (ref bio);
+ Dispose (ref errbio);
+ }
+ } finally {
+ base.Dispose (disposing);
+ }
+ }
+
+ int IMonoBtlsBioMono.Read (byte[] buffer, int offset, int size, out bool wantMore)
+ {
+ Debug ("InternalRead: {0} {1}", offset, size);
+ var ret = Parent.InternalRead (buffer, offset, size, out wantMore);
+ Debug ("InternalReadDone: {0} {1}", ret, wantMore);
+ return ret;
+ }
+
+ bool IMonoBtlsBioMono.Write (byte[] buffer, int offset, int size)
+ {
+ Debug ("InternalWrite: {0} {1}", offset, size);
+ var ret = Parent.InternalWrite (buffer, offset, size);
+ Debug ("InternalWrite done: {0}", ret);
+ return ret;
+ }
+
+ void IMonoBtlsBioMono.Flush ()
+ {
+ ;
+ }
+
+ void IMonoBtlsBioMono.Close ()
+ {
+ ;
+ }
+
+ public override bool HasContext {
+ get { return ssl != null && ssl.IsValid; }
+ }
+ public override bool IsAuthenticated {
+ get { return isAuthenticated; }
+ }
+ public override MonoTlsConnectionInfo ConnectionInfo {
+ get { return connectionInfo; }
+ }
+ internal override bool IsRemoteCertificateAvailable {
+ get { return remoteCertificate != null; }
+ }
+ internal override X509Certificate LocalClientCertificate {
+ get { return clientCertificate; }
+ }
+ public override X509Certificate RemoteCertificate {
+ get { return remoteCertificate; }
+ }
+ public override TlsProtocols NegotiatedProtocol {
+ get { return connectionInfo.ProtocolVersion; }
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsError.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// 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.
+// #if SECURITY_DEP
+using System;
+using System.IO;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+#if MONOTOUCH
+using MonoTouch;
+#endif
+
+namespace Mono.Btls
+{
+ static class MonoBtlsError
+ {
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_error_peek_error ();
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_error_get_error ();
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_error_clear_error ();
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_error_get_error_string_n (int error, IntPtr buf, int len);
+
+ public static int PeekError ()
+ {
+ return mono_btls_error_peek_error ();
+ }
+
+ public static int GetError ()
+ {
+ return mono_btls_error_get_error ();
+ }
+
+ public static void ClearError ()
+ {
+ mono_btls_error_clear_error ();
+ }
+
+ public static string GetErrorString (int error)
+ {
+ var size = 1024;
+ var buffer = Marshal.AllocHGlobal (size);
+ if (buffer == IntPtr.Zero)
+ throw new OutOfMemoryException ();
+ try {
+ mono_btls_error_get_error_string_n (error, buffer, size);
+ return Marshal.PtrToStringAnsi (buffer);
+ } finally {
+ Marshal.FreeHGlobal (buffer);
+ }
+ }
+ }
+}
+// #endif
--- /dev/null
+//
+// MonoBtlsException.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+
+namespace Mono.Btls
+{
+ class MonoBtlsException : Exception
+ {
+ public MonoBtlsException ()
+ {
+ }
+
+ public MonoBtlsException (MonoBtlsSslError error)
+ : base (error.ToString ())
+ {
+ }
+
+ public MonoBtlsException (string message)
+ : base (message)
+ {
+ }
+
+ public MonoBtlsException (string format, params object[] args)
+ : base (string.Format (format, args))
+ {
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsKey.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+namespace Mono.Btls
+{
+ class MonoBtlsKey : MonoBtlsObject
+ {
+ internal class BoringKeyHandle : MonoBtlsHandle
+ {
+ internal BoringKeyHandle (IntPtr handle)
+ : base (handle, true)
+ {
+ }
+
+ protected override bool ReleaseHandle ()
+ {
+ mono_btls_key_free (handle);
+ return true;
+ }
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_key_free (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_key_up_ref (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_key_get_bytes (IntPtr handle, out IntPtr data, out int size, int include_private_bits);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_key_get_bits (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_key_is_rsa (IntPtr handle);
+
+ new internal BoringKeyHandle Handle {
+ get { return (BoringKeyHandle)base.Handle; }
+ }
+
+ internal MonoBtlsKey (BoringKeyHandle handle)
+ : base (handle)
+ {
+ }
+
+ public byte[] GetBytes (bool include_private_bits)
+ {
+ int size;
+ IntPtr data;
+
+ var ret = mono_btls_key_get_bytes (Handle.DangerousGetHandle (), out data, out size, include_private_bits ? 1 : 0);
+ CheckError (ret);
+
+ var buffer = new byte [size];
+ Marshal.Copy (data, buffer, 0, size);
+ FreeDataPtr (data);
+ return buffer;
+ }
+
+ public MonoBtlsKey Copy ()
+ {
+ CheckThrow ();
+ var copy = mono_btls_key_up_ref (Handle.DangerousGetHandle ());
+ CheckError (copy != IntPtr.Zero);
+ return new MonoBtlsKey (new BoringKeyHandle (copy));
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsObject.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.Threading;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+namespace Mono.Btls
+{
+ abstract class MonoBtlsObject : IDisposable
+ {
+ internal MonoBtlsObject (MonoBtlsHandle handle)
+ {
+ this.handle = handle;
+ }
+
+ protected internal abstract class MonoBtlsHandle : SafeHandle
+ {
+ internal MonoBtlsHandle ()
+ : base (IntPtr.Zero, true)
+ {
+ }
+
+ internal MonoBtlsHandle (IntPtr handle, bool ownsHandle)
+ : base (handle, ownsHandle)
+ {
+ }
+
+ public override bool IsInvalid {
+ get { return handle == IntPtr.Zero; }
+ }
+ }
+
+ internal MonoBtlsHandle Handle {
+ get {
+ CheckThrow ();
+ return handle;
+ }
+ }
+
+ public bool IsValid {
+ get { return handle != null && !handle.IsInvalid; }
+ }
+
+ MonoBtlsHandle handle;
+ Exception lastError;
+
+ protected void CheckThrow ()
+ {
+ if (lastError != null)
+ throw lastError;
+ if (handle == null || handle.IsInvalid)
+ throw new ObjectDisposedException ("MonoBtlsSsl");
+ }
+
+ protected Exception SetException (Exception ex)
+ {
+ if (lastError == null)
+ lastError = ex;
+ return ex;
+ }
+
+ protected void CheckError (bool ok, [CallerMemberName] string callerName = null)
+ {
+ if (!ok) {
+ if (callerName != null)
+ throw new MonoBtlsException ("{0}.{1} failed.", GetType ().Name, callerName);
+ else
+ throw new MonoBtlsException ();
+ }
+
+ }
+
+ protected void CheckError (int ret, [CallerMemberName] string callerName = null)
+ {
+ CheckError (ret == 1, callerName);
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_free (IntPtr data);
+
+ protected void FreeDataPtr (IntPtr data)
+ {
+ mono_btls_free (data);
+ }
+
+ protected virtual void Close ()
+ {
+ }
+
+ protected void Dispose (bool disposing)
+ {
+ if (disposing) {
+ try {
+ if (handle != null) {
+ Close ();
+ handle.Dispose ();
+ handle = null;
+ }
+ } finally {
+ var disposedExc = new ObjectDisposedException (GetType ().Name);
+ Interlocked.CompareExchange (ref lastError, disposedExc, null);
+ }
+ }
+ }
+
+ public void Dispose ()
+ {
+ Dispose (true);
+ GC.SuppressFinalize (this);
+ }
+
+ ~MonoBtlsObject ()
+ {
+ Dispose (false);
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsPkcs12.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Security.Cryptography.X509Certificates;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Mono.Btls
+{
+ class MonoBtlsPkcs12 : MonoBtlsObject
+ {
+ internal class BoringPkcs12Handle : MonoBtlsHandle
+ {
+ public BoringPkcs12Handle (IntPtr handle)
+ : base (handle, true)
+ {
+ }
+
+ protected override bool ReleaseHandle ()
+ {
+ mono_btls_pkcs12_free (handle);
+ return true;
+ }
+ }
+
+ new internal BoringPkcs12Handle Handle {
+ get { return (BoringPkcs12Handle)base.Handle; }
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_pkcs12_free (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_pkcs12_new ();
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_pkcs12_get_count (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_pkcs12_get_cert (IntPtr Handle, int index);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_pkcs12_add_cert (IntPtr chain, IntPtr x509);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern unsafe static int mono_btls_pkcs12_import (IntPtr chain, void* data, int len, IntPtr password);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_pkcs12_has_private_key (IntPtr pkcs12);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_pkcs12_get_private_key (IntPtr pkcs12);
+
+ internal MonoBtlsPkcs12 ()
+ : base (new BoringPkcs12Handle (mono_btls_pkcs12_new ()))
+ {
+ }
+
+ internal MonoBtlsPkcs12 (BoringPkcs12Handle handle)
+ : base (handle)
+ {
+ }
+
+ MonoBtlsKey privateKey;
+
+ public int Count {
+ get { return mono_btls_pkcs12_get_count (Handle.DangerousGetHandle ()); }
+ }
+
+ public MonoBtlsX509 GetCertificate (int index)
+ {
+ if (index >= Count)
+ throw new IndexOutOfRangeException ();
+ var handle = mono_btls_pkcs12_get_cert (Handle.DangerousGetHandle (), index);
+ CheckError (handle != IntPtr.Zero);
+ return new MonoBtlsX509 (new MonoBtlsX509.BoringX509Handle (handle));
+ }
+
+ public void AddCertificate (MonoBtlsX509 x509)
+ {
+ mono_btls_pkcs12_add_cert (
+ Handle.DangerousGetHandle (),
+ x509.Handle.DangerousGetHandle ());
+ }
+
+ public unsafe void Import (byte[] buffer, string password)
+ {
+ var passptr = IntPtr.Zero;
+ fixed (void* ptr = buffer)
+ try {
+ passptr = Marshal.StringToHGlobalAnsi (password ?? string.Empty);
+ var ret = mono_btls_pkcs12_import (
+ Handle.DangerousGetHandle (), ptr,
+ buffer.Length, passptr);
+ CheckError (ret);
+ } finally {
+ if (passptr != IntPtr.Zero)
+ Marshal.FreeHGlobal (passptr);
+ }
+ }
+
+ public bool HasPrivateKey {
+ get { return mono_btls_pkcs12_has_private_key (Handle.DangerousGetHandle ()) != 0; }
+ }
+
+ public MonoBtlsKey GetPrivateKey ()
+ {
+ if (!HasPrivateKey)
+ throw new InvalidOperationException ();
+ if (privateKey == null) {
+ var handle = mono_btls_pkcs12_get_private_key (Handle.DangerousGetHandle ());
+ CheckError (handle != IntPtr.Zero);
+ privateKey = new MonoBtlsKey (new MonoBtlsKey.BoringKeyHandle (handle));
+ }
+ return privateKey;
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsProvider.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+#if MONO_SECURITY_ALIAS
+extern alias MonoSecurity;
+#endif
+
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Runtime.CompilerServices;
+using System.Security.Cryptography.X509Certificates;
+using System.Security.Authentication;
+
+#if MONO_SECURITY_ALIAS
+using MonoSecurity::Mono.Security.Interface;
+using MX = MonoSecurity::Mono.Security.X509;
+#else
+using Mono.Security.Interface;
+using MX = Mono.Security.X509;
+#endif
+
+using MNS = Mono.Net.Security;
+
+namespace Mono.Btls
+{
+ class MonoBtlsProvider : MonoTlsProvider
+ {
+ static readonly Guid id = new Guid ("432d18c9-9348-4b90-bfbf-9f2a10e1f15b");
+
+ public override Guid ID {
+ get { return id; }
+ }
+ public override string Name {
+ get { return "btls"; }
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ public extern static bool IsSupported ();
+
+ internal MonoBtlsProvider ()
+ {
+ if (!IsSupported ())
+ throw new NotSupportedException ("BTLS is not supported in this runtime.");
+ }
+
+ public override bool SupportsSslStream {
+ get { return true; }
+ }
+
+ public override bool SupportsMonoExtensions {
+ get { return true; }
+ }
+
+ public override bool SupportsConnectionInfo {
+ get { return true; }
+ }
+
+ public override SslProtocols SupportedProtocols {
+ get { return SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls; }
+ }
+
+ public override IMonoSslStream CreateSslStream (
+ Stream innerStream, bool leaveInnerStreamOpen,
+ MonoTlsSettings settings = null)
+ {
+ return new MonoBtlsStream (
+ innerStream, leaveInnerStreamOpen, settings, this);
+ }
+
+ internal override bool HasNativeCertificates {
+ get { return true; }
+ }
+
+ internal override X509Certificate2Impl GetNativeCertificate (
+ byte[] data, string password, X509KeyStorageFlags flags)
+ {
+ var impl = new X509CertificateImplBtls (true);
+ impl.Import (data, password, flags);
+ return impl;
+ }
+
+ internal override X509Certificate2Impl GetNativeCertificate (
+ X509Certificate certificate)
+ {
+ var impl = certificate.Impl as X509CertificateImplBtls;
+ if (impl != null)
+ return (X509Certificate2Impl)impl.Clone ();
+
+ var data = certificate.GetRawCertData ();
+ return new X509CertificateImplBtls (data, MonoBtlsX509Format.DER, false);
+ }
+
+ internal static MonoBtlsX509VerifyParam GetVerifyParam (string targetHost, bool serverMode)
+ {
+ MonoBtlsX509VerifyParam param;
+ if (serverMode)
+ param = MonoBtlsX509VerifyParam.GetSslClient ();
+ else
+ param = MonoBtlsX509VerifyParam.GetSslServer ();
+
+ if (string.IsNullOrEmpty (targetHost))
+ return param;
+
+ try {
+ var copy = param.Copy ();
+ copy.SetHost (targetHost);
+ return copy;
+ } finally {
+ param.Dispose ();
+ }
+ }
+
+ internal override bool ValidateCertificate (
+ ICertificateValidator2 validator, string targetHost, bool serverMode,
+ X509CertificateCollection certificates, bool wantsChain, ref X509Chain chain,
+ ref MonoSslPolicyErrors errors, ref int status11)
+ {
+ if (chain != null) {
+ var chainImpl = (X509ChainImplBtls)chain.Impl;
+ var success = chainImpl.StoreCtx.VerifyResult == 1;
+ CheckValidationResult (
+ validator, targetHost, serverMode, certificates,
+ wantsChain, chain, chainImpl.StoreCtx,
+ success, ref errors, ref status11);
+ return success;
+ }
+
+ using (var store = new MonoBtlsX509Store ())
+ using (var nativeChain = MonoBtlsProvider.GetNativeChain (certificates))
+ using (var param = GetVerifyParam (targetHost, serverMode))
+ using (var storeCtx = new MonoBtlsX509StoreCtx ()) {
+ store.LoadLocations (null, GetSystemStoreLocation ());
+ store.SetDefaultPaths ();
+
+ storeCtx.Initialize (store, nativeChain);
+
+ storeCtx.SetVerifyParam (param);
+
+ var ret = storeCtx.Verify ();
+
+ var success = ret == 1;
+
+ if (wantsChain && chain == null) {
+ chain = GetManagedChain (nativeChain);
+ }
+
+ CheckValidationResult (
+ validator, targetHost, serverMode, certificates,
+ wantsChain, null, storeCtx,
+ success, ref errors, ref status11);
+ return success;
+ }
+ }
+
+ void CheckValidationResult (
+ ICertificateValidator validator, string targetHost, bool serverMode,
+ X509CertificateCollection certificates, bool wantsChain,
+ X509Chain chain, MonoBtlsX509StoreCtx storeCtx,
+ bool success, ref MonoSslPolicyErrors errors, ref int status11)
+ {
+ if (!success) {
+ errors = MonoSslPolicyErrors.RemoteCertificateChainErrors;
+ status11 = unchecked((int)0x800B010B);
+ }
+ }
+
+ public static string GetSystemStoreLocation ()
+ {
+#if ANDROID
+ return "/system/etc/security/cacerts";
+#else
+ var appData = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
+ var path = Path.Combine (appData, ".mono", "certs", "NewTrust");
+ return path;
+#endif
+ }
+
+ public static X509Certificate CreateCertificate (byte[] data, MonoBtlsX509Format format, bool disallowFallback = false)
+ {
+ using (var impl = new X509CertificateImplBtls (data, format, disallowFallback)) {
+ return new X509Certificate (impl);
+ }
+ }
+
+ public static X509Certificate2 CreateCertificate2 (byte[] data, MonoBtlsX509Format format, bool disallowFallback = false)
+ {
+ using (var impl = new X509CertificateImplBtls (data, format, disallowFallback)) {
+ return new X509Certificate2 (impl);
+ }
+ }
+
+ public static X509Certificate2 CreateCertificate2 (byte[] data, string password, bool disallowFallback = false)
+ {
+ using (var impl = new X509CertificateImplBtls (disallowFallback)) {
+ impl.Import (data, password, X509KeyStorageFlags.DefaultKeySet);
+ return new X509Certificate2 (impl);
+ }
+ }
+
+ public static X509Certificate CreateCertificate (MonoBtlsX509 x509)
+ {
+ using (var impl = new X509CertificateImplBtls (x509, true))
+ return new X509Certificate (impl);
+ }
+
+ public static X509Chain CreateChain ()
+ {
+ using (var impl = new X509ChainImplBtls ())
+ return new X509Chain (impl);
+ }
+
+ public static X509Chain GetManagedChain (MonoBtlsX509Chain chain)
+ {
+ var impl = new X509ChainImplBtls (chain);
+ return new X509Chain (impl);
+ }
+
+ public static MonoBtlsX509 GetBtlsCertificate (X509Certificate certificate)
+ {
+ var impl = certificate.Impl as X509CertificateImplBtls;
+ if (impl != null)
+ return impl.X509.Copy ();
+
+ return MonoBtlsX509.LoadFromData (certificate.GetRawCertData (), MonoBtlsX509Format.DER);
+ }
+
+ public static MonoBtlsX509Chain GetNativeChain (X509CertificateCollection certificates)
+ {
+ var chain = new MonoBtlsX509Chain ();
+ try {
+ foreach (var cert in certificates) {
+ using (var x509 = GetBtlsCertificate (cert))
+ chain.AddCertificate (x509);
+ }
+ return chain;
+ } catch {
+ chain.Dispose ();
+ throw;
+ }
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsSsl.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+#if MONOTOUCH
+using MonoTouch;
+#endif
+
+namespace Mono.Btls
+{
+ delegate int MonoBtlsVerifyCallback (MonoBtlsX509StoreCtx ctx);
+ delegate int MonoBtlsSelectCallback ();
+
+ class MonoBtlsSsl : MonoBtlsObject
+ {
+ internal class BoringSslHandle : MonoBtlsHandle
+ {
+ public BoringSslHandle (IntPtr handle)
+ : base (handle, true)
+ {
+ }
+
+ protected override bool ReleaseHandle ()
+ {
+ mono_btls_ssl_destroy (handle);
+ return true;
+ }
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_ssl_destroy (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_ssl_new (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_use_certificate (IntPtr handle, IntPtr x509);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_use_private_key (IntPtr handle, IntPtr key);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_add_chain_certificate (IntPtr handle, IntPtr x509);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_accept (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_connect (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_handshake (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_ssl_close (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_ssl_set_bio (IntPtr handle, IntPtr bio);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_read (IntPtr handle, IntPtr data, int len);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_write (IntPtr handle, IntPtr data, int len);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_get_error (IntPtr handle, int ret_code);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_get_version (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_ssl_set_min_version (IntPtr handle, int version);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_ssl_set_max_version (IntPtr handle, int version);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_get_cipher (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_get_ciphers (IntPtr handle, out IntPtr data);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_ssl_get_peer_certificate (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_set_cipher_list (IntPtr handle, IntPtr str);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_ssl_print_errors_cb (IntPtr func, IntPtr ctx);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_set_verify_param (IntPtr handle, IntPtr param);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_set_server_name (IntPtr handle, IntPtr name);
+
+ static BoringSslHandle Create_internal (MonoBtlsSslCtx ctx)
+ {
+ var handle = mono_btls_ssl_new (ctx.Handle.DangerousGetHandle ());
+ if (handle == IntPtr.Zero)
+ throw new MonoBtlsException ();
+ return new BoringSslHandle (handle);
+ }
+
+ PrintErrorsCallbackFunc printErrorsFunc;
+ IntPtr printErrorsFuncPtr;
+
+ public MonoBtlsSsl (MonoBtlsSslCtx ctx)
+ : base (Create_internal (ctx))
+ {
+ printErrorsFunc = PrintErrorsCallback;
+ printErrorsFuncPtr = Marshal.GetFunctionPointerForDelegate (printErrorsFunc);
+ }
+
+ new internal BoringSslHandle Handle {
+ get { return (BoringSslHandle)base.Handle; }
+ }
+
+ public void SetBio (MonoBtlsBio bio)
+ {
+ CheckThrow ();
+ mono_btls_ssl_set_bio (
+ Handle.DangerousGetHandle (),
+ bio.Handle.DangerousGetHandle ());
+ }
+
+ Exception ThrowError ([CallerMemberName] string callerName = null)
+ {
+ string errors;
+ try {
+ if (callerName == null)
+ callerName = GetType ().Name;
+ errors = GetErrors ();
+ } catch {
+ errors = null;
+ }
+
+ if (errors != null) {
+ Console.Error.WriteLine ("ERROR: {0} failed: {1}", callerName, errors);
+ throw new MonoBtlsException ("{0} failed: {1}.", callerName, errors);
+ } else {
+ Console.Error.WriteLine ("ERROR: {0} failed.", callerName);
+ throw new MonoBtlsException ("{0} failed.", callerName);
+ }
+ }
+
+ MonoBtlsSslError GetError (int ret_code)
+ {
+ CheckThrow ();
+ var error = mono_btls_ssl_get_error (
+ Handle.DangerousGetHandle (), ret_code);
+ return (MonoBtlsSslError)error;
+ }
+
+ public void SetCertificate (MonoBtlsX509 x509)
+ {
+ CheckThrow ();
+
+ var ret = mono_btls_ssl_use_certificate (
+ Handle.DangerousGetHandle (),
+ x509.Handle.DangerousGetHandle ());
+ if (ret <= 0)
+ throw ThrowError ();
+ }
+
+ public void SetPrivateKey (MonoBtlsKey key)
+ {
+ CheckThrow ();
+
+ var ret = mono_btls_ssl_use_private_key (
+ Handle.DangerousGetHandle (),
+ key.Handle.DangerousGetHandle ());
+ if (ret <= 0)
+ throw ThrowError ();
+ }
+
+ public void AddIntermediateCertificate (MonoBtlsX509 x509)
+ {
+ CheckThrow ();
+
+ var ret = mono_btls_ssl_add_chain_certificate (
+ Handle.DangerousGetHandle (),
+ x509.Handle.DangerousGetHandle ());
+ if (ret <= 0)
+ throw ThrowError ();
+ }
+
+ public MonoBtlsSslError Accept ()
+ {
+ CheckThrow ();
+
+ var ret = mono_btls_ssl_accept (Handle.DangerousGetHandle ());
+
+ var error = GetError (ret);
+ return error;
+ }
+
+ public MonoBtlsSslError Connect ()
+ {
+ CheckThrow ();
+
+ var ret = mono_btls_ssl_connect (Handle.DangerousGetHandle ());
+
+ var error = GetError (ret);
+ return error;
+ }
+
+ public MonoBtlsSslError Handshake ()
+ {
+ CheckThrow ();
+
+ var ret = mono_btls_ssl_handshake (Handle.DangerousGetHandle ());
+
+ var error = GetError (ret);
+ return error;
+ }
+
+ delegate int PrintErrorsCallbackFunc (IntPtr str, IntPtr len, IntPtr ctx);
+
+#if MONOTOUCH
+ [MonoPInvokeCallback (typeof (PrintErrorsCallbackFunc))]
+#endif
+ static int PrintErrorsCallback (IntPtr str, IntPtr len, IntPtr ctx)
+ {
+ var sb = (StringBuilder)GCHandle.FromIntPtr (ctx).Target;
+ try {
+ var text = Marshal.PtrToStringAnsi (str, (int)len);
+ sb.Append (text);
+ return 1;
+ } catch {
+ return 0;
+ }
+ }
+
+ public string GetErrors ()
+ {
+ var text = new StringBuilder ();
+ var handle = GCHandle.Alloc (text);
+
+ try {
+ mono_btls_ssl_print_errors_cb (printErrorsFuncPtr, GCHandle.ToIntPtr (handle));
+ return text.ToString ();
+ } finally {
+ if (handle.IsAllocated)
+ handle.Free ();
+ }
+ }
+
+ public void PrintErrors ()
+ {
+ var errors = GetErrors ();
+ if (string.IsNullOrEmpty (errors))
+ return;
+ Console.Error.WriteLine (errors);
+ }
+
+ public MonoBtlsSslError Read (IntPtr data, ref int dataSize)
+ {
+ CheckThrow ();
+ var ret = mono_btls_ssl_read (
+ Handle.DangerousGetHandle (), data, dataSize);
+
+ if (ret >= 0) {
+ dataSize = ret;
+ return MonoBtlsSslError.None;
+ }
+
+ var error = mono_btls_ssl_get_error (
+ Handle.DangerousGetHandle (), ret);
+ dataSize = 0;
+ return (MonoBtlsSslError)error;
+ }
+
+ public MonoBtlsSslError Write (IntPtr data, ref int dataSize)
+ {
+ CheckThrow ();
+ var ret = mono_btls_ssl_write (
+ Handle.DangerousGetHandle (), data, dataSize);
+
+ if (ret >= 0) {
+ dataSize = ret;
+ return MonoBtlsSslError.None;
+ }
+
+ var error = mono_btls_ssl_get_error (
+ Handle.DangerousGetHandle (), ret);
+ dataSize = 0;
+ return (MonoBtlsSslError)error;
+ }
+
+ public int GetVersion ()
+ {
+ CheckThrow ();
+ return mono_btls_ssl_get_version (Handle.DangerousGetHandle ());
+ }
+
+ public void SetMinVersion (int version)
+ {
+ CheckThrow ();
+ mono_btls_ssl_set_min_version (Handle.DangerousGetHandle (), version);
+ }
+
+ public void SetMaxVersion (int version)
+ {
+ CheckThrow ();
+ mono_btls_ssl_set_max_version (Handle.DangerousGetHandle (), version);
+ }
+
+ public int GetCipher ()
+ {
+ CheckThrow ();
+ var cipher = mono_btls_ssl_get_cipher (Handle.DangerousGetHandle ());
+ CheckError (cipher > 0);
+ return cipher;
+ }
+
+ public short[] GetCiphers ()
+ {
+ CheckThrow ();
+ IntPtr data;
+ var count = mono_btls_ssl_get_ciphers (
+ Handle.DangerousGetHandle (), out data);
+ CheckError (count > 0);
+ try {
+ short[] ciphers = new short[count];
+ Marshal.Copy (data, ciphers, 0, count);
+ return ciphers;
+ } finally {
+ FreeDataPtr (data);
+ }
+ }
+
+ public void SetCipherList (string str)
+ {
+ CheckThrow ();
+ IntPtr strPtr = IntPtr.Zero;
+ try {
+ strPtr = Marshal.StringToHGlobalAnsi (str);
+ var ret = mono_btls_ssl_set_cipher_list (
+ Handle.DangerousGetHandle (), strPtr);
+ CheckError (ret);
+ } finally {
+ if (strPtr != IntPtr.Zero)
+ Marshal.FreeHGlobal (strPtr);
+ }
+ }
+
+ public MonoBtlsX509 GetPeerCertificate ()
+ {
+ CheckThrow ();
+ var x509 = mono_btls_ssl_get_peer_certificate (
+ Handle.DangerousGetHandle ());
+ if (x509 == IntPtr.Zero)
+ return null;
+ return new MonoBtlsX509 (new MonoBtlsX509.BoringX509Handle (x509));
+ }
+
+ public void SetVerifyParam (MonoBtlsX509VerifyParam param)
+ {
+ CheckThrow ();
+ var ret = mono_btls_ssl_set_verify_param (
+ Handle.DangerousGetHandle (),
+ param.Handle.DangerousGetHandle ());
+ CheckError (ret);
+ }
+
+ public void SetServerName (string name)
+ {
+ CheckThrow ();
+ IntPtr namePtr = IntPtr.Zero;
+ try {
+ namePtr = Marshal.StringToHGlobalAnsi (name);
+ var ret = mono_btls_ssl_set_server_name (
+ Handle.DangerousGetHandle (), namePtr);
+ CheckError (ret);
+ } finally {
+ if (namePtr != IntPtr.Zero)
+ Marshal.FreeHGlobal (namePtr);
+ }
+ }
+
+ protected override void Close ()
+ {
+ mono_btls_ssl_close (Handle.DangerousGetHandle ());
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsSslCtx.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+#if MONOTOUCH
+using MonoTouch;
+#endif
+
+namespace Mono.Btls
+{
+ class MonoBtlsSslCtx : MonoBtlsObject
+ {
+ internal class BoringSslCtxHandle : MonoBtlsHandle
+ {
+ public BoringSslCtxHandle (IntPtr handle)
+ : base (handle, true)
+ {
+ }
+
+ protected override bool ReleaseHandle ()
+ {
+ mono_btls_ssl_ctx_free (handle);
+ return true;
+ }
+ }
+
+ new internal BoringSslCtxHandle Handle {
+ get { return (BoringSslCtxHandle)base.Handle; }
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_ssl_ctx_new ();
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_ctx_free (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_ssl_ctx_up_ref (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_ssl_ctx_initialize (IntPtr handle, IntPtr instance);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_ssl_ctx_set_debug_bio (IntPtr handle, IntPtr bio);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_ssl_ctx_set_cert_verify_callback (IntPtr handle, IntPtr func, int cert_required);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_ssl_ctx_set_cert_select_callback (IntPtr handle, IntPtr func);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_ssl_ctx_set_min_version (IntPtr handle, int version);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_ssl_ctx_set_max_version (IntPtr handle, int version);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_ctx_is_cipher_supported (IntPtr handle, short value);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_ctx_set_ciphers (IntPtr handle, int count, IntPtr data, int allow_unsupported);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_ssl_ctx_set_verify_param (IntPtr handle, IntPtr param);
+
+ delegate int NativeVerifyFunc (IntPtr instance, int preverify_ok, IntPtr ctx);
+ delegate int NativeSelectFunc (IntPtr instance);
+
+ NativeVerifyFunc verifyFunc;
+ NativeSelectFunc selectFunc;
+ IntPtr verifyFuncPtr;
+ IntPtr selectFuncPtr;
+ MonoBtlsVerifyCallback verifyCallback;
+ MonoBtlsSelectCallback selectCallback;
+ MonoBtlsX509Store store;
+ GCHandle instance;
+ IntPtr instancePtr;
+
+ public MonoBtlsSslCtx ()
+ : this (new BoringSslCtxHandle (mono_btls_ssl_ctx_new ()))
+ {
+ }
+
+ internal MonoBtlsSslCtx (BoringSslCtxHandle handle)
+ : base (handle)
+ {
+ instance = GCHandle.Alloc (this);
+ instancePtr = GCHandle.ToIntPtr (instance);
+ mono_btls_ssl_ctx_initialize (
+ handle.DangerousGetHandle (), instancePtr);
+
+ verifyFunc = NativeVerifyCallback;
+ selectFunc = NativeSelectCallback;
+ verifyFuncPtr = Marshal.GetFunctionPointerForDelegate (verifyFunc);
+ selectFuncPtr = Marshal.GetFunctionPointerForDelegate (selectFunc);
+
+ store = new MonoBtlsX509Store (Handle);
+ }
+
+ internal MonoBtlsSslCtx Copy ()
+ {
+ var copy = mono_btls_ssl_ctx_up_ref (Handle.DangerousGetHandle ());
+ return new MonoBtlsSslCtx (new BoringSslCtxHandle (copy));
+ }
+
+ public MonoBtlsX509Store CertificateStore {
+ get { return store; }
+ }
+
+ int VerifyCallback (bool preverify_ok, MonoBtlsX509StoreCtx ctx)
+ {
+ if (verifyCallback != null)
+ return verifyCallback (ctx);
+ return 0;
+ }
+
+#if MONOTOUCH
+ [MonoPInvokeCallback (typeof (NativeVerifyFunc))]
+#endif
+ static int NativeVerifyCallback (IntPtr instance, int preverify_ok, IntPtr store_ctx)
+ {
+ var c = (MonoBtlsSslCtx)GCHandle.FromIntPtr (instance).Target;
+ using (var ctx = new MonoBtlsX509StoreCtx (preverify_ok, store_ctx)) {
+ try {
+ return c.VerifyCallback (preverify_ok != 0, ctx);
+ } catch (Exception ex) {
+ c.SetException (ex);
+ }
+ }
+ return 0;
+ }
+
+ int SelectCallback ()
+ {
+ if (selectCallback != null)
+ return selectCallback ();
+ return 1;
+ }
+
+#if MONOTOUCH
+ [MonoPInvokeCallback (typeof (NativeSelectFunc))]
+#endif
+ static int NativeSelectCallback (IntPtr instance)
+ {
+ var c = (MonoBtlsSslCtx)GCHandle.FromIntPtr (instance).Target;
+ try {
+ return c.SelectCallback ();
+ } catch (Exception ex) {
+ c.SetException (ex);
+ return 0;
+ }
+ }
+
+ public void SetDebugBio (MonoBtlsBio bio)
+ {
+ CheckThrow ();
+ mono_btls_ssl_ctx_set_debug_bio (Handle.DangerousGetHandle (), bio.Handle.DangerousGetHandle ());
+ }
+
+ public void SetVerifyCallback (MonoBtlsVerifyCallback callback, bool client_cert_required)
+ {
+ CheckThrow ();
+
+ verifyCallback = callback;
+ mono_btls_ssl_ctx_set_cert_verify_callback (
+ Handle.DangerousGetHandle (), verifyFuncPtr,
+ client_cert_required ? 1 : 0);
+ }
+
+ public void SetSelectCallback (MonoBtlsSelectCallback callback)
+ {
+ CheckThrow ();
+
+ selectCallback = callback;
+ mono_btls_ssl_ctx_set_cert_select_callback (
+ Handle.DangerousGetHandle (), selectFuncPtr);
+ }
+
+ public void SetMinVersion (int version)
+ {
+ CheckThrow ();
+ mono_btls_ssl_ctx_set_min_version (Handle.DangerousGetHandle (), version);
+ }
+
+ public void SetMaxVersion (int version)
+ {
+ CheckThrow ();
+ mono_btls_ssl_ctx_set_max_version (Handle.DangerousGetHandle (), version);
+ }
+
+ public bool IsCipherSupported (short value)
+ {
+ CheckThrow ();
+ return mono_btls_ssl_ctx_is_cipher_supported (Handle.DangerousGetHandle (), value) != 0;
+ }
+
+ public void SetCiphers (short[] ciphers, bool allow_unsupported)
+ {
+ CheckThrow ();
+ var data = Marshal.AllocHGlobal (ciphers.Length * 2);
+ try {
+ Marshal.Copy (ciphers, 0, data, ciphers.Length);
+ var ret = mono_btls_ssl_ctx_set_ciphers (
+ Handle.DangerousGetHandle (),
+ ciphers.Length, data, allow_unsupported ? 1 : 0);
+ CheckError (ret > 0);
+ } finally {
+ Marshal.FreeHGlobal (data);
+ }
+ }
+
+ public void SetVerifyParam (MonoBtlsX509VerifyParam param)
+ {
+ CheckThrow ();
+ var ret = mono_btls_ssl_ctx_set_verify_param (
+ Handle.DangerousGetHandle (),
+ param.Handle.DangerousGetHandle ());
+ CheckError (ret);
+ }
+
+ protected override void Close ()
+ {
+ if (store != null) {
+ store.Dispose ();
+ store = null;
+ }
+ if (instance.IsAllocated)
+ instance.Free ();
+ base.Close ();
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsSslError.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// 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.
+#if SECURITY_DEP
+namespace Mono.Btls
+{
+ // keep in sync with boringssl/include/ssl.h
+ enum MonoBtlsSslError
+ {
+ None = 0,
+ Ssl = 1,
+ WantRead = 2,
+ WantWrite = 3,
+ WantX509Lookup = 4,
+ Syscall = 5,
+ ZeroReturn = 6,
+ WantConnect = 7,
+ WantAccept = 8,
+ WantChannelIdLookup = 9,
+ PendingSession = 11,
+ PendingCertificate = 12,
+ WantPrivateKeyOperation = 13
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsStream.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+#if MONO_SECURITY_ALIAS
+extern alias MonoSecurity;
+#endif
+
+using System;
+using System.IO;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+
+#if MONO_SECURITY_ALIAS
+using MonoSecurity::Mono.Security.Interface;
+#else
+using Mono.Security.Interface;
+#endif
+
+using MNS = Mono.Net.Security;
+
+namespace Mono.Btls
+{
+ class MonoBtlsStream : MNS.MobileAuthenticatedStream
+ {
+ public MonoBtlsStream (Stream innerStream, bool leaveInnerStreamOpen, MonoTlsSettings settings, MonoTlsProvider provider)
+ : base (innerStream, leaveInnerStreamOpen, settings, provider)
+ {
+ }
+
+ protected override MNS.MobileTlsContext CreateContext (
+ MNS.MobileAuthenticatedStream parent, bool serverMode, string targetHost,
+ SslProtocols enabledProtocols, X509Certificate serverCertificate,
+ X509CertificateCollection clientCertificates, bool askForClientCert)
+ {
+ return new MonoBtlsContext (
+ parent, serverMode, targetHost,
+ enabledProtocols, serverCertificate,
+ clientCertificates, askForClientCert);
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsUtils.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.Text;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Mono.Btls
+{
+ static class MonoBtlsUtils
+ {
+ static byte[] emailOid = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01 };
+
+ public static bool Compare (byte[] a, byte[] b)
+ {
+ if (a.Length != b.Length)
+ return false;
+ for (int i = 0; i < a.Length; i++) {
+ if (a[i] != b[i])
+ return false;
+ }
+ return true;
+ }
+
+ static bool AppendEntry (StringBuilder sb, MonoBtlsX509Name name, int index, string separator, bool quotes)
+ {
+ var type = name.GetEntryType (index);
+ if (type < 0)
+ return false;
+ else if (type == 0) {
+ var oidValue = name.GetEntryOidData (index);
+ if (Compare (oidValue, emailOid))
+ type = MonoBtlsX509NameEntryType.Email;
+ }
+ var text = name.GetEntryValue (index);
+ if (text == null)
+ return false;
+ var oid = name.GetEntryOid (index);
+ if (oid == null)
+ return false;
+
+ if (sb.Length > 0)
+ sb.Append (separator);
+
+ switch (type) {
+ case MonoBtlsX509NameEntryType.CountryName:
+ sb.Append ("C=");
+ break;
+ case MonoBtlsX509NameEntryType.OrganizationName:
+ sb.Append ("O=");
+ break;
+ case MonoBtlsX509NameEntryType.OrganizationalUnitName:
+ sb.Append ("OU=");
+ break;
+ case MonoBtlsX509NameEntryType.CommonName:
+ sb.Append ("CN=");
+ break;
+ case MonoBtlsX509NameEntryType.LocalityName:
+ sb.Append ("L=");
+ break;
+ case MonoBtlsX509NameEntryType.StateOrProvinceName:
+ sb.Append ("S="); // NOTE: RFC2253 uses ST=
+ break;
+ case MonoBtlsX509NameEntryType.StreetAddress:
+ sb.Append ("STREET=");
+ break;
+ case MonoBtlsX509NameEntryType.DomainComponent:
+ sb.Append ("DC=");
+ break;
+ case MonoBtlsX509NameEntryType.UserId:
+ sb.Append ("UID=");
+ break;
+ case MonoBtlsX509NameEntryType.Email:
+ sb.Append ("E="); // NOTE: Not part of RFC2253
+ break;
+ case MonoBtlsX509NameEntryType.DnQualifier:
+ sb.Append ("dnQualifier=");
+ break;
+ case MonoBtlsX509NameEntryType.Title:
+ sb.Append ("T=");
+ break;
+ case MonoBtlsX509NameEntryType.Surname:
+ sb.Append ("SN=");
+ break;
+ case MonoBtlsX509NameEntryType.GivenName:
+ sb.Append ("G=");
+ break;
+ case MonoBtlsX509NameEntryType.Initial:
+ sb.Append ("I=");
+ break;
+ default:
+ // unknown OID
+ sb.Append ("OID."); // NOTE: Not present as RFC2253
+ sb.Append (oid);
+ sb.Append ("=");
+ break;
+ }
+
+ // 16bits or 8bits string ? TODO not complete (+special chars!)
+ char[] specials = { ',', '+', '"', '\\', '<', '>', ';' };
+ if (quotes) {
+ if ((text.IndexOfAny (specials, 0, text.Length) > 0) ||
+ text.StartsWith (" ") || (text.EndsWith (" ")))
+ text = "\"" + text + "\"";
+ }
+
+ sb.Append (text);
+ return true;
+ }
+
+ const X500DistinguishedNameFlags AllFlags = X500DistinguishedNameFlags.Reversed |
+ X500DistinguishedNameFlags.UseSemicolons | X500DistinguishedNameFlags.DoNotUsePlusSign |
+ X500DistinguishedNameFlags.DoNotUseQuotes | X500DistinguishedNameFlags.UseCommas |
+ X500DistinguishedNameFlags.UseNewLines | X500DistinguishedNameFlags.UseUTF8Encoding |
+ X500DistinguishedNameFlags.UseT61Encoding | X500DistinguishedNameFlags.ForceUTF8Encoding;
+
+ static string GetSeparator (X500DistinguishedNameFlags flag)
+ {
+ if ((flag & X500DistinguishedNameFlags.UseSemicolons) != 0)
+ return "; ";
+ if ((flag & X500DistinguishedNameFlags.UseCommas) != 0)
+ return ", ";
+ if ((flag & X500DistinguishedNameFlags.UseNewLines) != 0)
+ return Environment.NewLine;
+ return ", "; //default
+ }
+
+ public static string FormatName (MonoBtlsX509Name name, X500DistinguishedNameFlags flag)
+ {
+ if ((flag != 0) && ((flag & AllFlags) == 0))
+ throw new ArgumentException ("flag");
+
+ if (name.GetEntryCount () == 0)
+ return String.Empty;
+
+ // Mono.Security reversed isn't the same as fx 2.0 (which is the reverse of 1.x)
+ bool reversed = ((flag & X500DistinguishedNameFlags.Reversed) != 0);
+ bool quotes = ((flag & X500DistinguishedNameFlags.DoNotUseQuotes) == 0);
+ string separator = GetSeparator (flag);
+
+ return FormatName (name, reversed, separator, quotes);
+ }
+
+ public static string FormatName (MonoBtlsX509Name name, bool reversed, string separator, bool quotes)
+ {
+ var count = name.GetEntryCount ();
+ StringBuilder sb = new StringBuilder ();
+
+ if (reversed) {
+ for (int i = count - 1; i >= 0; i--) {
+ AppendEntry (sb, name, i, separator, quotes);
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ AppendEntry (sb, name, i, separator, quotes);
+ }
+ }
+
+ return sb.ToString ();
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Text;
+using System.Threading;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography.X509Certificates;
+using System.Security.Cryptography;
+
+namespace Mono.Btls
+{
+ class MonoBtlsX509 : MonoBtlsObject
+ {
+ internal class BoringX509Handle : MonoBtlsHandle
+ {
+ public BoringX509Handle (IntPtr handle)
+ : base (handle, true)
+ {
+ }
+
+ protected override bool ReleaseHandle ()
+ {
+ if (handle != IntPtr.Zero)
+ mono_btls_x509_free (handle);
+ return true;
+ }
+
+ public IntPtr StealHandle ()
+ {
+ var retval = Interlocked.Exchange (ref handle, IntPtr.Zero);
+ return retval;
+ }
+ }
+
+ new internal BoringX509Handle Handle {
+ get { return (BoringX509Handle)base.Handle; }
+ }
+
+ internal MonoBtlsX509 (BoringX509Handle handle)
+ : base (handle)
+ {
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_up_ref (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_from_data (IntPtr data, int len, MonoBtlsX509Format format);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_get_subject_name (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_get_issuer_name (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_get_subject_name_string (IntPtr handle, IntPtr buffer, int size);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_get_issuer_name_string (IntPtr handle, IntPtr buffer, int size);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_get_raw_data (IntPtr handle, IntPtr bio, MonoBtlsX509Format format);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_cmp (IntPtr a, IntPtr b);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_get_hash (IntPtr handle, out IntPtr data);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static long mono_btls_x509_get_not_before (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static long mono_btls_x509_get_not_after (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_get_public_key (IntPtr handle, IntPtr bio);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_get_serial_number (IntPtr handle, IntPtr data, int size, int mono_style);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_get_version (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_get_signature_algorithm (IntPtr handle, IntPtr buffer, int size);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_get_public_key_asn1 (IntPtr handle, IntPtr oid, int oid_size, out IntPtr data, out int size);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_get_public_key_parameters (IntPtr handle, IntPtr oid, int oid_size, out IntPtr data, out int size);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_get_pubkey (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_get_subject_key_identifier (IntPtr handle, out IntPtr data, out int size);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_print (IntPtr handle, IntPtr bio);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_x509_free (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_dup (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_add_trust_object (IntPtr handle, MonoBtlsX509Purpose purpose);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_add_reject_object (IntPtr handle, MonoBtlsX509Purpose purpose);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_add_explicit_trust (IntPtr handle, MonoBtlsX509TrustKind kind);
+
+ internal MonoBtlsX509 Copy ()
+ {
+ var copy = mono_btls_x509_up_ref (Handle.DangerousGetHandle ());
+ CheckError (copy != IntPtr.Zero);
+ return new MonoBtlsX509 (new BoringX509Handle (copy));
+ }
+
+ // This will actually duplicate the underlying 'X509 *' object instead of
+ // simply increasing the reference count.
+ internal MonoBtlsX509 Duplicate ()
+ {
+ var copy = mono_btls_x509_dup (Handle.DangerousGetHandle ());
+ CheckError (copy != IntPtr.Zero);
+ return new MonoBtlsX509 (new BoringX509Handle (copy));
+ }
+
+ public static MonoBtlsX509 LoadFromData (byte[] buffer, MonoBtlsX509Format format)
+ {
+ var data = Marshal.AllocHGlobal (buffer.Length);
+ if (data == IntPtr.Zero)
+ throw new OutOfMemoryException ();
+
+ try {
+ Marshal.Copy (buffer, 0, data, buffer.Length);
+ var x509 = mono_btls_x509_from_data (data, buffer.Length, format);
+ if (x509 == IntPtr.Zero)
+ throw new MonoBtlsException ("Failed to read certificate from data.");
+
+ return new MonoBtlsX509 (new BoringX509Handle (x509));
+ } finally {
+ Marshal.FreeHGlobal (data);
+ }
+ }
+
+ public MonoBtlsX509Name GetSubjectName ()
+ {
+ var handle = mono_btls_x509_get_subject_name (Handle.DangerousGetHandle ());
+ CheckError (handle != IntPtr.Zero);
+ return new MonoBtlsX509Name (new MonoBtlsX509Name.BoringX509NameHandle (handle, false));
+ }
+
+ public string GetSubjectNameString ()
+ {
+ const int size = 4096;
+ var data = Marshal.AllocHGlobal (size);
+ try {
+ var ret = mono_btls_x509_get_subject_name_string (
+ Handle.DangerousGetHandle (), data, size);
+ CheckError (ret);
+ return Marshal.PtrToStringAnsi (data);
+ } finally {
+ Marshal.FreeHGlobal (data);
+ }
+ }
+
+ public long GetSubjectNameHash ()
+ {
+ CheckThrow ();
+ using (var subject = GetSubjectName ())
+ return subject.GetHash ();
+ }
+
+ public MonoBtlsX509Name GetIssuerName ()
+ {
+ var handle = mono_btls_x509_get_issuer_name (Handle.DangerousGetHandle ());
+ CheckError (handle != IntPtr.Zero);
+ return new MonoBtlsX509Name (new MonoBtlsX509Name.BoringX509NameHandle (handle, false));
+ }
+
+ public string GetIssuerNameString ()
+ {
+ const int size = 4096;
+ var data = Marshal.AllocHGlobal (size);
+ try {
+ var ret = mono_btls_x509_get_issuer_name_string (
+ Handle.DangerousGetHandle (), data, size);
+ CheckError (ret);
+ return Marshal.PtrToStringAnsi (data);
+ } finally {
+ Marshal.FreeHGlobal (data);
+ }
+ }
+
+ public byte[] GetRawData (MonoBtlsX509Format format)
+ {
+ using (var bio = new MonoBtlsBioMemory ()) {
+ var ret = mono_btls_x509_get_raw_data (
+ Handle.DangerousGetHandle (),
+ bio.Handle.DangerousGetHandle (),
+ format);
+ CheckError (ret);
+ return bio.GetData ();
+ }
+ }
+
+ public void GetRawData (MonoBtlsBio bio, MonoBtlsX509Format format)
+ {
+ CheckThrow ();
+ var ret = mono_btls_x509_get_raw_data (
+ Handle.DangerousGetHandle (),
+ bio.Handle.DangerousGetHandle (),
+ format);
+ CheckError (ret);
+ }
+
+ public static int Compare (MonoBtlsX509 a, MonoBtlsX509 b)
+ {
+ return mono_btls_x509_cmp (
+ a.Handle.DangerousGetHandle (),
+ b.Handle.DangerousGetHandle ());
+ }
+
+ public byte[] GetCertHash ()
+ {
+ IntPtr data;
+ var ret = mono_btls_x509_get_hash (Handle.DangerousGetHandle (), out data);
+ CheckError (ret > 0);
+ var buffer = new byte [ret];
+ Marshal.Copy (data, buffer, 0, ret);
+ return buffer;
+ }
+
+ public DateTime GetNotBefore ()
+ {
+ var ticks = mono_btls_x509_get_not_before (Handle.DangerousGetHandle ());
+ return new DateTime (1970, 1, 1).AddSeconds (ticks);
+ }
+
+ public DateTime GetNotAfter ()
+ {
+ var ticks = mono_btls_x509_get_not_after (Handle.DangerousGetHandle ());
+ return new DateTime (1970, 1, 1).AddSeconds (ticks);
+ }
+
+ public byte[] GetPublicKeyData ()
+ {
+ using (var bio = new MonoBtlsBioMemory ()) {
+ var ret = mono_btls_x509_get_public_key (
+ Handle.DangerousGetHandle (),
+ bio.Handle.DangerousGetHandle ());
+ CheckError (ret > 0);
+ return bio.GetData ();
+ }
+ }
+
+ public byte[] GetSerialNumber (bool mono_style)
+ {
+ int size = 256;
+ IntPtr data = Marshal.AllocHGlobal (size);
+ try {
+ var ret = mono_btls_x509_get_serial_number (
+ Handle.DangerousGetHandle (), data,
+ size, mono_style ? 1 : 0);
+ CheckError (ret > 0);
+ var buffer = new byte [ret];
+ Marshal.Copy (data, buffer, 0, ret);
+ return buffer;
+ } finally {
+ if (data != IntPtr.Zero)
+ Marshal.FreeHGlobal (data);
+ }
+ }
+
+ public int GetVersion ()
+ {
+ return mono_btls_x509_get_version (Handle.DangerousGetHandle ());
+ }
+
+ public Oid GetSignatureAlgorithm ()
+ {
+ int size = 256;
+ IntPtr data = Marshal.AllocHGlobal (size);
+ try {
+ var ret = mono_btls_x509_get_signature_algorithm (
+ Handle.DangerousGetHandle (), data, size);
+ CheckError (ret > 0);
+ return new Oid (Marshal.PtrToStringAnsi (data));
+ } finally {
+ Marshal.FreeHGlobal (data);
+ }
+ }
+
+ public AsnEncodedData GetPublicKeyAsn1 ()
+ {
+ int size;
+ IntPtr data;
+
+ int oidSize = 256;
+ var oidData = Marshal.AllocHGlobal (256);
+ string oid;
+
+ try {
+ var ret = mono_btls_x509_get_public_key_asn1 (
+ Handle.DangerousGetHandle (), oidData, oidSize,
+ out data, out size);
+ CheckError (ret);
+ oid = Marshal.PtrToStringAnsi (oidData);
+ } finally {
+ Marshal.FreeHGlobal (oidData);
+ }
+
+ try {
+ var buffer = new byte[size];
+ Marshal.Copy (data, buffer, 0, size);
+ return new AsnEncodedData (oid.ToString (), buffer);
+ } finally {
+ if (data != IntPtr.Zero)
+ FreeDataPtr (data);
+ }
+ }
+
+ public AsnEncodedData GetPublicKeyParameters ()
+ {
+ int size;
+ IntPtr data;
+
+ int oidSize = 256;
+ var oidData = Marshal.AllocHGlobal (256);
+ string oid;
+
+ try {
+ var ret = mono_btls_x509_get_public_key_parameters (
+ Handle.DangerousGetHandle (), oidData, oidSize,
+ out data, out size);
+ CheckError (ret);
+ oid = Marshal.PtrToStringAnsi (oidData);
+ } finally {
+ Marshal.FreeHGlobal (oidData);
+ }
+
+ try {
+ var buffer = new byte[size];
+ Marshal.Copy (data, buffer, 0, size);
+ return new AsnEncodedData (oid.ToString (), buffer);
+ } finally {
+ if (data != IntPtr.Zero)
+ FreeDataPtr (data);
+ }
+ }
+
+ public byte[] GetSubjectKeyIdentifier ()
+ {
+ int size;
+ IntPtr data = IntPtr.Zero;
+
+ try {
+ var ret = mono_btls_x509_get_subject_key_identifier (
+ Handle.DangerousGetHandle (), out data, out size);
+ CheckError (ret);
+ var buffer = new byte[size];
+ Marshal.Copy (data, buffer, 0, size);
+ return buffer;
+ } finally {
+ if (data != IntPtr.Zero)
+ FreeDataPtr (data);
+ }
+ }
+
+ public MonoBtlsKey GetPublicKey ()
+ {
+ var handle = mono_btls_x509_get_pubkey (Handle.DangerousGetHandle ());
+ CheckError (handle != IntPtr.Zero);
+ return new MonoBtlsKey (new MonoBtlsKey.BoringKeyHandle (handle));
+ }
+
+ public void Print (MonoBtlsBio bio)
+ {
+ var ret = mono_btls_x509_print (
+ Handle.DangerousGetHandle (),
+ bio.Handle.DangerousGetHandle ());
+ CheckError (ret);
+ }
+
+ public void ExportAsPEM (MonoBtlsBio bio, bool includeHumanReadableForm)
+ {
+ GetRawData (bio, MonoBtlsX509Format.PEM);
+
+ if (!includeHumanReadableForm)
+ return;
+
+ Print (bio);
+
+ var hash = GetCertHash ();
+ var output = new StringBuilder ();
+ output.Append ("SHA1 Fingerprint=");
+ for (int i = 0; i < hash.Length; i++) {
+ if (i > 0)
+ output.Append (":");
+ output.AppendFormat ("{0:X2}", hash [i]);
+ }
+ output.AppendLine ();
+ var outputData = Encoding.ASCII.GetBytes (output.ToString ());
+ bio.Write (outputData, 0, outputData.Length);
+ }
+
+ public void AddTrustObject (MonoBtlsX509Purpose purpose)
+ {
+ CheckThrow ();
+ var ret = mono_btls_x509_add_trust_object (
+ Handle.DangerousGetHandle (), purpose);
+ CheckError (ret);
+ }
+
+ public void AddRejectObject (MonoBtlsX509Purpose purpose)
+ {
+ CheckThrow ();
+ var ret = mono_btls_x509_add_reject_object (
+ Handle.DangerousGetHandle (), purpose);
+ CheckError (ret);
+ }
+
+ public void AddExplicitTrust (MonoBtlsX509TrustKind kind)
+ {
+ CheckThrow ();
+ var ret = mono_btls_x509_add_explicit_trust (
+ Handle.DangerousGetHandle (), kind);
+ CheckError (ret);
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509Chain.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Security.Cryptography.X509Certificates;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Mono.Btls
+{
+ class MonoBtlsX509Chain : MonoBtlsObject
+ {
+ internal class BoringX509ChainHandle : MonoBtlsHandle
+ {
+ public BoringX509ChainHandle (IntPtr handle)
+ : base (handle, true)
+ {
+ }
+
+ protected override bool ReleaseHandle ()
+ {
+ mono_btls_x509_chain_free (handle);
+ return true;
+ }
+ }
+
+ new internal BoringX509ChainHandle Handle {
+ get { return (BoringX509ChainHandle)base.Handle; }
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_chain_new ();
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_chain_get_count (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_chain_get_cert (IntPtr Handle, int index);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_chain_add_cert (IntPtr chain, IntPtr x509);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_chain_up_ref (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_x509_chain_free (IntPtr handle);
+
+ public MonoBtlsX509Chain ()
+ : base (new BoringX509ChainHandle (mono_btls_x509_chain_new ()))
+ {
+ }
+
+ internal MonoBtlsX509Chain (BoringX509ChainHandle handle)
+ : base (handle)
+ {
+ }
+
+ public int Count {
+ get { return mono_btls_x509_chain_get_count (Handle.DangerousGetHandle ()); }
+ }
+
+ public MonoBtlsX509 GetCertificate (int index)
+ {
+ if (index >= Count)
+ throw new IndexOutOfRangeException ();
+ var handle = mono_btls_x509_chain_get_cert (
+ Handle.DangerousGetHandle (), index);
+ CheckError (handle != IntPtr.Zero);
+ return new MonoBtlsX509 (new MonoBtlsX509.BoringX509Handle (handle));
+ }
+
+ public void Dump ()
+ {
+ Console.Error.WriteLine ("CHAIN: {0:x} {1}", Handle, Count);
+ for (int i = 0; i < Count; i++) {
+ using (var cert = GetCertificate (i)) {
+ Console.Error.WriteLine (" CERT #{0}: {1}", i, cert.GetSubjectNameString ());
+ }
+ }
+ }
+
+ public void AddCertificate (MonoBtlsX509 x509)
+ {
+ mono_btls_x509_chain_add_cert (
+ Handle.DangerousGetHandle (),
+ x509.Handle.DangerousGetHandle ());
+ }
+
+ internal MonoBtlsX509Chain Copy ()
+ {
+ var copy = mono_btls_x509_chain_up_ref (Handle.DangerousGetHandle ());
+ CheckError (copy != IntPtr.Zero);
+ return new MonoBtlsX509Chain (new BoringX509ChainHandle (copy));
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509Crl.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Text;
+using System.Threading;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography.X509Certificates;
+using System.Security.Cryptography;
+
+namespace Mono.Btls
+{
+ class MonoBtlsX509Crl : MonoBtlsObject
+ {
+ internal class BoringX509CrlHandle : MonoBtlsHandle
+ {
+ public BoringX509CrlHandle (IntPtr handle)
+ : base (handle, true)
+ {
+ }
+
+ protected override bool ReleaseHandle ()
+ {
+ if (handle != IntPtr.Zero)
+ mono_btls_x509_crl_free (handle);
+ return true;
+ }
+
+ public IntPtr StealHandle ()
+ {
+ var retval = Interlocked.Exchange (ref handle, IntPtr.Zero);
+ return retval;
+ }
+ }
+
+ new internal BoringX509CrlHandle Handle {
+ get { return (BoringX509CrlHandle)base.Handle; }
+ }
+
+ internal MonoBtlsX509Crl (BoringX509CrlHandle handle)
+ : base (handle)
+ {
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_crl_ref (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_crl_from_data (IntPtr data, int len, MonoBtlsX509Format format);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_crl_get_by_cert (IntPtr handle, IntPtr x509);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ unsafe extern static IntPtr mono_btls_x509_crl_get_by_serial (IntPtr handle, void *serial, int len);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_crl_get_revoked_count (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_crl_get_revoked (IntPtr handle, int index);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static long mono_btls_x509_crl_get_last_update (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static long mono_btls_x509_crl_get_next_update (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static long mono_btls_x509_crl_get_version (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_crl_get_issuer (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_x509_crl_free (IntPtr handle);
+
+ public static MonoBtlsX509Crl LoadFromData (byte[] buffer, MonoBtlsX509Format format)
+ {
+ var data = Marshal.AllocHGlobal (buffer.Length);
+ if (data == IntPtr.Zero)
+ throw new OutOfMemoryException ();
+
+ try {
+ Marshal.Copy (buffer, 0, data, buffer.Length);
+ var crl = mono_btls_x509_crl_from_data (data, buffer.Length, format);
+ if (crl == IntPtr.Zero)
+ throw new MonoBtlsException ("Failed to read CRL from data.");
+
+ return new MonoBtlsX509Crl (new BoringX509CrlHandle (crl));
+ } finally {
+ Marshal.FreeHGlobal (data);
+ }
+ }
+
+ public MonoBtlsX509Revoked GetByCert (MonoBtlsX509 x509)
+ {
+ var revoked = mono_btls_x509_crl_get_by_cert (
+ Handle.DangerousGetHandle (),
+ x509.Handle.DangerousGetHandle ());
+ if (revoked == IntPtr.Zero)
+ return null;
+ return new MonoBtlsX509Revoked (new MonoBtlsX509Revoked.BoringX509RevokedHandle (revoked));
+ }
+
+ public unsafe MonoBtlsX509Revoked GetBySerial (byte[] serial)
+ {
+ fixed (void *ptr = serial)
+ {
+ var revoked = mono_btls_x509_crl_get_by_serial (
+ Handle.DangerousGetHandle (), ptr, serial.Length);
+ if (revoked == IntPtr.Zero)
+ return null;
+ return new MonoBtlsX509Revoked (new MonoBtlsX509Revoked.BoringX509RevokedHandle (revoked));
+ }
+ }
+
+ public int GetRevokedCount ()
+ {
+ return mono_btls_x509_crl_get_revoked_count (Handle.DangerousGetHandle ());
+ }
+
+ public MonoBtlsX509Revoked GetRevoked (int index)
+ {
+ if (index >= GetRevokedCount ())
+ throw new ArgumentOutOfRangeException ();
+
+ var revoked = mono_btls_x509_crl_get_revoked (
+ Handle.DangerousGetHandle (), index);
+ if (revoked == IntPtr.Zero)
+ return null;
+ return new MonoBtlsX509Revoked (new MonoBtlsX509Revoked.BoringX509RevokedHandle (revoked));
+ }
+
+ public DateTime GetLastUpdate ()
+ {
+ var ticks = mono_btls_x509_crl_get_last_update (Handle.DangerousGetHandle ());
+ return new DateTime (1970, 1, 1).AddSeconds (ticks);
+ }
+
+ public DateTime GetNextUpdate ()
+ {
+ var ticks = mono_btls_x509_crl_get_next_update (Handle.DangerousGetHandle ());
+ return new DateTime (1970, 1, 1).AddSeconds (ticks);
+ }
+
+ public long GetVersion ()
+ {
+ return mono_btls_x509_crl_get_version (Handle.DangerousGetHandle ());
+ }
+
+ public MonoBtlsX509Name GetIssuerName ()
+ {
+ var handle = mono_btls_x509_crl_get_issuer (Handle.DangerousGetHandle ());
+ CheckError (handle != IntPtr.Zero);
+ return new MonoBtlsX509Name (new MonoBtlsX509Name.BoringX509NameHandle (handle, false));
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509Error.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+namespace Mono.Btls
+{
+ // Keep in sync with x509_vfy.h
+ enum MonoBtlsX509Error
+ {
+ OK = 0,
+ /* illegal error (for uninitialized values, to avoid X509_V_OK): 1 */
+
+ UNABLE_TO_GET_ISSUER_CERT = 2,
+ UNABLE_TO_GET_CRL = 3,
+ UNABLE_TO_DECRYPT_CERT_SIGNATURE = 4,
+ UNABLE_TO_DECRYPT_CRL_SIGNATURE = 5,
+ UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY = 6,
+ CERT_SIGNATURE_FAILURE = 7,
+ CRL_SIGNATURE_FAILURE = 8,
+ CERT_NOT_YET_VALID = 9,
+ CERT_HAS_EXPIRED = 10,
+ CRL_NOT_YET_VALID = 11,
+ CRL_HAS_EXPIRED = 12,
+ ERROR_IN_CERT_NOT_BEFORE_FIELD = 13,
+ ERROR_IN_CERT_NOT_AFTER_FIELD = 14,
+ ERROR_IN_CRL_LAST_UPDATE_FIELD = 15,
+ ERROR_IN_CRL_NEXT_UPDATE_FIELD = 16,
+ OUT_OF_MEM = 17,
+ DEPTH_ZERO_SELF_SIGNED_CERT = 18,
+ SELF_SIGNED_CERT_IN_CHAIN = 19,
+ UNABLE_TO_GET_ISSUER_CERT_LOCALLY = 20,
+ UNABLE_TO_VERIFY_LEAF_SIGNATURE = 21,
+ CERT_CHAIN_TOO_LONG = 22,
+ CERT_REVOKED = 23,
+ INVALID_CA = 24,
+ PATH_LENGTH_EXCEEDED = 25,
+ INVALID_PURPOSE = 26,
+ CERT_UNTRUSTED = 27,
+ CERT_REJECTED = 28,
+ /* These are 'informational' when looking for issuer cert */
+ SUBJECT_ISSUER_MISMATCH = 29,
+ AKID_SKID_MISMATCH = 30,
+ AKID_ISSUER_SERIAL_MISMATCH = 31,
+ KEYUSAGE_NO_CERTSIGN = 32,
+
+ UNABLE_TO_GET_CRL_ISSUER = 33,
+ UNHANDLED_CRITICAL_EXTENSION = 34,
+ KEYUSAGE_NO_CRL_SIGN = 35,
+ UNHANDLED_CRITICAL_CRL_EXTENSION = 36,
+ INVALID_NON_CA = 37,
+ PROXY_PATH_LENGTH_EXCEEDED = 38,
+ KEYUSAGE_NO_DIGITAL_SIGNATURE = 39,
+ PROXY_CERTIFICATES_NOT_ALLOWED = 40,
+
+ INVALID_EXTENSION = 41,
+ INVALID_POLICY_EXTENSION = 42,
+ NO_EXPLICIT_POLICY = 43,
+ DIFFERENT_CRL_SCOPE = 44,
+ UNSUPPORTED_EXTENSION_FEATURE = 45,
+
+ UNNESTED_RESOURCE = 46,
+
+ PERMITTED_VIOLATION = 47,
+ EXCLUDED_VIOLATION = 48,
+ SUBTREE_MINMAX = 49,
+ UNSUPPORTED_CONSTRAINT_TYPE = 51,
+ UNSUPPORTED_CONSTRAINT_SYNTAX = 52,
+ UNSUPPORTED_NAME_SYNTAX = 53,
+ CRL_PATH_VALIDATION_ERROR = 54,
+
+ /* Suite B mode algorithm violation */
+ SUITE_B_INVALID_VERSION = 56,
+ SUITE_B_INVALID_ALGORITHM = 57,
+ SUITE_B_INVALID_CURVE = 58,
+ SUITE_B_INVALID_SIGNATURE_ALGORITHM = 59,
+ SUITE_B_LOS_NOT_ALLOWED = 60,
+ SUITE_B_CANNOT_SIGN_P_384_WITH_P_256 = 61,
+
+ /* Host, email and IP check errors */
+ HOSTNAME_MISMATCH = 62,
+ EMAIL_MISMATCH = 63,
+ IP_ADDRESS_MISMATCH = 64,
+
+ /* The application is not happy */
+ APPLICATION_VERIFICATION = 50
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509Exception.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+
+namespace Mono.Btls
+{
+ class MonoBtlsX509Exception : Exception
+ {
+ public MonoBtlsX509Error ErrorCode {
+ get;
+ private set;
+ }
+
+ public string ErrorMessage {
+ get;
+ private set;
+ }
+
+ public MonoBtlsX509Exception (MonoBtlsX509Error code, string message)
+ : base (message)
+ {
+ ErrorCode = code;
+ ErrorMessage = message;
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("[MonoBtlsX509Exception: ErrorCode={0}, ErrorMessage={1}]", ErrorCode, ErrorMessage);
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509FileType.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+namespace Mono.Btls
+{
+ enum MonoBtlsX509FileType
+ {
+ PEM = 1,
+ ASN1 = 2,
+ DEFAULT = 3
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509Format.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+namespace Mono.Btls
+{
+ enum MonoBtlsX509Format
+ {
+ DER = 1,
+ PEM = 2
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509Lookup.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using System.Collections.Generic;
+
+namespace Mono.Btls
+{
+ class MonoBtlsX509Lookup : MonoBtlsObject
+ {
+ internal class BoringX509LookupHandle : MonoBtlsHandle
+ {
+ public BoringX509LookupHandle (IntPtr handle)
+ : base (handle, true)
+ {
+ }
+
+ protected override bool ReleaseHandle ()
+ {
+ mono_btls_x509_lookup_free (handle);
+ return true;
+ }
+ }
+
+ new internal BoringX509LookupHandle Handle {
+ get { return (BoringX509LookupHandle)base.Handle; }
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_lookup_new (IntPtr store, MonoBtlsX509LookupType type);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_lookup_load_file (IntPtr handle, IntPtr file, MonoBtlsX509FileType type);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_lookup_add_dir (IntPtr handle, IntPtr dir, MonoBtlsX509FileType type);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_lookup_add_mono (IntPtr handle, IntPtr monoLookup);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_x509_lookup_method_mono_init (
+ IntPtr handle, IntPtr instance, IntPtr by_subject_func);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_lookup_init (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_lookup_shutdown (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_lookup_by_subject (IntPtr handle, IntPtr name);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_lookup_by_fingerprint (IntPtr handle, IntPtr bytes, int len);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_x509_lookup_free (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_lookup_peek_lookup (IntPtr handle);
+
+ MonoBtlsX509LookupType type;
+ List<MonoBtlsX509LookupMono> monoLookups;
+
+#if FIXME
+ // Do we need this?
+ internal MonoBtlsX509Lookup (BoringX509LookupHandle handle)
+ : base (handle)
+ {
+ }
+#endif
+
+ static BoringX509LookupHandle Create_internal (MonoBtlsX509Store store, MonoBtlsX509LookupType type)
+ {
+ var handle = mono_btls_x509_lookup_new (
+ store.Handle.DangerousGetHandle (), type);
+ if (handle == IntPtr.Zero)
+ throw new MonoBtlsException ();
+ return new BoringX509LookupHandle (handle);
+ }
+
+ internal MonoBtlsX509Lookup (MonoBtlsX509Store store, MonoBtlsX509LookupType type)
+ : base (Create_internal (store, type))
+ {
+ this.type = type;
+ }
+
+ internal IntPtr GetNativeLookup ()
+ {
+ return mono_btls_x509_lookup_peek_lookup (Handle.DangerousGetHandle ());
+ }
+
+ public void LoadFile (string file, MonoBtlsX509FileType type)
+ {
+ IntPtr filePtr = IntPtr.Zero;
+ try {
+ if (file != null)
+ filePtr = Marshal.StringToHGlobalAnsi (file);
+ var ret = mono_btls_x509_lookup_load_file (
+ Handle.DangerousGetHandle (), filePtr, type);
+ CheckError (ret);
+ } finally {
+ if (filePtr != IntPtr.Zero)
+ Marshal.FreeHGlobal (filePtr);
+ }
+ }
+
+ public void AddDirectory (string dir, MonoBtlsX509FileType type)
+ {
+ IntPtr dirPtr = IntPtr.Zero;
+ try {
+ if (dir != null)
+ dirPtr = Marshal.StringToHGlobalAnsi (dir);
+ var ret = mono_btls_x509_lookup_add_dir (
+ Handle.DangerousGetHandle (), dirPtr, type);
+ CheckError (ret);
+ } finally {
+ if (dirPtr != IntPtr.Zero)
+ Marshal.FreeHGlobal (dirPtr);
+ }
+ }
+
+ // Takes ownership of the 'monoLookup'.
+ internal void AddMono (MonoBtlsX509LookupMono monoLookup)
+ {
+ if (type != MonoBtlsX509LookupType.MONO)
+ throw new NotSupportedException ();
+ var ret = mono_btls_x509_lookup_add_mono (
+ Handle.DangerousGetHandle (), monoLookup.Handle.DangerousGetHandle ());
+ CheckError (ret);
+
+ if (monoLookups == null)
+ monoLookups = new List<MonoBtlsX509LookupMono> ();
+ monoLookups.Add (monoLookup);
+ }
+
+ public void Initialize ()
+ {
+ var ret = mono_btls_x509_lookup_init (Handle.DangerousGetHandle ());
+ CheckError (ret);
+ }
+
+ public void Shutdown ()
+ {
+ var ret = mono_btls_x509_lookup_shutdown (Handle.DangerousGetHandle ());
+ CheckError (ret);
+ }
+
+ public MonoBtlsX509 LookupBySubject (MonoBtlsX509Name name)
+ {
+ var handle = mono_btls_x509_lookup_by_subject (
+ Handle.DangerousGetHandle (),
+ name.Handle.DangerousGetHandle ());
+ if (handle == IntPtr.Zero)
+ return null;
+ return new MonoBtlsX509 (new MonoBtlsX509.BoringX509Handle (handle));
+ }
+
+ public MonoBtlsX509 LookupByFingerPrint (byte[] fingerprint)
+ {
+ var bytes = Marshal.AllocHGlobal (fingerprint.Length);
+ try {
+ Marshal.Copy (fingerprint, 0, bytes, fingerprint.Length);
+ var handle = mono_btls_x509_lookup_by_fingerprint (
+ Handle.DangerousGetHandle (),
+ bytes, fingerprint.Length);
+ if (handle == IntPtr.Zero)
+ return null;
+ return new MonoBtlsX509 (new MonoBtlsX509.BoringX509Handle (handle));
+ } finally {
+ if (bytes != IntPtr.Zero)
+ Marshal.FreeHGlobal (bytes);
+ }
+ }
+
+ protected override void Close ()
+ {
+ try {
+ if (monoLookups != null) {
+ foreach (var monoLookup in monoLookups)
+ monoLookup.Dispose ();
+ monoLookups = null;
+ }
+ } finally {
+ base.Close ();
+ }
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509LookupAndroid.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP && MONODROID
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Mono.Btls
+{
+ internal class MonoBtlsX509LookupAndroid : MonoBtlsX509LookupMono
+ {
+ protected override MonoBtlsX509 OnGetBySubject (MonoBtlsX509Name name)
+ {
+ return AndroidPlatform.CertStoreLookup (name);
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509LookupMono.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+namespace Mono.Btls
+{
+ abstract class MonoBtlsX509LookupMono : MonoBtlsObject
+ {
+ internal class BoringX509LookupMonoHandle : MonoBtlsHandle
+ {
+ public BoringX509LookupMonoHandle (IntPtr handle)
+ : base (handle, true)
+ {
+ }
+
+ protected override bool ReleaseHandle ()
+ {
+ mono_btls_x509_lookup_mono_free (handle);
+ return true;
+ }
+ }
+
+ new internal BoringX509LookupMonoHandle Handle {
+ get { return (BoringX509LookupMonoHandle)base.Handle; }
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_lookup_mono_new ();
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_x509_lookup_mono_init (
+ IntPtr handle, IntPtr instance, IntPtr by_subject_func);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_lookup_mono_free (IntPtr handle);
+
+ delegate int BySubjectFunc (IntPtr instance, IntPtr name, out IntPtr x509_ptr);
+
+ GCHandle gch;
+ IntPtr instance;
+ BySubjectFunc bySubjectFunc;
+ IntPtr bySubjectFuncPtr;
+
+ internal MonoBtlsX509LookupMono ()
+ : base (new BoringX509LookupMonoHandle (mono_btls_x509_lookup_mono_new ()))
+ {
+ gch = GCHandle.Alloc (this);
+ instance = GCHandle.ToIntPtr (gch);
+ bySubjectFunc = OnGetBySubject;
+ bySubjectFuncPtr = Marshal.GetFunctionPointerForDelegate (bySubjectFunc);
+ mono_btls_x509_lookup_mono_init (Handle.DangerousGetHandle (), instance, bySubjectFuncPtr);
+ }
+
+ protected abstract MonoBtlsX509 OnGetBySubject (MonoBtlsX509Name name);
+
+#if MONOTOUCH
+ [MonoTouch.MonoPInvokeCallback (typeof (BySubjectFunc))]
+#endif
+ static int OnGetBySubject (IntPtr instance, IntPtr name_ptr, out IntPtr x509_ptr)
+ {
+ try {
+ MonoBtlsX509LookupMono obj;
+ MonoBtlsX509Name.BoringX509NameHandle name_handle = null;
+ try {
+ obj = (MonoBtlsX509LookupMono)GCHandle.FromIntPtr (instance).Target;
+ name_handle = new MonoBtlsX509Name.BoringX509NameHandle (name_ptr, false);
+ MonoBtlsX509Name name_obj = new MonoBtlsX509Name (name_handle);
+ var x509 = obj.OnGetBySubject (name_obj);
+ if (x509 != null) {
+ x509_ptr = x509.Handle.StealHandle ();
+ return 1;
+ } else {
+ x509_ptr = IntPtr.Zero;
+ return 0;
+ }
+ } finally {
+ if (name_handle != null)
+ name_handle.Dispose ();
+ }
+ } catch (Exception ex) {
+ Console.WriteLine ("LOOKUP METHOD - GET BY SUBJECT EX: {0}", ex);
+ x509_ptr = IntPtr.Zero;
+ return 0;
+ }
+ }
+
+ protected override void Close ()
+ {
+ try {
+ if (gch.IsAllocated)
+ gch.Free ();
+ } finally {
+ instance = IntPtr.Zero;
+ bySubjectFunc = null;
+ bySubjectFuncPtr = IntPtr.Zero;
+ base.Close ();
+ }
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509LookupMonoCollection.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using System.Security.Cryptography.X509Certificates;
+
+#if MONOTOUCH
+using MonoTouch;
+#endif
+
+namespace Mono.Btls
+{
+ internal class MonoBtlsX509LookupMonoCollection : MonoBtlsX509LookupMono
+ {
+ long[] hashes;
+ MonoBtlsX509[] certificates;
+ X509CertificateCollection collection;
+ MonoBtlsX509TrustKind trust;
+
+ internal MonoBtlsX509LookupMonoCollection (X509CertificateCollection collection, MonoBtlsX509TrustKind trust)
+ {
+ this.collection = collection;
+ this.trust = trust;
+ }
+
+ void Initialize ()
+ {
+ if (certificates != null)
+ return;
+
+ hashes = new long [collection.Count];
+ certificates = new MonoBtlsX509 [collection.Count];
+ for (int i = 0; i < collection.Count; i++) {
+ // Create new 'X509 *' instance since we need to modify it to add the
+ // trust settings.
+ var data = collection [i].GetRawCertData ();
+ certificates [i] = MonoBtlsX509.LoadFromData (data, MonoBtlsX509Format.DER);
+ certificates [i].AddExplicitTrust (trust);
+ hashes [i] = certificates [i].GetSubjectNameHash ();
+ }
+ }
+
+ protected override MonoBtlsX509 OnGetBySubject (MonoBtlsX509Name name)
+ {
+ Console.WriteLine ("COLLECTION LOOKUP: {0:x} - {1}", name.GetHash (), name.GetString ());
+ Initialize ();
+
+ var hash = name.GetHash ();
+ for (int i = 0; i < certificates.Length; i++) {
+ if (hashes [i] == hash)
+ return certificates [i];
+ }
+
+ return null;
+ }
+
+ protected override void Close ()
+ {
+ try {
+ if (certificates != null) {
+ for (int i = 0; i < certificates.Length; i++) {
+ if (certificates [i] != null) {
+ certificates [i].Dispose ();
+ certificates [i] = null;
+ }
+ }
+ certificates = null;
+ hashes = null;
+ }
+ } finally {
+ base.Close ();
+ }
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509LookupCollection.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+
+namespace Mono.Btls
+{
+ enum MonoBtlsX509LookupType
+ {
+ UNKNOWN = 0,
+ FILE,
+ HASH_DIR,
+ MONO
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509Name.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+namespace Mono.Btls
+{
+ class MonoBtlsX509Name : MonoBtlsObject
+ {
+ internal class BoringX509NameHandle : MonoBtlsHandle
+ {
+ bool dontFree;
+
+ internal BoringX509NameHandle (IntPtr handle, bool ownsHandle)
+ : base (handle, ownsHandle)
+ {
+ this.dontFree = !ownsHandle;
+ }
+
+ protected override bool ReleaseHandle ()
+ {
+ if (!dontFree)
+ mono_btls_x509_name_free (handle);
+ return true;
+ }
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_name_print_bio (IntPtr handle, IntPtr bio);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_name_print_string (IntPtr handle, IntPtr buffer, int size);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_name_get_raw_data (IntPtr handle, out IntPtr buffer, int use_canon_enc);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static long mono_btls_x509_name_hash (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static long mono_btls_x509_name_hash_old (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_name_get_entry_count (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static MonoBtlsX509NameEntryType mono_btls_x509_name_get_entry_type (IntPtr name, int index);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_name_get_entry_oid (IntPtr name, int index, IntPtr buffer, int size);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_name_get_entry_oid_data (IntPtr name, int index, out IntPtr data);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_name_get_entry_value (IntPtr name, int index, out IntPtr str);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern unsafe static IntPtr mono_btls_x509_name_from_data (void* data, int len, int use_canon_enc);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_x509_name_free (IntPtr handle);
+
+ new internal BoringX509NameHandle Handle {
+ get { return (BoringX509NameHandle)base.Handle; }
+ }
+
+ internal MonoBtlsX509Name (BoringX509NameHandle handle)
+ : base (handle)
+ {
+ }
+
+ public string GetString ()
+ {
+ const int size = 4096;
+ var data = Marshal.AllocHGlobal (size);
+ try {
+ var ret = mono_btls_x509_name_print_string (
+ Handle.DangerousGetHandle (), data, size);
+ CheckError (ret);
+ return Marshal.PtrToStringAnsi (data);
+ } finally {
+ Marshal.FreeHGlobal (data);
+ }
+ }
+
+ public void PrintBio (MonoBtlsBio bio)
+ {
+ var ret = mono_btls_x509_name_print_bio (
+ Handle.DangerousGetHandle (),
+ bio.Handle.DangerousGetHandle ());
+ CheckError (ret);
+ }
+
+ public byte[] GetRawData (bool use_canon_enc)
+ {
+ IntPtr data;
+ var ret = mono_btls_x509_name_get_raw_data (
+ Handle.DangerousGetHandle (),
+ out data, use_canon_enc ? 1 : 0);
+ CheckError (ret > 0);
+ var buffer = new byte [ret];
+ Marshal.Copy (data, buffer, 0, ret);
+ FreeDataPtr (data);
+ return buffer;
+ }
+
+ public long GetHash ()
+ {
+ return mono_btls_x509_name_hash (Handle.DangerousGetHandle ());
+ }
+
+ public long GetHashOld ()
+ {
+ return mono_btls_x509_name_hash_old (Handle.DangerousGetHandle ());
+ }
+
+ public int GetEntryCount ()
+ {
+ return mono_btls_x509_name_get_entry_count (Handle.DangerousGetHandle ());
+ }
+
+ public MonoBtlsX509NameEntryType GetEntryType (int index)
+ {
+ if (index >= GetEntryCount ())
+ throw new ArgumentOutOfRangeException ();
+ return mono_btls_x509_name_get_entry_type (
+ Handle.DangerousGetHandle (), index);
+ }
+
+ public string GetEntryOid (int index)
+ {
+ if (index >= GetEntryCount ())
+ throw new ArgumentOutOfRangeException ();
+
+ const int size = 4096;
+ var data = Marshal.AllocHGlobal (size);
+ try {
+ var ret = mono_btls_x509_name_get_entry_oid (
+ Handle.DangerousGetHandle (),
+ index, data, size);
+ CheckError (ret > 0);
+ return Marshal.PtrToStringAnsi (data);
+ } finally {
+ Marshal.FreeHGlobal (data);
+ }
+ }
+
+ public byte[] GetEntryOidData (int index)
+ {
+ IntPtr data;
+ var ret = mono_btls_x509_name_get_entry_oid_data (
+ Handle.DangerousGetHandle (), index, out data);
+ CheckError (ret > 0);
+
+ var bytes = new byte[ret];
+ Marshal.Copy (data, bytes, 0, ret);
+ return bytes;
+ }
+
+ public unsafe string GetEntryValue (int index)
+ {
+ if (index >= GetEntryCount ())
+ throw new ArgumentOutOfRangeException ();
+ IntPtr data;
+ var ret = mono_btls_x509_name_get_entry_value (
+ Handle.DangerousGetHandle (), index, out data);
+ if (ret <= 0)
+ return null;
+ try {
+ return new UTF8Encoding ().GetString ((byte*)data, ret);
+ } finally {
+ if (data != IntPtr.Zero)
+ FreeDataPtr (data);
+ }
+ }
+
+ public static unsafe MonoBtlsX509Name CreateFromData (byte[] data, bool use_canon_enc)
+ {
+ fixed (void *ptr = data) {
+ var handle = mono_btls_x509_name_from_data (ptr, data.Length, use_canon_enc ? 1 : 0);
+ if (handle == IntPtr.Zero)
+ throw new MonoBtlsException ("mono_btls_x509_name_from_data() failed.");
+ return new MonoBtlsX509Name (new BoringX509NameHandle (handle, false));
+ }
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509NameEntryType.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+namespace Mono.Btls
+{
+ enum MonoBtlsX509NameEntryType : int
+ {
+ Unknown = 0,
+ CountryName,
+ OrganizationName,
+ OrganizationalUnitName,
+ CommonName,
+ LocalityName,
+ StateOrProvinceName,
+ StreetAddress,
+ SerialNumber,
+ DomainComponent,
+ UserId,
+ Email,
+ DnQualifier,
+ Title,
+ Surname,
+ GivenName,
+ Initial
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509NameList.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+namespace Mono.Btls
+{
+ class MonoBtlsX509NameList : MonoBtlsObject
+ {
+ internal class BoringX509NameListHandle : MonoBtlsHandle
+ {
+ bool dontFree;
+
+ internal BoringX509NameListHandle (IntPtr handle, bool ownsHandle)
+ : base (handle, ownsHandle)
+ {
+ this.dontFree = !ownsHandle;
+ }
+
+ protected override bool ReleaseHandle ()
+ {
+ if (!dontFree)
+ mono_btls_x509_name_list_free (handle);
+ return true;
+ }
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_name_list_new ();
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_name_list_get_count (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_name_list_add (IntPtr handle, IntPtr name);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_name_list_get_item (IntPtr handle, int index);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_x509_name_list_free (IntPtr handle);
+
+ new internal BoringX509NameListHandle Handle {
+ get { return (BoringX509NameListHandle)base.Handle; }
+ }
+
+ internal MonoBtlsX509NameList (BoringX509NameListHandle handle)
+ : base (handle)
+ {
+ }
+
+ internal MonoBtlsX509NameList ()
+ : this (Create_internal ())
+ {
+ }
+
+ static BoringX509NameListHandle Create_internal ()
+ {
+ var handle = mono_btls_x509_name_list_new ();
+ if (handle == IntPtr.Zero)
+ throw new MonoBtlsException ();
+ return new BoringX509NameListHandle (handle, true);
+ }
+
+ public int GetCount ()
+ {
+ CheckThrow ();
+ return mono_btls_x509_name_list_get_count (
+ Handle.DangerousGetHandle ());
+ }
+
+ public MonoBtlsX509Name GetItem (int index)
+ {
+ CheckThrow ();
+ if (index < 0 || index >= GetCount ())
+ throw new ArgumentOutOfRangeException ();
+ var ptr = mono_btls_x509_name_list_get_item (
+ Handle.DangerousGetHandle (), index);
+ if (ptr == IntPtr.Zero)
+ return null;
+ return new MonoBtlsX509Name (
+ new MonoBtlsX509Name.BoringX509NameHandle (ptr, true));
+ }
+
+ public void Add (MonoBtlsX509Name name)
+ {
+ CheckThrow ();
+ mono_btls_x509_name_list_add (
+ Handle.DangerousGetHandle (),
+ name.Handle.DangerousGetHandle ());
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509Purpose.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+namespace Mono.Btls
+{
+ enum MonoBtlsX509Purpose
+ {
+ SSL_CLIENT = 1,
+ SSL_SERVER = 2,
+ NS_SSL_SERVER = 3,
+ SMIME_SIGN = 4,
+ SMIME_ENCRYPT = 5,
+ CRL_SIGN = 6,
+ ANY = 7,
+ OCSP_HELPER = 8,
+ TIMESTAMP_SIGN = 9,
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509Revoked.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Text;
+using System.Threading;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography.X509Certificates;
+using System.Security.Cryptography;
+
+namespace Mono.Btls
+{
+ class MonoBtlsX509Revoked : MonoBtlsObject
+ {
+ internal class BoringX509RevokedHandle : MonoBtlsHandle
+ {
+ public BoringX509RevokedHandle (IntPtr handle)
+ : base (handle, true)
+ {
+ }
+
+ protected override bool ReleaseHandle ()
+ {
+ if (handle != IntPtr.Zero)
+ mono_btls_x509_revoked_free (handle);
+ return true;
+ }
+
+ public IntPtr StealHandle ()
+ {
+ var retval = Interlocked.Exchange (ref handle, IntPtr.Zero);
+ return retval;
+ }
+ }
+
+ new internal BoringX509RevokedHandle Handle {
+ get { return (BoringX509RevokedHandle)base.Handle; }
+ }
+
+ internal MonoBtlsX509Revoked (BoringX509RevokedHandle handle)
+ : base (handle)
+ {
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_revoked_get_serial_number (IntPtr handle, IntPtr data, int size);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static long mono_btls_x509_revoked_get_revocation_date (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_revoked_get_reason (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_revoked_get_sequence (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_x509_revoked_free (IntPtr handle);
+
+ public byte[] GetSerialNumber ()
+ {
+ int size = 256;
+ IntPtr data = Marshal.AllocHGlobal (size);
+ try {
+ var ret = mono_btls_x509_revoked_get_serial_number (
+ Handle.DangerousGetHandle (), data, size);
+ CheckError (ret > 0);
+ var buffer = new byte[ret];
+ Marshal.Copy (data, buffer, 0, ret);
+ return buffer;
+ } finally {
+ if (data != IntPtr.Zero)
+ Marshal.FreeHGlobal (data);
+ }
+ }
+
+ public DateTime GetRevocationDate ()
+ {
+ var ticks = mono_btls_x509_revoked_get_revocation_date (
+ Handle.DangerousGetHandle ());
+ return new DateTime (1970, 1, 1).AddSeconds (ticks);
+ }
+
+ public int GetReason ()
+ {
+ return mono_btls_x509_revoked_get_reason (Handle.DangerousGetHandle ());
+ }
+
+ public int GetSequence ()
+ {
+ return mono_btls_x509_revoked_get_sequence (Handle.DangerousGetHandle ());
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509Store.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Mono.Btls
+{
+ class MonoBtlsX509Store : MonoBtlsObject
+ {
+ internal class BoringX509StoreHandle : MonoBtlsHandle
+ {
+ public BoringX509StoreHandle (IntPtr handle)
+ : base (handle, true)
+ {
+ }
+
+ protected override bool ReleaseHandle ()
+ {
+ mono_btls_x509_store_free (handle);
+ return true;
+ }
+ }
+
+ new internal BoringX509StoreHandle Handle {
+ get { return (BoringX509StoreHandle)base.Handle; }
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_store_new ();
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_store_from_ctx (IntPtr ctx);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_store_from_ssl_ctx (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_store_load_locations (IntPtr handle, IntPtr file, IntPtr path);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_store_set_default_paths (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_store_add_cert (IntPtr handle, IntPtr x509);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_store_get_count (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_x509_store_free (IntPtr handle);
+
+ Dictionary<IntPtr,MonoBtlsX509Lookup> lookupHash;
+
+ public void LoadLocations (string file, string path)
+ {
+ IntPtr filePtr = IntPtr.Zero;
+ IntPtr pathPtr = IntPtr.Zero;
+ try {
+ if (file != null)
+ filePtr = Marshal.StringToHGlobalAnsi (file);
+ if (path != null)
+ pathPtr = Marshal.StringToHGlobalAnsi (path);
+ var ret = mono_btls_x509_store_load_locations (
+ Handle.DangerousGetHandle (), filePtr, pathPtr);
+ CheckError (ret);
+ } finally {
+ if (filePtr != IntPtr.Zero)
+ Marshal.FreeHGlobal (filePtr);
+ if (pathPtr != IntPtr.Zero)
+ Marshal.FreeHGlobal (pathPtr);
+ }
+ }
+
+ public void SetDefaultPaths ()
+ {
+ var ret = mono_btls_x509_store_set_default_paths (Handle.DangerousGetHandle ());
+ CheckError (ret);
+ }
+
+ static BoringX509StoreHandle Create_internal ()
+ {
+ var handle = mono_btls_x509_store_new ();
+ if (handle == IntPtr.Zero)
+ throw new MonoBtlsException ();
+ return new BoringX509StoreHandle (handle);
+ }
+
+ static BoringX509StoreHandle Create_internal (IntPtr store_ctx)
+ {
+ var handle = mono_btls_x509_store_from_ssl_ctx (store_ctx);
+ if (handle == IntPtr.Zero)
+ throw new MonoBtlsException ();
+ return new BoringX509StoreHandle (handle);
+ }
+
+ static BoringX509StoreHandle Create_internal (MonoBtlsSslCtx.BoringSslCtxHandle ctx)
+ {
+ var handle = mono_btls_x509_store_from_ssl_ctx (ctx.DangerousGetHandle ());
+ if (handle == IntPtr.Zero)
+ throw new MonoBtlsException ();
+ return new BoringX509StoreHandle (handle);
+ }
+
+ internal MonoBtlsX509Store ()
+ : base (Create_internal ())
+ {
+ }
+
+ internal MonoBtlsX509Store (IntPtr store_ctx)
+ : base (Create_internal (store_ctx))
+ {
+ }
+
+ internal MonoBtlsX509Store (MonoBtlsSslCtx.BoringSslCtxHandle ctx)
+ : base (Create_internal (ctx))
+ {
+ }
+
+ public void AddCertificate (MonoBtlsX509 x509)
+ {
+ var ret = mono_btls_x509_store_add_cert (
+ Handle.DangerousGetHandle (),
+ x509.Handle.DangerousGetHandle ());
+ CheckError (ret);
+ }
+
+ public int GetCount ()
+ {
+ return mono_btls_x509_store_get_count (Handle.DangerousGetHandle ());
+ }
+
+ internal void AddTrustedRoots ()
+ {
+ var systemRoot = MonoBtlsProvider.GetSystemStoreLocation ();
+ LoadLocations (null, systemRoot);
+ }
+
+ public MonoBtlsX509Lookup AddLookup (MonoBtlsX509LookupType type)
+ {
+ if (lookupHash == null)
+ lookupHash = new Dictionary<IntPtr,MonoBtlsX509Lookup> ();
+
+ /*
+ * X509_STORE_add_lookup() returns the same 'X509_LOOKUP *' for each
+ * unique 'X509_LOOKUP_METHOD *' (which is supposed to be a static struct)
+ * and we want to use the same managed object for each unique 'X509_LOOKUP *'.
+ */
+ var lookup = new MonoBtlsX509Lookup (this, type);
+ var nativeLookup = lookup.GetNativeLookup ();
+ if (lookupHash.ContainsKey (nativeLookup)) {
+ lookup.Dispose ();
+ lookup = lookupHash [nativeLookup];
+ } else {
+ lookupHash.Add (nativeLookup, lookup);
+ }
+
+ return lookup;
+ }
+
+ public void AddDirectoryLookup (string dir, MonoBtlsX509FileType type)
+ {
+ var lookup = AddLookup (MonoBtlsX509LookupType.HASH_DIR);
+ lookup.AddDirectory (dir, type);
+ }
+
+ public void AddFileLookup (string file, MonoBtlsX509FileType type)
+ {
+ var lookup = AddLookup (MonoBtlsX509LookupType.FILE);
+ lookup.LoadFile (file, type);
+ }
+
+ public void AddCollection (X509CertificateCollection collection, MonoBtlsX509TrustKind trust)
+ {
+ var monoLookup = new MonoBtlsX509LookupMonoCollection (collection, trust);
+ var lookup = new MonoBtlsX509Lookup (this, MonoBtlsX509LookupType.MONO);
+ lookup.AddMono (monoLookup);
+ }
+
+#if MONODROID
+ public void AddAndroidLookup ()
+ {
+ var androidLookup = new MonoBtlsX509LookupAndroid ();
+ var lookup = new MonoBtlsX509Lookup (this, MonoBtlsX509LookupType.MONO);
+ lookup.AddMono (androidLookup);
+ }
+#endif
+
+ protected override void Close ()
+ {
+ try {
+ if (lookupHash != null) {
+ foreach (var lookup in lookupHash.Values)
+ lookup.Dispose ();
+ lookupHash = null;
+ }
+ } finally {
+ base.Close ();
+ }
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509StoreCtx.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+namespace Mono.Btls
+{
+ class MonoBtlsX509StoreCtx : MonoBtlsObject
+ {
+ internal class BoringX509StoreCtxHandle : MonoBtlsHandle
+ {
+ bool dontFree;
+
+ internal BoringX509StoreCtxHandle (IntPtr handle, bool ownsHandle = true)
+ : base (handle, ownsHandle)
+ {
+ dontFree = !ownsHandle;
+ }
+
+ #if FIXME
+ internal BoringX509StoreCtxHandle (IntPtr handle)
+ : base ()
+ {
+ base.handle = handle;
+ this.dontFree = true;
+ }
+ #endif
+
+ protected override bool ReleaseHandle ()
+ {
+ if (!dontFree)
+ mono_btls_x509_store_ctx_free (handle);
+ return true;
+ }
+ }
+
+ int? verifyResult;
+
+ new internal BoringX509StoreCtxHandle Handle {
+ get { return (BoringX509StoreCtxHandle)base.Handle; }
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_store_ctx_new ();
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_store_ctx_from_ptr (IntPtr ctx);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static MonoBtlsX509Error mono_btls_x509_store_ctx_get_error (IntPtr handle, out IntPtr error_string);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_store_ctx_get_error_depth (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_store_ctx_get_chain (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_store_ctx_init (IntPtr handle, IntPtr store, IntPtr chain);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_store_ctx_set_param (IntPtr handle, IntPtr param);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_store_ctx_verify_cert (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_store_ctx_get_by_subject (IntPtr handle, IntPtr name);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_store_ctx_get_current_cert (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_store_ctx_get_current_issuer (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_store_ctx_get_verify_param (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_store_ctx_get_untrusted (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_store_ctx_up_ref (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_x509_store_ctx_free (IntPtr handle);
+
+ internal MonoBtlsX509StoreCtx ()
+ : base (new BoringX509StoreCtxHandle (mono_btls_x509_store_ctx_new ()))
+ {
+ }
+
+ static BoringX509StoreCtxHandle Create_internal (IntPtr store_ctx)
+ {
+ var handle = mono_btls_x509_store_ctx_from_ptr (store_ctx);
+ if (handle == IntPtr.Zero)
+ throw new MonoBtlsException ();
+ return new BoringX509StoreCtxHandle (handle);
+ }
+
+ internal MonoBtlsX509StoreCtx (int preverify_ok, IntPtr store_ctx)
+ : base (Create_internal (store_ctx))
+ {
+ verifyResult = preverify_ok;
+ }
+
+ internal MonoBtlsX509StoreCtx (BoringX509StoreCtxHandle ptr, int? verifyResult)
+ : base (ptr)
+ {
+ this.verifyResult = verifyResult;
+ }
+
+ public MonoBtlsX509Error GetError ()
+ {
+ IntPtr error_string_ptr;
+ return mono_btls_x509_store_ctx_get_error (Handle.DangerousGetHandle (), out error_string_ptr);
+ }
+
+ public MonoBtlsX509Exception GetException ()
+ {
+ IntPtr error_string_ptr;
+ var error = mono_btls_x509_store_ctx_get_error (Handle.DangerousGetHandle (), out error_string_ptr);
+ if (error == 0)
+ return null;
+ if (error_string_ptr != IntPtr.Zero) {
+ var error_string = Marshal.PtrToStringAnsi (error_string_ptr);
+ return new MonoBtlsX509Exception (error, error_string);
+ }
+ return new MonoBtlsX509Exception (error, "Unknown verify error.");
+ }
+
+ public MonoBtlsX509Chain GetChain ()
+ {
+ var chain = mono_btls_x509_store_ctx_get_chain (Handle.DangerousGetHandle ());
+ CheckError (chain != IntPtr.Zero);
+ return new MonoBtlsX509Chain (new MonoBtlsX509Chain.BoringX509ChainHandle (chain));
+ }
+
+ public MonoBtlsX509Chain GetUntrusted ()
+ {
+ var chain = mono_btls_x509_store_ctx_get_untrusted (Handle.DangerousGetHandle ());
+ CheckError (chain != IntPtr.Zero);
+ return new MonoBtlsX509Chain (new MonoBtlsX509Chain.BoringX509ChainHandle (chain));
+ }
+
+ public void Initialize (MonoBtlsX509Store store, MonoBtlsX509Chain chain)
+ {
+ var ret = mono_btls_x509_store_ctx_init (
+ Handle.DangerousGetHandle (),
+ store.Handle.DangerousGetHandle (),
+ chain.Handle.DangerousGetHandle ());
+ CheckError (ret);
+ }
+
+ public void SetVerifyParam (MonoBtlsX509VerifyParam param)
+ {
+ var ret = mono_btls_x509_store_ctx_set_param (
+ Handle.DangerousGetHandle (),
+ param.Handle.DangerousGetHandle ());
+ CheckError (ret);
+ }
+
+ public int VerifyResult {
+ get {
+ if (verifyResult == null)
+ throw new InvalidOperationException ();
+ return verifyResult.Value;
+ }
+ }
+
+ public int Verify ()
+ {
+ verifyResult = mono_btls_x509_store_ctx_verify_cert (Handle.DangerousGetHandle ());
+ return verifyResult.Value;
+ }
+
+ public MonoBtlsX509 LookupBySubject (MonoBtlsX509Name name)
+ {
+ var handle = mono_btls_x509_store_ctx_get_by_subject (
+ Handle.DangerousGetHandle (), name.Handle.DangerousGetHandle ());
+ if (handle == IntPtr.Zero)
+ return null;
+ return new MonoBtlsX509 (new MonoBtlsX509.BoringX509Handle (handle));
+ }
+
+ public MonoBtlsX509 GetCurrentCertificate ()
+ {
+ var x509 = mono_btls_x509_store_ctx_get_current_cert (Handle.DangerousGetHandle ());
+ if (x509 == IntPtr.Zero)
+ return null;
+ return new MonoBtlsX509 (new MonoBtlsX509.BoringX509Handle (x509));
+ }
+
+ public MonoBtlsX509 GetCurrentIssuer ()
+ {
+ var x509 = mono_btls_x509_store_ctx_get_current_issuer (Handle.DangerousGetHandle ());
+ if (x509 == IntPtr.Zero)
+ return null;
+ return new MonoBtlsX509 (new MonoBtlsX509.BoringX509Handle (x509));
+ }
+
+ public MonoBtlsX509VerifyParam GetVerifyParam ()
+ {
+ var param = mono_btls_x509_store_ctx_get_verify_param (Handle.DangerousGetHandle ());
+ if (param == IntPtr.Zero)
+ return null;
+ return new MonoBtlsX509VerifyParam (new MonoBtlsX509VerifyParam.BoringX509VerifyParamHandle (param));
+ }
+
+ public MonoBtlsX509StoreCtx Copy ()
+ {
+ var copy = mono_btls_x509_store_ctx_up_ref (Handle.DangerousGetHandle ());
+ CheckError (copy != IntPtr.Zero);
+ return new MonoBtlsX509StoreCtx (new BoringX509StoreCtxHandle (copy), verifyResult);
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509StoreManager.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+#if MONO_SECURITY_ALIAS
+extern alias MonoSecurity;
+#endif
+
+using System;
+using System.IO;
+using System.Security.Cryptography.X509Certificates;
+
+#if MONO_SECURITY_ALIAS
+using MonoSecurity::Mono.Security.Interface;
+using MX = MonoSecurity::Mono.Security.X509;
+#else
+using Mono.Security.Interface;
+using MX = Mono.Security.X509;
+#endif
+
+namespace Mono.Btls
+{
+ static class MonoBtlsX509StoreManager
+ {
+ static bool initialized;
+ static string machineTrustedRootPath;
+ static string machineIntermediateCAPath;
+ static string machineUntrustedPath;
+ static string userTrustedRootPath;
+ static string userIntermediateCAPath;
+ static string userUntrustedPath;
+
+ static void Initialize ()
+ {
+ if (initialized)
+ return;
+
+ try {
+ DoInitialize ();
+ } catch (Exception ex) {
+ Console.Error.WriteLine ("MonoBtlsX509StoreManager.Initialize() threw exception: {0}", ex);
+ } finally {
+ initialized = true;
+ }
+ }
+
+ static void DoInitialize ()
+ {
+#if !ANDROID
+ var userPath = MX.X509StoreManager.NewCurrentUserPath;
+ userTrustedRootPath = Path.Combine (userPath, MX.X509Stores.Names.TrustedRoot);
+ userIntermediateCAPath = Path.Combine (userPath, MX.X509Stores.Names.IntermediateCA);
+ userUntrustedPath = Path.Combine (userPath, MX.X509Stores.Names.Untrusted);
+
+ var machinePath = MX.X509StoreManager.NewLocalMachinePath;
+ machineTrustedRootPath = Path.Combine (userPath, MX.X509Stores.Names.TrustedRoot);
+ machineIntermediateCAPath = Path.Combine (userPath, MX.X509Stores.Names.IntermediateCA);
+ machineUntrustedPath = Path.Combine (userPath, MX.X509Stores.Names.Untrusted);
+#endif
+ }
+
+ public static bool HasStore (MonoBtlsX509StoreType type)
+ {
+#if ANDROID
+ return false;
+#else
+ var path = GetStorePath (type);
+ return path != null && Directory.Exists (path);
+#endif
+ }
+
+ public static string GetStorePath (MonoBtlsX509StoreType type)
+ {
+#if ANDROID
+ throw new NotSupportedException ();
+#else
+ Initialize ();
+ switch (type) {
+ case MonoBtlsX509StoreType.MachineTrustedRoots:
+ return machineTrustedRootPath;
+ case MonoBtlsX509StoreType.MachineIntermediateCA:
+ return machineIntermediateCAPath;
+ case MonoBtlsX509StoreType.MachineUntrusted:
+ return machineUntrustedPath;
+ case MonoBtlsX509StoreType.UserTrustedRoots:
+ return userTrustedRootPath;
+ case MonoBtlsX509StoreType.UserIntermediateCA:
+ return userIntermediateCAPath;
+ case MonoBtlsX509StoreType.UserUntrusted:
+ return userUntrustedPath;
+ default:
+ throw new NotSupportedException ();
+ }
+#endif
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509StoreType.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+namespace Mono.Btls
+{
+ enum MonoBtlsX509StoreType
+ {
+ Custom,
+ MachineTrustedRoots,
+ MachineIntermediateCA,
+ MachineUntrusted,
+ UserTrustedRoots,
+ UserIntermediateCA,
+ UserUntrusted
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509TrustKind.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+namespace Mono.Btls
+{
+ [Flags]
+ enum MonoBtlsX509TrustKind
+ {
+ DEFAULT = 0,
+ TRUST_CLIENT = 1,
+ TRUST_SERVER = 2,
+ TRUST_ALL = 4,
+ REJECT_CLIENT = 32,
+ REJECT_SERVER = 64,
+ REJECT_ALL = 128
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509VerifyFlags.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+namespace Mono.Btls
+{
+ [Flags]
+ enum MonoBtlsX509VerifyFlags
+ {
+ DEFAULT = 0,
+ CRL_CHECK = 1,
+ CRL_CHECK_ALL = 2,
+ X509_STRIC = 4
+ }
+}
+#endif
--- /dev/null
+//
+// MonoBtlsX509VerifyParam.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+namespace Mono.Btls
+{
+ class MonoBtlsX509VerifyParam : MonoBtlsObject
+ {
+ internal class BoringX509VerifyParamHandle : MonoBtlsHandle
+ {
+ public BoringX509VerifyParamHandle (IntPtr handle)
+ : base (handle, true)
+ {
+ }
+
+ protected override bool ReleaseHandle ()
+ {
+ mono_btls_x509_verify_param_free (handle);
+ return true;
+ }
+ }
+
+ new internal BoringX509VerifyParamHandle Handle {
+ get { return (BoringX509VerifyParamHandle)base.Handle; }
+ }
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_verify_param_new ();
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_verify_param_copy (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_verify_param_lookup (IntPtr name);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_verify_param_can_modify (IntPtr param);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_verify_param_set_name (IntPtr handle, IntPtr name);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_verify_param_set_host (IntPtr handle, IntPtr name, int namelen);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_verify_param_add_host (IntPtr handle, IntPtr name, int namelen);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static ulong mono_btls_x509_verify_param_get_flags (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_verify_param_set_flags (IntPtr handle, ulong flags);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static MonoBtlsX509VerifyFlags mono_btls_x509_verify_param_get_mono_flags (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_verify_param_set_mono_flags (IntPtr handle, MonoBtlsX509VerifyFlags flags);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_verify_param_set_purpose (IntPtr handle, MonoBtlsX509Purpose purpose);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_verify_param_get_depth (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_verify_param_set_depth (IntPtr handle, int depth);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static int mono_btls_x509_verify_param_set_time (IntPtr handle, long time);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static IntPtr mono_btls_x509_verify_param_get_peername (IntPtr handle);
+
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ extern static void mono_btls_x509_verify_param_free (IntPtr handle);
+
+ internal MonoBtlsX509VerifyParam ()
+ : base (new BoringX509VerifyParamHandle (mono_btls_x509_verify_param_new ()))
+ {
+ }
+
+ internal MonoBtlsX509VerifyParam (BoringX509VerifyParamHandle handle)
+ : base (handle)
+ {
+ }
+
+ public MonoBtlsX509VerifyParam Copy ()
+ {
+ var copy = mono_btls_x509_verify_param_copy (Handle.DangerousGetHandle ());
+ CheckError (copy != IntPtr.Zero);
+ return new MonoBtlsX509VerifyParam (new BoringX509VerifyParamHandle (copy));
+ }
+
+ public static MonoBtlsX509VerifyParam GetSslClient ()
+ {
+ return Lookup ("ssl_client", true);
+ }
+
+ public static MonoBtlsX509VerifyParam GetSslServer ()
+ {
+ return Lookup ("ssl_server", true);
+ }
+
+ public static MonoBtlsX509VerifyParam Lookup (string name, bool fail = false)
+ {
+ IntPtr namePtr = IntPtr.Zero;
+ IntPtr handle = IntPtr.Zero;
+
+ try {
+ namePtr = Marshal.StringToHGlobalAnsi (name);
+ handle = mono_btls_x509_verify_param_lookup (namePtr);
+ if (handle == IntPtr.Zero) {
+ if (!fail)
+ return null;
+ throw new MonoBtlsException ("X509_VERIFY_PARAM_lookup() could not find '{0}'.", name);
+ }
+
+ return new MonoBtlsX509VerifyParam (new BoringX509VerifyParamHandle (handle));
+ } finally {
+ if (namePtr != IntPtr.Zero)
+ Marshal.FreeHGlobal (namePtr);
+ }
+ }
+
+ public bool CanModify {
+ get {
+ return mono_btls_x509_verify_param_can_modify (Handle.DangerousGetHandle ()) != 0;
+ }
+ }
+
+ void WantToModify ()
+ {
+ if (!CanModify)
+ throw new MonoBtlsException ("Attempting to modify read-only MonoBtlsX509VerifyParam instance.");
+ }
+
+ public void SetName (string name)
+ {
+ WantToModify ();
+ IntPtr namePtr = IntPtr.Zero;
+ try {
+ namePtr = Marshal.StringToHGlobalAnsi (name);
+ var ret = mono_btls_x509_verify_param_set_name (
+ Handle.DangerousGetHandle (), namePtr);
+ CheckError (ret);
+ } finally {
+ if (namePtr != IntPtr.Zero)
+ Marshal.FreeHGlobal (namePtr);
+ }
+ }
+
+ public void SetHost (string name)
+ {
+ WantToModify ();
+ IntPtr namePtr = IntPtr.Zero;
+ try {
+ namePtr = Marshal.StringToHGlobalAnsi (name);
+ var ret = mono_btls_x509_verify_param_set_host (
+ Handle.DangerousGetHandle (), namePtr, name.Length);
+ CheckError (ret);
+ } finally {
+ if (namePtr != IntPtr.Zero)
+ Marshal.FreeHGlobal (namePtr);
+ }
+ }
+
+ public void AddHost (string name)
+ {
+ WantToModify ();
+ IntPtr namePtr = IntPtr.Zero;
+ try {
+ namePtr = Marshal.StringToHGlobalAnsi (name);
+ var ret = mono_btls_x509_verify_param_add_host (
+ Handle.DangerousGetHandle (), namePtr, name.Length);
+ CheckError (ret);
+ } finally {
+ if (namePtr != IntPtr.Zero)
+ Marshal.FreeHGlobal (namePtr);
+ }
+ }
+
+ public ulong GetFlags ()
+ {
+ return mono_btls_x509_verify_param_get_flags (Handle.DangerousGetHandle ());
+ }
+
+ public void SetFlags (ulong flags)
+ {
+ WantToModify ();
+ var ret = mono_btls_x509_verify_param_set_flags (
+ Handle.DangerousGetHandle (), flags);
+ CheckError (ret);
+ }
+
+ public MonoBtlsX509VerifyFlags GetMonoFlags ()
+ {
+ return mono_btls_x509_verify_param_get_mono_flags (
+ Handle.DangerousGetHandle ());
+ }
+
+ public void SetMonoFlags (MonoBtlsX509VerifyFlags flags)
+ {
+ WantToModify ();
+ var ret = mono_btls_x509_verify_param_set_mono_flags (
+ Handle.DangerousGetHandle (), flags);
+ CheckError (ret);
+ }
+
+ public void SetPurpose (MonoBtlsX509Purpose purpose)
+ {
+ WantToModify ();
+ var ret = mono_btls_x509_verify_param_set_purpose (
+ Handle.DangerousGetHandle (), purpose);
+ CheckError (ret);
+ }
+
+ public int GetDepth ()
+ {
+ return mono_btls_x509_verify_param_get_depth (Handle.DangerousGetHandle ());
+ }
+
+ public void SetDepth (int depth)
+ {
+ WantToModify ();
+ var ret = mono_btls_x509_verify_param_set_depth (
+ Handle.DangerousGetHandle (), depth);
+ CheckError (ret);
+ }
+
+ public void SetTime (DateTime time)
+ {
+ WantToModify ();
+ var epoch = new DateTime (1970, 1, 1);
+ var ticks = (long)time.Subtract (epoch).TotalSeconds;
+ var ret = mono_btls_x509_verify_param_set_time (
+ Handle.DangerousGetHandle (), ticks);
+ CheckError (ret);
+ }
+
+ public string GetPeerName ()
+ {
+ var peer = mono_btls_x509_verify_param_get_peername (Handle.DangerousGetHandle ());
+ if (peer == IntPtr.Zero)
+ return null;
+ return Marshal.PtrToStringAnsi (peer);
+ }
+ }
+}
+#endif
--- /dev/null
+//
+// X509CertificateImplBtls.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.Text;
+using System.Security;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using MX = Mono.Security.X509;
+
+namespace Mono.Btls
+{
+ class X509CertificateImplBtls : X509Certificate2Impl
+ {
+ MonoBtlsX509 x509;
+ MonoBtlsKey privateKey;
+ X500DistinguishedName subjectName;
+ X500DistinguishedName issuerName;
+ X509CertificateImplCollection intermediateCerts;
+ PublicKey publicKey;
+ bool archived;
+ bool disallowFallback;
+
+ internal X509CertificateImplBtls (bool disallowFallback = false)
+ {
+ this.disallowFallback = disallowFallback;
+ }
+
+ internal X509CertificateImplBtls (MonoBtlsX509 x509, bool disallowFallback = false)
+ {
+ this.disallowFallback = disallowFallback;
+ this.x509 = x509.Copy ();
+ }
+
+ X509CertificateImplBtls (X509CertificateImplBtls other)
+ {
+ disallowFallback = other.disallowFallback;
+ x509 = other.x509 != null ? other.x509.Copy () : null;
+ privateKey = other.privateKey != null ? other.privateKey.Copy () : null;
+ if (other.intermediateCerts != null)
+ intermediateCerts = other.intermediateCerts.Clone ();
+ }
+
+ internal X509CertificateImplBtls (byte[] data, MonoBtlsX509Format format, bool disallowFallback = false)
+ {
+ this.disallowFallback = disallowFallback;
+ x509 = MonoBtlsX509.LoadFromData (data, format);
+ }
+
+ public override bool IsValid {
+ get { return x509 != null && x509.IsValid; }
+ }
+
+ public override IntPtr Handle {
+ get { return x509.Handle.DangerousGetHandle (); }
+ }
+
+ public override IntPtr GetNativeAppleCertificate ()
+ {
+ return IntPtr.Zero;
+ }
+
+ internal MonoBtlsX509 X509 {
+ get {
+ ThrowIfContextInvalid ();
+ return x509;
+ }
+ }
+
+ internal MonoBtlsKey NativePrivateKey {
+ get {
+ ThrowIfContextInvalid ();
+ return privateKey;
+ }
+ }
+
+ public override X509CertificateImpl Clone ()
+ {
+ ThrowIfContextInvalid ();
+ return new X509CertificateImplBtls (this);
+ }
+
+ public override bool Equals (X509CertificateImpl other, out bool result)
+ {
+ var otherBoringImpl = other as X509CertificateImplBtls;
+ if (otherBoringImpl == null) {
+ result = false;
+ return false;
+ }
+
+ result = MonoBtlsX509.Compare (X509, otherBoringImpl.X509) == 0;
+ return true;
+ }
+
+ protected override byte [] GetCertHash (bool lazy)
+ {
+ return X509.GetCertHash ();
+ }
+
+ public override byte [] GetRawCertData ()
+ {
+ return X509.GetRawData (MonoBtlsX509Format.DER);
+ }
+
+ public override string GetSubjectName (bool legacyV1Mode)
+ {
+ if (legacyV1Mode)
+ return SubjectName.Decode (X500DistinguishedNameFlags.None);
+ return SubjectName.Name;
+ }
+
+ public override string GetIssuerName (bool legacyV1Mode)
+ {
+ if (legacyV1Mode)
+ return IssuerName.Decode (X500DistinguishedNameFlags.None);
+ return IssuerName.Name;
+ }
+
+ public override DateTime GetValidFrom ()
+ {
+ return X509.GetNotBefore ().ToLocalTime ();
+ }
+
+ public override DateTime GetValidUntil ()
+ {
+ return X509.GetNotAfter ().ToLocalTime ();
+ }
+
+ public override byte[] GetPublicKey ()
+ {
+ return X509.GetPublicKeyData ();
+ }
+
+ public override byte[] GetSerialNumber ()
+ {
+ return X509.GetSerialNumber (true);
+ }
+
+ public override string GetKeyAlgorithm ()
+ {
+ return PublicKey.Oid.Value;
+ }
+
+ public override byte[] GetKeyAlgorithmParameters ()
+ {
+ return PublicKey.EncodedParameters.RawData;
+ }
+
+ public override byte [] Export (X509ContentType contentType, byte [] password)
+ {
+ ThrowIfContextInvalid ();
+
+ switch (contentType) {
+ case X509ContentType.Cert:
+ return GetRawCertData ();
+ case X509ContentType.Pfx: // this includes Pkcs12
+ // TODO
+ throw new NotSupportedException ();
+ case X509ContentType.SerializedCert:
+ // TODO
+ throw new NotSupportedException ();
+ default:
+ string msg = Locale.GetText ("This certificate format '{0}' cannot be exported.", contentType);
+ throw new CryptographicException (msg);
+ }
+ }
+
+ internal override X509CertificateImplCollection IntermediateCertificates {
+ get { return intermediateCerts; }
+ }
+
+ public override string ToString (bool full)
+ {
+ ThrowIfContextInvalid ();
+
+ if (!full) {
+ var summary = GetSubjectName (false);
+ return string.Format ("[X509Certificate: {0}]", summary);
+ }
+
+ string nl = Environment.NewLine;
+ StringBuilder sb = new StringBuilder ();
+ sb.AppendFormat ("[Subject]{0} {1}{0}{0}", nl, GetSubjectName (false));
+
+ sb.AppendFormat ("[Issuer]{0} {1}{0}{0}", nl, GetIssuerName (false));
+ sb.AppendFormat ("[Not Before]{0} {1}{0}{0}", nl, GetValidFrom ().ToLocalTime ());
+ sb.AppendFormat ("[Not After]{0} {1}{0}{0}", nl, GetValidUntil ().ToLocalTime ());
+ sb.AppendFormat ("[Thumbprint]{0} {1}{0}", nl, X509Helper.ToHexString (GetCertHash ()));
+
+ sb.Append (nl);
+ return sb.ToString ();
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (x509 != null) {
+ x509.Dispose ();
+ x509 = null;
+ }
+ }
+
+#region X509Certificate2Impl
+
+ X509Certificate2Impl fallback;
+
+ void MustFallback ()
+ {
+ if (disallowFallback)
+ throw new InvalidOperationException ();
+ if (fallback != null)
+ return;
+ fallback = X509Helper2.Import (GetRawCertData (), null, X509KeyStorageFlags.DefaultKeySet);
+ }
+
+ internal X509Certificate2Impl FallbackImpl {
+ get {
+ MustFallback ();
+ return fallback;
+ }
+ }
+
+ [MonoTODO]
+ public override bool Archived {
+ get {
+ ThrowIfContextInvalid ();
+ return archived;
+ }
+ set {
+ ThrowIfContextInvalid ();
+ archived = value;
+ }
+ }
+
+ public override X509ExtensionCollection Extensions {
+ get { return FallbackImpl.Extensions; }
+ }
+
+ public override bool HasPrivateKey {
+ get { return FallbackImpl.HasPrivateKey; }
+ }
+
+ public override X500DistinguishedName IssuerName {
+ get {
+ ThrowIfContextInvalid ();
+ if (issuerName == null) {
+ using (var xname = x509.GetIssuerName ()) {
+ var encoding = xname.GetRawData (false);
+ var canonEncoding = xname.GetRawData (true);
+ var name = MonoBtlsUtils.FormatName (xname, true, ", ", true);
+ issuerName = new X500DistinguishedName (encoding, canonEncoding, name);
+ }
+ }
+ return issuerName;
+ }
+ }
+
+ public override AsymmetricAlgorithm PrivateKey {
+ get { return FallbackImpl.PrivateKey; }
+ set { FallbackImpl.PrivateKey = value; }
+ }
+
+ public override PublicKey PublicKey {
+ get {
+ ThrowIfContextInvalid ();
+ if (publicKey == null) {
+ var keyAsn = X509.GetPublicKeyAsn1 ();
+ var keyParamAsn = X509.GetPublicKeyParameters ();
+ publicKey = new PublicKey (keyAsn.Oid, keyParamAsn, keyAsn);
+ }
+ return publicKey;
+ }
+ }
+
+ public override Oid SignatureAlgorithm {
+ get {
+ ThrowIfContextInvalid ();
+ return X509.GetSignatureAlgorithm ();
+ }
+ }
+
+ public override X500DistinguishedName SubjectName {
+ get {
+ ThrowIfContextInvalid ();
+ if (subjectName == null) {
+ using (var xname = x509.GetSubjectName ()) {
+ var encoding = xname.GetRawData (false);
+ var canonEncoding = xname.GetRawData (true);
+ var name = MonoBtlsUtils.FormatName (xname, true, ", ", true);
+ subjectName = new X500DistinguishedName (encoding, canonEncoding, name);
+ }
+ }
+ return subjectName;
+ }
+ }
+
+ public override int Version {
+ get { return X509.GetVersion (); }
+ }
+
+ public override string GetNameInfo (X509NameType nameType, bool forIssuer)
+ {
+ return FallbackImpl.GetNameInfo (nameType, forIssuer);
+ }
+
+ public override void Import (byte [] data, string password, X509KeyStorageFlags keyStorageFlags)
+ {
+ if (password == null) {
+ // Does it look like PEM?
+ if ((data.Length > 0) && (data [0] != 0x30))
+ x509 = MonoBtlsX509.LoadFromData (data, MonoBtlsX509Format.PEM);
+ else
+ x509 = MonoBtlsX509.LoadFromData (data, MonoBtlsX509Format.DER);
+ return;
+ }
+
+ using (var pkcs12 = new MonoBtlsPkcs12 ()) {
+ pkcs12.Import (data, password);
+ x509 = pkcs12.GetCertificate (0);
+ if (pkcs12.HasPrivateKey)
+ privateKey = pkcs12.GetPrivateKey ();
+ if (pkcs12.Count > 1) {
+ intermediateCerts = new X509CertificateImplCollection ();
+ for (int i = 0; i < pkcs12.Count; i++) {
+ using (var ic = pkcs12.GetCertificate (i)) {
+ if (MonoBtlsX509.Compare (ic, x509) == 0)
+ continue;
+ var impl = new X509CertificateImplBtls (ic, true);
+ intermediateCerts.Add (impl, true);
+ }
+ }
+ }
+ }
+ }
+
+ public override byte [] Export (X509ContentType contentType, string password)
+ {
+ return FallbackImpl.Export (contentType, password);
+ }
+
+ public override bool Verify (X509Certificate2 thisCertificate)
+ {
+ return FallbackImpl.Verify (thisCertificate);
+ }
+
+ public override void Reset ()
+ {
+ if (x509 != null) {
+ x509.Dispose ();
+ x509 = null;
+ }
+ if (privateKey != null) {
+ privateKey = null;
+ privateKey = null;
+ }
+ subjectName = null;
+ issuerName = null;
+ archived = false;
+ publicKey = null;
+ intermediateCerts = null;
+ if (fallback != null)
+ fallback.Reset ();
+ }
+
+#endregion
+ }
+}
+#endif
--- /dev/null
+//
+// X509ChainImplBtls.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2016 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.
+#if SECURITY_DEP
+using System;
+using System.Text;
+using System.Security;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using MX = Mono.Security.X509;
+
+namespace Mono.Btls
+{
+ class X509ChainImplBtls : X509ChainImpl
+ {
+ MonoBtlsX509StoreCtx storeCtx;
+ MonoBtlsX509Chain chain;
+ MonoBtlsX509Chain untrustedChain;
+ X509ChainElementCollection elements;
+ X509Certificate2Collection untrusted;
+ X509Certificate2[] certificates;
+ X509ChainPolicy policy;
+
+ internal X509ChainImplBtls (MonoBtlsX509Chain chain)
+ {
+ this.chain = chain.Copy ();
+ policy = new X509ChainPolicy ();
+ }
+
+ internal X509ChainImplBtls (MonoBtlsX509StoreCtx storeCtx)
+ {
+ this.storeCtx = storeCtx.Copy ();
+ this.chain = storeCtx.GetChain ();
+
+ policy = new X509ChainPolicy ();
+
+ untrustedChain = storeCtx.GetUntrusted ();
+
+ if (untrustedChain != null) {
+ untrusted = new X509Certificate2Collection ();
+ policy.ExtraStore = untrusted;
+ for (int i = 0; i < untrustedChain.Count; i++) {
+ using (var cert = untrustedChain.GetCertificate (i))
+ using (var impl = new X509CertificateImplBtls (cert))
+ untrusted.Add (new X509Certificate2 (impl));
+ }
+ }
+ }
+
+ internal X509ChainImplBtls ()
+ {
+ chain = new MonoBtlsX509Chain ();
+ elements = new X509ChainElementCollection ();
+ policy = new X509ChainPolicy ();
+ }
+
+ public override bool IsValid {
+ get { return chain != null && chain.IsValid; }
+ }
+
+ public override IntPtr Handle {
+ get { return chain.Handle.DangerousGetHandle (); }
+ }
+
+ internal MonoBtlsX509Chain Chain {
+ get {
+ ThrowIfContextInvalid ();
+ return chain;
+ }
+ }
+
+ internal MonoBtlsX509StoreCtx StoreCtx {
+ get {
+ ThrowIfContextInvalid ();
+ return storeCtx;
+ }
+ }
+
+ public override X509ChainElementCollection ChainElements {
+ get {
+ ThrowIfContextInvalid ();
+ if (elements != null)
+ return elements;
+
+ elements = new X509ChainElementCollection ();
+ certificates = new X509Certificate2 [chain.Count];
+
+ for (int i = 0; i < certificates.Length; i++) {
+ var cert = chain.GetCertificate (i);
+ var impl = new X509CertificateImplBtls (cert);
+ certificates [i] = new X509Certificate2 (impl);
+ elements.Add (certificates [i]);
+ }
+
+ return elements;
+ }
+ }
+
+ public override X509ChainPolicy ChainPolicy {
+ get { return policy; }
+ set { policy = value; }
+ }
+
+ public override X509ChainStatus[] ChainStatus {
+ get { throw new NotImplementedException (); }
+ }
+
+ public override bool Build (X509Certificate2 certificate)
+ {
+ return false;
+ }
+
+ public override void Reset ()
+ {
+ if (certificates != null) {
+ foreach (var certificate in certificates)
+ certificate.Dispose ();
+ certificates = null;
+ }
+ if (elements != null) {
+ elements.Clear ();
+ elements = null;
+ }
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing) {
+ if (chain != null) {
+ chain.Dispose ();
+ chain = null;
+ }
+ if (storeCtx != null) {
+ storeCtx.Dispose ();
+ storeCtx = null;
+ }
+ if (untrustedChain != null) {
+ untrustedChain.Dispose ();
+ untrustedChain = null;
+ }
+ if (untrusted != null) {
+ foreach (var cert in untrusted)
+ cert.Dispose ();
+ }
+ }
+ base.Dispose (disposing);
+ }
+ }
+}
+#endif
return new ValidationResult (result, user_denied, 0, (MonoSslPolicyErrors)errors);
}
+ // Ignore port number when validating certificates.
+ if (!string.IsNullOrEmpty (host)) {
+ var pos = host.IndexOf (':');
+ if (pos > 0)
+ host = host.Substring (0, pos);
+ }
+
ICertificatePolicy policy = ServicePointManager.GetLegacyCertificatePolicy ();
int status11 = 0; // Error code passed to the obsolete ICertificatePolicy callback
wantsChain = true;
}
- bool providerValidated = false;
- if (provider != null && provider.HasCustomSystemCertificateValidator) {
- var xerrors = (MonoSslPolicyErrors)errors;
- providerValidated = provider.InvokeSystemCertificateValidator (this, host, server, certs, wantsChain, ref chain, out result, ref xerrors, ref status11);
- errors = (SslPolicyErrors)xerrors;
- } else if (wantsChain) {
- chain = SystemCertificateValidator.CreateX509Chain (certs);
- }
-
- if (!providerValidated)
- result = SystemCertificateValidator.Evaluate (settings, host, certs, chain, ref errors, ref status11);
+ var xerrors = (MonoSslPolicyErrors)errors;
+ result = provider.ValidateCertificate (this, host, server, certs, wantsChain, ref chain, ref xerrors, ref status11);
+ errors = (SslPolicyErrors)xerrors;
if (policy != null && (!(policy is DefaultCertificatePolicy) || certValidationCallback == null)) {
ServicePoint sp = null;
IMonoSslStream CreateSslStream (
Stream innerStream, bool leaveInnerStreamOpen,
MonoTlsSettings settings);
-
- IMonoTlsContext CreateTlsContext (
- string hostname, bool serverMode, TlsProtocols protocolFlags,
- X509Certificate serverCertificate, X509CertificateCollection clientCertificates,
- bool remoteCertRequired, bool checkCertName, bool checkCertRevocationStatus,
- MonoEncryptionPolicy encryptionPolicy, MonoTlsSettings settings);
#endif
}
}
get { return false; }
}
- internal override bool SupportsTlsContext {
- get { return false; }
- }
-
public override SslProtocols SupportedProtocols {
get { return SslProtocols.Tls; }
}
return new Private.MonoSslStreamImpl (impl);
}
- internal override MSI.IMonoTlsContext CreateTlsContext (
- string hostname, bool serverMode, MSI.TlsProtocols protocolFlags,
- X509Certificate serverCertificate, X509CertificateCollection clientCertificates,
- bool remoteCertRequired, MSI.MonoEncryptionPolicy encryptionPolicy,
- MSI.MonoTlsSettings settings)
+ internal override bool ValidateCertificate (
+ MSI.ICertificateValidator2 validator, string targetHost, bool serverMode,
+ X509CertificateCollection certificates, bool wantsChain, ref X509Chain chain,
+ ref MSI.MonoSslPolicyErrors errors, ref int status11)
{
- throw new NotSupportedException ();
+ if (wantsChain)
+ chain = SystemCertificateValidator.CreateX509Chain (certificates);
+ var xerrors = (SslPolicyErrors)errors;
+ var result = SystemCertificateValidator.Evaluate (validator.Settings, targetHost, certificates, chain, ref xerrors, ref status11);
+ errors = (MSI.MonoSslPolicyErrors)xerrors;
+ return result;
}
}
}
#if SECURITY_DEP
using System;
using MSI = Mono.Security.Interface;
-#if HAVE_BTLS
using Mono.Btls;
-#endif
namespace Mono.Net.Security
{
case "legacy":
return new LegacyTlsProvider ();
case "btls":
-#if HAVE_BTLS
if (!MonoBtlsProvider.IsSupported ())
throw new NotSupportedException ("BTLS in not supported!");
return new MonoBtlsProvider ();
-#else
- throw new NotSupportedException ("BTLS in not supported!");
-#endif
default:
throw new NotSupportedException (string.Format ("Invalid TLS Provider: `{0}'.", provider));
}
return;
providerRegistration = new Dictionary<string,string> ();
providerRegistration.Add ("legacy", "Mono.Net.Security.LegacyTlsProvider");
-#if HAVE_BTLS
+ providerRegistration.Add ("default", "Mono.Net.Security.LegacyTlsProvider");
if (Mono.Btls.MonoBtlsProvider.IsSupported ())
providerRegistration.Add ("btls", "Mono.Btls.MonoBtlsProvider");
-#endif
X509Helper2.Initialize ();
}
}
static MSI.MonoTlsProvider TryDynamicLoad ()
{
var variable = Environment.GetEnvironmentVariable ("MONO_TLS_PROVIDER");
- if (variable == null)
- return null;
-
- if (string.Equals (variable, "default", StringComparison.OrdinalIgnoreCase))
- return null;
+ if (string.IsNullOrEmpty (variable))
+ variable = "default";
return LookupProvider (variable, true);
}
+++ /dev/null
-//
-// MonoTlsProviderImpl.cs
-//
-// Author:
-// Martin Baulig <martin.baulig@xamarin.com>
-//
-// Copyright (c) 2015 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.
-
-#if SECURITY_DEP
-
-#if MONO_SECURITY_ALIAS
-extern alias MonoSecurity;
-#endif
-
-#if MONO_SECURITY_ALIAS
-using MSI = MonoSecurity::Mono.Security.Interface;
-#else
-using MSI = Mono.Security.Interface;
-#endif
-
-using System;
-using System.IO;
-using System.Net;
-using System.Net.Security;
-using System.Security.Cryptography.X509Certificates;
-
-namespace Mono.Net.Security.Private
-{
- /*
- * Strictly private - do not use outside the Mono.Net.Security directory.
- */
- abstract class MonoTlsProviderImpl : MSI.MonoTlsProvider, IMonoTlsProvider
- {
- MSI.MonoTlsProvider IMonoTlsProvider.Provider {
- get { return this; }
- }
-
- IMonoSslStream IMonoTlsProvider.CreateSslStream (
- Stream innerStream, bool leaveInnerStreamOpen,
- MSI.MonoTlsSettings settings)
- {
- return CreateSslStreamImpl (innerStream, leaveInnerStreamOpen, settings);
- }
-
- protected abstract IMonoSslStream CreateSslStreamImpl (
- Stream innerStream, bool leaveInnerStreamOpen,
- MSI.MonoTlsSettings settings);
-
- public override MSI.IMonoSslStream CreateSslStream (
- Stream innerStream, bool leaveInnerStreamOpen,
- MSI.MonoTlsSettings settings = null)
- {
- var sslStream = CreateSslStreamImpl (innerStream, leaveInnerStreamOpen, settings);
- return new MonoSslStreamImpl (sslStream);
- }
-
- MSI.IMonoTlsContext IMonoTlsProvider.CreateTlsContext (
- string hostname, bool serverMode, MSI.TlsProtocols protocolFlags,
- X509Certificate serverCertificate, X509CertificateCollection clientCertificates,
- bool remoteCertRequired, bool checkCertName, bool checkCertRevocationStatus,
- MSI.MonoEncryptionPolicy encryptionPolicy, MSI.MonoTlsSettings settings)
- {
- return CreateTlsContextImpl (
- hostname, serverMode, protocolFlags,
- serverCertificate, clientCertificates,
- remoteCertRequired, encryptionPolicy, settings);
- }
-
- protected abstract MSI.IMonoTlsContext CreateTlsContextImpl (
- string hostname, bool serverMode, MSI.TlsProtocols protocolFlags,
- X509Certificate serverCertificate, X509CertificateCollection clientCertificates,
- bool remoteCertRequired, MSI.MonoEncryptionPolicy encryptionPolicy,
- MSI.MonoTlsSettings settings);
-
- internal override MSI.IMonoTlsContext CreateTlsContext (
- string hostname, bool serverMode, MSI.TlsProtocols protocolFlags,
- X509Certificate serverCertificate, X509CertificateCollection clientCertificates,
- bool remoteCertRequired, MSI.MonoEncryptionPolicy encryptionPolicy,
- MSI.MonoTlsSettings settings)
- {
- return CreateTlsContextImpl (
- hostname, serverMode, (MSI.TlsProtocols)protocolFlags,
- serverCertificate, clientCertificates,
- remoteCertRequired, (MSI.MonoEncryptionPolicy)encryptionPolicy,
- settings);
- }
- }
-}
-
-#endif
return monoSslStreamImpl.Impl;
return new MonoSslStreamWrapper (sslStream);
}
-
- public MSI.IMonoTlsContext CreateTlsContext (
- string hostname, bool serverMode, MSI.TlsProtocols protocolFlags,
- X509Certificate serverCertificate, X509CertificateCollection clientCertificates,
- bool remoteCertRequired, bool checkCertName, bool checkCertRevocationStatus,
- MSI.MonoEncryptionPolicy encryptionPolicy, MSI.MonoTlsSettings settings)
- {
- return provider.CreateTlsContext (
- hostname, serverMode, protocolFlags,
- serverCertificate, clientCertificates,
- remoteCertRequired, (MSI.MonoEncryptionPolicy)encryptionPolicy,
- settings);
- }
}
}
<Compile Include="Mono.Net.Security\MonoSslStreamImpl.cs" />\r
<Compile Include="Mono.Net.Security\MonoSslStreamWrapper.cs" />\r
<Compile Include="Mono.Net.Security\MonoTlsProviderFactory.cs" />\r
- <Compile Include="Mono.Net.Security\MonoTlsProviderImpl.cs" />\r
<Compile Include="Mono.Net.Security\MonoTlsProviderWrapper.cs" />\r
<Compile Include="Mono.Net.Security\MonoTlsStream.cs" />\r
<Compile Include="Mono.Net.Security\NoReflectionHelper.cs" />\r
<Compile Include="Mono.Net.Security\MonoSslStreamImpl.cs" />\r
<Compile Include="Mono.Net.Security\MonoSslStreamWrapper.cs" />\r
<Compile Include="Mono.Net.Security\MonoTlsProviderFactory.cs" />\r
- <Compile Include="Mono.Net.Security\MonoTlsProviderImpl.cs" />\r
<Compile Include="Mono.Net.Security\MonoTlsProviderWrapper.cs" />\r
<Compile Include="Mono.Net.Security\MonoTlsStream.cs" />\r
<Compile Include="Mono.Net.Security\NoReflectionHelper.cs" />\r
<Compile Include="Mono.Net.Security\MonoSslStreamImpl.cs" />\r
<Compile Include="Mono.Net.Security\MonoSslStreamWrapper.cs" />\r
<Compile Include="Mono.Net.Security\MonoTlsProviderFactory.cs" />\r
- <Compile Include="Mono.Net.Security\MonoTlsProviderImpl.cs" />\r
<Compile Include="Mono.Net.Security\MonoTlsProviderWrapper.cs" />\r
<Compile Include="Mono.Net.Security\MonoTlsStream.cs" />\r
<Compile Include="Mono.Net.Security\NoReflectionHelper.cs" />\r
--- /dev/null
+//
+// SslStream.cs
+//
+// Author:
+// Rolf Bjarne Kvinge <rolf@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.IO;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading.Tasks;
+
+namespace System.Net.Security
+{
+ public class SslStream : AuthenticatedStream
+ {
+ const string EXCEPTION_MESSAGE = "System.Net.Security.SslStream is not supported on the current platform.";
+
+ public SslStream (Stream innerStream)
+ : this (innerStream, false, null, null)
+ {
+ }
+
+ public SslStream (Stream innerStream, bool leaveInnerStreamOpen)
+ : this (innerStream, leaveInnerStreamOpen, null, null)
+ {
+ }
+
+ public SslStream (Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback)
+ : this (innerStream, leaveInnerStreamOpen, userCertificateValidationCallback, null)
+ {
+ }
+
+ public SslStream (Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback, LocalCertificateSelectionCallback userCertificateSelectionCallback)
+ : base (innerStream, leaveInnerStreamOpen)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public SslStream (Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback, LocalCertificateSelectionCallback userCertificateSelectionCallback, EncryptionPolicy encryptionPolicy)
+ : this (innerStream, leaveInnerStreamOpen, userCertificateValidationCallback, userCertificateSelectionCallback)
+ {
+ }
+
+ public virtual void AuthenticateAsClient (string targetHost)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual void AuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, AsyncCallback asyncCallback, object asyncState)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual void EndAuthenticateAsClient (IAsyncResult asyncResult)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual void AuthenticateAsServer (X509Certificate serverCertificate)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual void AuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, AsyncCallback asyncCallback, object asyncState)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual void EndAuthenticateAsServer (IAsyncResult asyncResult)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public TransportContext TransportContext {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public virtual Task AuthenticateAsClientAsync (string targetHost)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual Task AuthenticateAsClientAsync (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override bool IsAuthenticated {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool IsMutuallyAuthenticated {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool IsEncrypted {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool IsSigned {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool IsServer {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public virtual SslProtocols SslProtocol {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public virtual bool CheckCertRevocationStatus {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public virtual X509Certificate LocalCertificate {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public virtual X509Certificate RemoteCertificate {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public virtual CipherAlgorithmType CipherAlgorithm {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public virtual int CipherStrength {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public virtual HashAlgorithmType HashAlgorithm {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public virtual int HashStrength {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public virtual ExchangeAlgorithmType KeyExchangeAlgorithm {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public virtual int KeyExchangeStrength {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool CanSeek {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool CanRead {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool CanTimeout {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override bool CanWrite {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override int ReadTimeout {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override int WriteTimeout {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override long Length {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override long Position {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public override void SetLength (long value)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override long Seek (long offset, SeekOrigin origin)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void Flush ()
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ }
+
+ public override int Read (byte[] buffer, int offset, int count)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public void Write (byte[] buffer)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void Write (byte[] buffer, int offset, int count)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override IAsyncResult BeginRead (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override int EndRead (IAsyncResult asyncResult)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override IAsyncResult BeginWrite (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public override void EndWrite (IAsyncResult asyncResult)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+ }
+}
--- /dev/null
+//
+// AuthenticationManager.cs
+//
+// Author:
+// Rolf Bjarne Kvinge <rolf@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.Collections;
+using System.Collections.Specialized;
+
+namespace System.Net
+{
+ public class AuthenticationManager {
+ const string EXCEPTION_MESSAGE = "System.Net.AuthenticationManager is not supported on the current platform.";
+
+ private AuthenticationManager ()
+ {
+ }
+
+ public static ICredentialPolicy CredentialPolicy {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static StringDictionary CustomTargetNameDictionary {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static IEnumerator RegisteredModules {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static Authorization Authenticate (string challenge, WebRequest request, ICredentials credentials)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public static Authorization PreAuthenticate (WebRequest request, ICredentials credentials)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public static void Register (IAuthenticationModule authenticationModule)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public static void Unregister (IAuthenticationModule authenticationModule)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public static void Unregister (string authenticationScheme)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+ }
+}
throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
}
- internal HttpListener (System.Security.Cryptography.X509Certificates.X509Certificate certificate, Mono.Net.Security.IMonoTlsProvider tlsProvider, Mono.Security.Interface.MonoTlsSettings tlsSettings)
- {
- throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
- }
-
public AuthenticationSchemes AuthenticationSchemes {
get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
msg = "Error: " + status;
wex = new WebException (msg, status);
} else {
- msg = String.Format ("Error: {0} ({1})", status, exc.Message);
- wex = new WebException (msg, status, WebExceptionInternalStatus.RequestFatal, exc);
+ wex = exc as WebException;
+ if (wex == null) {
+ msg = String.Format ("Error: {0} ({1})", status, exc.Message);
+ wex = new WebException (msg, status, WebExceptionInternalStatus.RequestFatal, exc);
+ }
}
r.SetCompleted (false, wex);
r.DoCallback ();
{
internal const string EXCEPTION_MESSAGE = "System.Net.HttpWebRequest is not supported on the current platform.";
- internal WebConnection WebConnection {
- get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
- set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
- }
-
#if MOBILE
public
#else
get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
}
- internal Mono.Net.Security.IMonoTlsProvider TlsProvider {
- get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
- }
-
- internal Mono.Security.Interface.MonoTlsSettings TlsSettings {
- get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
- }
-
public X509CertificateCollection ClientCertificates {
get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
{
throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
}
-
- internal void SetWriteStream (WebConnectionStream stream)
- {
- throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
- }
-
- internal void SetResponseData (WebConnectionData data)
- {
- throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
- }
-
- internal void SetResponseError (WebExceptionStatus status, Exception e, string where)
- {
- throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
- }
-
- internal bool ReuseConnection {
- get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
- set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
- }
-
- internal WebConnection StoredConnection {
- get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
- set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
- }
}
}
public string Server {
get {
CheckDisposed ();
- return webHeaders ["Server"];
+ return webHeaders ["Server"] ?? "";
}
}
--- /dev/null
+//
+// ServicePoint.cs
+//
+// Author:
+// Rolf Bjarne Kvinge <rolf@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.Net.Sockets;
+using System.Security.Cryptography.X509Certificates;
+
+namespace System.Net
+{
+ public class ServicePoint
+ {
+ const string EXCEPTION_MESSAGE = "System.Net.ServicePoint is not supported on the current platform.";
+
+ ServicePoint () {}
+
+ public Uri Address {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ static Exception GetMustImplement ()
+ {
+ return new NotImplementedException ();
+ }
+
+ public BindIPEndPoint BindIPEndPointDelegate
+ {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public int ConnectionLeaseTimeout {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public int ConnectionLimit {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public string ConnectionName {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public int CurrentConnections {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public DateTime IdleSince {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public int MaxIdleTime {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public virtual Version ProtocolVersion {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public int ReceiveBufferSize {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public bool SupportsPipelining {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public bool Expect100Continue {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public bool UseNagleAlgorithm {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public void SetTcpKeepAlive (bool enabled, int keepAliveTime, int keepAliveInterval)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public bool CloseConnectionGroup (string connectionGroupName)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public X509Certificate Certificate {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public X509Certificate ClientCertificate {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ // For reference source
+ internal Socket GetConnection(PooledStream PooledStream, object owner, bool async, out IPAddress address, ref Socket abortSocket, ref Socket abortSocket6)
+ {
+ throw new NotImplementedException ();
+ }
+ }
+}
--- /dev/null
+//
+// ServicePointManager.cs
+//
+// Author:
+// Rolf Bjarne Kvinge <rolf@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.Net.Security;
+
+namespace System.Net
+{
+ public partial class ServicePointManager {
+ const string EXCEPTION_MESSAGE = "System.Net.ServicePointManager is not supported on the current platform.";
+
+ public const int DefaultNonPersistentConnectionLimit = 4;
+#if MOBILE
+ public const int DefaultPersistentConnectionLimit = 10;
+#else
+ public const int DefaultPersistentConnectionLimit = 2;
+#endif
+
+ private ServicePointManager ()
+ {
+ }
+
+ public static ICertificatePolicy CertificatePolicy {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static bool CheckCertificateRevocationList {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static int DefaultConnectionLimit {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static int DnsRefreshTimeout {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static bool EnableDnsRoundRobin {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static int MaxServicePointIdleTime {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static int MaxServicePoints {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static bool ReusePort {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static SecurityProtocolType SecurityProtocol {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static RemoteCertificateValidationCallback ServerCertificateValidationCallback {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static EncryptionPolicy EncryptionPolicy {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static bool Expect100Continue {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static bool UseNagleAlgorithm {
+ get { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ set { throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); }
+ }
+
+ public static void SetTcpKeepAlive (bool enabled, int keepAliveTime, int keepAliveInterval)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public static ServicePoint FindServicePoint (Uri address)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public static ServicePoint FindServicePoint (string uriString, IWebProxy proxy)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+
+ public static ServicePoint FindServicePoint (Uri address, IWebProxy proxy)
+ {
+ throw new PlatformNotSupportedException (EXCEPTION_MESSAGE);
+ }
+ }
+}
Data.StatusCode = status;
Data.Challenge = result.GetValues ("Proxy-Authentic");
+ Data.Headers = result;
return false;
} else if (status != 200) {
string msg = String.Format ("The remote server returned a {0} status code.", status);
}
status = (int)UInt32.Parse (parts [1]);
+ if (parts.Length >= 3)
+ Data.StatusDescription = String.Join (" ", parts, 2, parts.Length - 2);
+
gotStatus = true;
}
}
Exception cnc_exc = connect_exception;
if (cnc_exc == null && (Data.StatusCode == 401 || Data.StatusCode == 407)) {
st = WebExceptionStatus.ProtocolError;
- cnc_exc = new WebException (Data.StatusCode == 407 ? "(407) Proxy Authentication Required" : "(401) Unauthorized" , st);
+ if (Data.Headers == null)
+ Data.Headers = new WebHeaderCollection ();
+
+ var webResponse = new HttpWebResponse (sPoint.Address, "CONNECT", Data, null);
+ cnc_exc = new WebException (Data.StatusCode == 407 ? "(407) Proxy Authentication Required" : "(401) Unauthorized", null, st, webResponse);
}
connect_exception = null;
}
return store2;
}
+ internal set {
+ store2 = value;
+ }
}
public X509RevocationFlag RevocationFlag {
#if MONO_SECURITY_ALIAS
using MonoSecurity::Mono.Security.Interface;
#else
+#if !FEATURE_NO_BSD_SOCKETS
using Mono.Security.Interface;
#endif
+#endif
+
+#if !FEATURE_NO_BSD_SOCKETS
+using Mono.Btls;
+#endif
+#endif
+
+using System.IO;
+using System.Text;
namespace System.Security.Cryptography.X509Certificates
{
internal static class X509Helper2
{
+ internal static long GetSubjectNameHash (X509Certificate certificate)
+ {
+ return GetSubjectNameHash (certificate.Impl);
+ }
+
+ internal static long GetSubjectNameHash (X509CertificateImpl impl)
+ {
+#if SECURITY_DEP
+ using (var x509 = GetNativeInstance (impl))
+ return GetSubjectNameHash (x509);
+#else
+ throw new NotSupportedException ();
+#endif
+ }
+
+ internal static void ExportAsPEM (X509Certificate certificate, Stream stream, bool includeHumanReadableForm)
+ {
+ ExportAsPEM (certificate.Impl, stream, includeHumanReadableForm);
+ }
+
+ internal static void ExportAsPEM (X509CertificateImpl impl, Stream stream, bool includeHumanReadableForm)
+ {
+#if SECURITY_DEP
+ using (var x509 = GetNativeInstance (impl))
+ ExportAsPEM (x509, stream, includeHumanReadableForm);
+#else
+ throw new NotSupportedException ();
+#endif
+ }
+
+#if SECURITY_DEP
internal static void Initialize ()
{
X509Helper.InstallNativeHelper (new MyNativeHelper ());
X509Helper.ThrowIfContextInvalid (impl);
}
+#if FEATURE_NO_BSD_SOCKETS
+ static X509Certificate GetNativeInstance (X509CertificateImpl impl)
+ {
+ throw new PlatformNotSupportedException ();
+ }
+#else
+ static MonoBtlsX509 GetNativeInstance (X509CertificateImpl impl)
+ {
+ ThrowIfContextInvalid (impl);
+ var btlsImpl = impl as X509CertificateImplBtls;
+ if (btlsImpl != null)
+ return btlsImpl.X509.Copy ();
+ else
+ return MonoBtlsX509.LoadFromData (impl.GetRawCertData (), MonoBtlsX509Format.DER);
+ }
+
+ internal static long GetSubjectNameHash (MonoBtlsX509 x509)
+ {
+ using (var subject = x509.GetSubjectName ())
+ return subject.GetHash ();
+ }
+
+ internal static void ExportAsPEM (MonoBtlsX509 x509, Stream stream, bool includeHumanReadableForm)
+ {
+ using (var bio = MonoBtlsBio.CreateMonoStream (stream)) {
+ x509.ExportAsPEM (bio, includeHumanReadableForm);
+ }
+ }
+#endif // !FEATURE_NO_BSD_SOCKETS
+
internal static X509Certificate2Impl Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
{
+#if !FEATURE_NO_BSD_SOCKETS
var provider = MonoTlsProviderFactory.GetProvider ();
if (provider.HasNativeCertificates) {
var impl = provider.GetNativeCertificate (rawData, password, keyStorageFlags);
return impl;
- } else {
- var impl = new X509Certificate2ImplMono ();
- impl.Import (rawData, password, keyStorageFlags);
- return impl;
}
+#endif // FEATURE_NO_BSD_SOCKETS
+ var impl2 = new X509Certificate2ImplMono ();
+ impl2.Import (rawData, password, keyStorageFlags);
+ return impl2;
}
internal static X509Certificate2Impl Import (X509Certificate cert)
{
+#if !FEATURE_NO_BSD_SOCKETS
var provider = MonoTlsProviderFactory.GetProvider ();
if (provider.HasNativeCertificates) {
var impl = provider.GetNativeCertificate (cert);
return impl;
}
+#endif // FEATURE_NO_BSD_SOCKETS
var impl2 = cert.Impl as X509Certificate2Impl;
if (impl2 != null)
return (X509Certificate2Impl)impl2.Clone ();
return X509Helper2.Import (cert);
}
}
+#endif
}
}
-#endif
System/IOSelector.cs
+Mono.Btls/MonoBtlsBio.cs
+Mono.Btls/MonoBtlsContext.cs
+Mono.Btls/MonoBtlsError.cs
+Mono.Btls/MonoBtlsException.cs
+Mono.Btls/MonoBtlsKey.cs
+Mono.Btls/MonoBtlsObject.cs
+Mono.Btls/MonoBtlsPkcs12.cs
+Mono.Btls/MonoBtlsProvider.cs
+Mono.Btls/MonoBtlsSsl.cs
+Mono.Btls/MonoBtlsSslCtx.cs
+Mono.Btls/MonoBtlsSslError.cs
+Mono.Btls/MonoBtlsStream.cs
+Mono.Btls/MonoBtlsUtils.cs
+Mono.Btls/MonoBtlsX509.cs
+Mono.Btls/MonoBtlsX509Chain.cs
+Mono.Btls/MonoBtlsX509Crl.cs
+Mono.Btls/MonoBtlsX509Error.cs
+Mono.Btls/MonoBtlsX509Exception.cs
+Mono.Btls/MonoBtlsX509FileType.cs
+Mono.Btls/MonoBtlsX509Format.cs
+Mono.Btls/MonoBtlsX509Lookup.cs
+Mono.Btls/MonoBtlsX509LookupMono.cs
+Mono.Btls/MonoBtlsX509LookupMonoCollection.cs
+Mono.Btls/MonoBtlsX509LookupType.cs
+Mono.Btls/MonoBtlsX509Name.cs
+Mono.Btls/MonoBtlsX509NameList.cs
+Mono.Btls/MonoBtlsX509NameEntryType.cs
+Mono.Btls/MonoBtlsX509Purpose.cs
+Mono.Btls/MonoBtlsX509Revoked.cs
+Mono.Btls/MonoBtlsX509Store.cs
+Mono.Btls/MonoBtlsX509StoreCtx.cs
+Mono.Btls/MonoBtlsX509StoreManager.cs
+Mono.Btls/MonoBtlsX509StoreType.cs
+Mono.Btls/MonoBtlsX509TrustKind.cs
+Mono.Btls/MonoBtlsX509VerifyFlags.cs
+Mono.Btls/MonoBtlsX509VerifyParam.cs
+Mono.Btls/X509CertificateImplBtls.cs
+Mono.Btls/X509ChainImplBtls.cs
+
Mono.Net.Security/AsyncProtocolRequest.cs
Mono.Net.Security/CallbackHelpers.cs
Mono.Net.Security/ChainValidationHelper.cs
Mono.Net.Security/MonoSslStreamImpl.cs
Mono.Net.Security/MonoSslStreamWrapper.cs
Mono.Net.Security/MonoTlsProviderFactory.cs
-Mono.Net.Security/MonoTlsProviderImpl.cs
Mono.Net.Security/MonoTlsProviderWrapper.cs
Mono.Net.Security/MonoTlsStream.cs
Mono.Net.Security/NoReflectionHelper.cs
using System.Security.Cryptography.X509Certificates;
#if SECURITY_DEP
using MSX = Mono.Security.X509;
+using Mono.Btls;
#endif
namespace System {
#if SECURITY_DEP
static readonly Converter<List <byte[]>, bool> trustEvaluateSsl;
+ static readonly Func<long, bool, byte[]> certStoreLookup;
#endif // SECURITY_DEP
static readonly Func<IWebProxy> getDefaultProxy;
static readonly GetInterfaceAddressesDelegate getInterfaceAddresses;
"TrustEvaluateSsl",
ignoreCase:false,
throwOnBindFailure:true);
+ certStoreLookup = (Func<long, bool, byte[]>)
+ Delegate.CreateDelegate (typeof (Func<long, bool, byte[]>),
+ t,
+ "CertStoreLookup",
+ ignoreCase:false,
+ throwOnBindFailure:true);
#endif // SECURITY_DEP
getDefaultProxy = (Func<IWebProxy>)Delegate.CreateDelegate (
typeof (Func<IWebProxy>), t, "GetDefaultProxy",
certsRawData.Add (cert.GetRawCertData ());
return trustEvaluateSsl (certsRawData);
}
+
+ internal static MonoBtlsX509 CertStoreLookup (MonoBtlsX509Name name)
+ {
+ var hash = name.GetHash ();
+ var hashOld = name.GetHashOld ();
+ var result = certStoreLookup (hash, false);
+ if (result == null)
+ result = certStoreLookup (hashOld, false);
+ if (result == null)
+ result = certStoreLookup (hash, true);
+ if (result == null)
+ result = certStoreLookup (hashOld, true);
+
+ if (result == null)
+ return null;
+
+ return MonoBtlsX509.LoadFromData (result, MonoBtlsX509Format.DER);
+ }
#endif // SECURITY_DEP
internal static IWebProxy GetDefaultProxy ()
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (AggregateException))] // Something catches the PlatformNotSupportedException and re-throws an AggregateException
+#endif
public void ConcurrentExceedSocketLimit ()
{
var tasks = new Task[4];
}
[Test]
+#if FEATURE_NO_BSD_SOCKETS
+ [ExpectedException (typeof (PlatformNotSupportedException))]
+#endif
public void SendGenericExceedBuffer ()
{
// Create a buffer larger than the default max.
[SetUp]\r
public void GetReady () \r
{\r
+#if !FEATURE_NO_BSD_SOCKETS\r
maxIdle = ServicePointManager.MaxServicePointIdleTime;\r
ServicePointManager.MaxServicePointIdleTime = 10;\r
+#endif\r
googleUri = new Uri ("http://www.google.com");\r
yahooUri = new Uri ("http://www.yahoo.com");\r
apacheUri = new Uri ("http://www.apache.org");\r
[TearDown]\r
public void Finish ()\r
{\r
+#if !FEATURE_NO_BSD_SOCKETS\r
ServicePointManager.MaxServicePointIdleTime = maxIdle;\r
+#endif\r
}\r
\r
[Test, ExpectedException (typeof (InvalidOperationException))]\r
}\r
\r
[Test]\r
+#if FEATURE_NO_BSD_SOCKETS\r
+ [ExpectedException (typeof (PlatformNotSupportedException))]\r
+#endif\r
public void FindServicePoint ()\r
{\r
ServicePointManager.MaxServicePoints = 0;\r
public class ServicePointTest\r
{\r
static private int max;\r
+\r
+#if !FEATURE_NO_BSD_SOCKETS\r
[SetUp]\r
public void SaveMax () {\r
max = ServicePointManager.MaxServicePoints;\r
public void RestoreMax () {\r
ServicePointManager.MaxServicePoints = max;\r
}\r
+#endif\r
\r
[Test]\r
[Category ("InetAccess")]\r
Mono.Net.Security/MonoSslStreamImpl.cs
Mono.Net.Security/MonoSslStreamWrapper.cs
Mono.Net.Security/MonoTlsProviderFactory.cs
-Mono.Net.Security/MonoTlsProviderImpl.cs
Mono.Net.Security/MonoTlsProviderWrapper.cs
Mono.Net.Security/MonoTlsStream.cs
Mono.Net.Security/NoReflectionHelper.cs
Mono.Net.Security/SystemCertificateValidator.cs
+Mono.Btls/MonoBtlsBio.cs
+Mono.Btls/MonoBtlsContext.cs
+Mono.Btls/MonoBtlsError.cs
+Mono.Btls/MonoBtlsException.cs
+Mono.Btls/MonoBtlsKey.cs
+Mono.Btls/MonoBtlsObject.cs
+Mono.Btls/MonoBtlsPkcs12.cs
+Mono.Btls/MonoBtlsProvider.cs
+Mono.Btls/MonoBtlsSsl.cs
+Mono.Btls/MonoBtlsSslCtx.cs
+Mono.Btls/MonoBtlsSslError.cs
+Mono.Btls/MonoBtlsStream.cs
+Mono.Btls/MonoBtlsUtils.cs
+Mono.Btls/MonoBtlsX509.cs
+Mono.Btls/MonoBtlsX509Chain.cs
+Mono.Btls/MonoBtlsX509Crl.cs
+Mono.Btls/MonoBtlsX509Error.cs
+Mono.Btls/MonoBtlsX509Exception.cs
+Mono.Btls/MonoBtlsX509FileType.cs
+Mono.Btls/MonoBtlsX509Format.cs
+Mono.Btls/MonoBtlsX509Lookup.cs
+Mono.Btls/MonoBtlsX509LookupMonoCollection.cs
+Mono.Btls/MonoBtlsX509LookupMono.cs
+Mono.Btls/MonoBtlsX509LookupType.cs
+Mono.Btls/MonoBtlsX509Name.cs
+Mono.Btls/MonoBtlsX509NameList.cs
+Mono.Btls/MonoBtlsX509NameEntryType.cs
+Mono.Btls/MonoBtlsX509Purpose.cs
+Mono.Btls/MonoBtlsX509Revoked.cs
+Mono.Btls/MonoBtlsX509Store.cs
+Mono.Btls/MonoBtlsX509StoreCtx.cs
+Mono.Btls/MonoBtlsX509StoreManager.cs
+Mono.Btls/MonoBtlsX509StoreType.cs
+Mono.Btls/MonoBtlsX509TrustKind.cs
+Mono.Btls/MonoBtlsX509VerifyFlags.cs
+Mono.Btls/MonoBtlsX509VerifyParam.cs
+Mono.Btls/X509CertificateImplBtls.cs
+Mono.Btls/X509ChainImplBtls.cs
+
ReferenceSources/AutoWebProxyScriptEngine.cs
ReferenceSources/AssertWrapper.cs
ReferenceSources/CAPI.cs
../Mono.Security/Mono.Security.Interface/HashAlgorithmType.cs
../Mono.Security/Mono.Security.Interface/IBufferOffsetSize.cs
../Mono.Security/Mono.Security.Interface/IMonoTlsEventSink.cs
-../Mono.Security/Mono.Security.Interface/IMonoTlsContext.cs
../Mono.Security/Mono.Security.Interface/IMonoSslStream.cs
../Mono.Security/Mono.Security.Interface/MonoTlsConnectionInfo.cs
../Mono.Security/Mono.Security.Interface/MonoTlsProvider.cs
#include mobile_System.dll.sources
System/AndroidPlatform.cs
Mono.Net.Security/MonoTlsProviderFactory.Droid.cs
+Mono.Btls/MonoBtlsX509LookupAndroid.cs
+../Mono.Security/Mono.Security.Authenticode/PrivateKey.cs
+../Mono.Security/Mono.Security.Cryptography/MD5SHA1.cs
+../Mono.Security/Mono.Security.Cryptography/TlsHMAC.cs
+../Mono.Security/Mono.Security.Interface/Alert.cs
+../Mono.Security/Mono.Security.Interface/CertificateValidationHelper.cs
+../Mono.Security/Mono.Security.Interface/CipherAlgorithmType.cs
+../Mono.Security/Mono.Security.Interface/CipherSuiteCode.cs
+../Mono.Security/Mono.Security.Interface/ExchangeAlgorithmType.cs
+../Mono.Security/Mono.Security.Interface/HashAlgorithmType.cs
+../Mono.Security/Mono.Security.Interface/IBufferOffsetSize.cs
+../Mono.Security/Mono.Security.Interface/IMonoSslStream.cs
+../Mono.Security/Mono.Security.Interface/IMonoTlsContext.cs
+../Mono.Security/Mono.Security.Interface/IMonoTlsEventSink.cs
+../Mono.Security/Mono.Security.Interface/MonoTlsConnectionInfo.cs
+../Mono.Security/Mono.Security.Interface/MonoTlsProvider.cs
+../Mono.Security/Mono.Security.Interface/MonoTlsProviderFactory.Apple.cs
+../Mono.Security/Mono.Security.Interface/MonoTlsProviderFactory.cs
+../Mono.Security/Mono.Security.Interface/MonoTlsSettings.cs
+../Mono.Security/Mono.Security.Interface/TlsException.cs
+../Mono.Security/Mono.Security.Interface/TlsProtocolCode.cs
+../Mono.Security/Mono.Security.Interface/TlsProtocols.cs
+../Mono.Security/Mono.Security.Protocol.Ntlm/ChallengeResponse.cs
+../Mono.Security/Mono.Security.Protocol.Ntlm/ChallengeResponse2.cs
+../Mono.Security/Mono.Security.Protocol.Ntlm/MessageBase.cs
+../Mono.Security/Mono.Security.Protocol.Ntlm/NtlmAuthLevel.cs
+../Mono.Security/Mono.Security.Protocol.Ntlm/NtlmFlags.cs
+../Mono.Security/Mono.Security.Protocol.Ntlm/NtlmSettings.cs
+../Mono.Security/Mono.Security.Protocol.Ntlm/Type1Message.cs
+../Mono.Security/Mono.Security.Protocol.Ntlm/Type2Message.cs
+../Mono.Security/Mono.Security.Protocol.Ntlm/Type3Message.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsClientCertificate.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsClientCertificateVerify.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsClientFinished.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsClientHello.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsClientKeyExchange.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsServerCertificate.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsServerCertificateRequest.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsServerFinished.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsServerHello.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsServerHelloDone.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsServerKeyExchange.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsClientCertificate.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsClientCertificateVerify.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsClientFinished.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsClientHello.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsClientKeyExchange.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsServerCertificate.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsServerCertificateRequest.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsServerFinished.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsServerHello.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsServerHelloDone.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsServerKeyExchange.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake/ClientCertificateType.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake/HandshakeMessage.cs
+../Mono.Security/Mono.Security.Protocol.Tls.Handshake/HandshakeType.cs
+../Mono.Security/Mono.Security.Protocol.Tls/Alert.cs
+../Mono.Security/Mono.Security.Protocol.Tls/CipherAlgorithmType.cs
+../Mono.Security/Mono.Security.Protocol.Tls/CipherSuite.cs
+../Mono.Security/Mono.Security.Protocol.Tls/CipherSuiteCollection.cs
+../Mono.Security/Mono.Security.Protocol.Tls/CipherSuiteFactory.cs
+../Mono.Security/Mono.Security.Protocol.Tls/ClientContext.cs
+../Mono.Security/Mono.Security.Protocol.Tls/ClientRecordProtocol.cs
+../Mono.Security/Mono.Security.Protocol.Tls/ClientSessionCache.cs
+../Mono.Security/Mono.Security.Protocol.Tls/ContentType.cs
+../Mono.Security/Mono.Security.Protocol.Tls/Context.cs
+../Mono.Security/Mono.Security.Protocol.Tls/DebugHelper.cs
+../Mono.Security/Mono.Security.Protocol.Tls/ExchangeAlgorithmType.cs
+../Mono.Security/Mono.Security.Protocol.Tls/HandshakeState.cs
+../Mono.Security/Mono.Security.Protocol.Tls/HashAlgorithmType.cs
+../Mono.Security/Mono.Security.Protocol.Tls/HttpsClientStream.cs
+../Mono.Security/Mono.Security.Protocol.Tls/RecordProtocol.cs
+../Mono.Security/Mono.Security.Protocol.Tls/RSASslSignatureDeformatter.cs
+../Mono.Security/Mono.Security.Protocol.Tls/RSASslSignatureFormatter.cs
+../Mono.Security/Mono.Security.Protocol.Tls/SecurityCompressionType.cs
+../Mono.Security/Mono.Security.Protocol.Tls/SecurityParameters.cs
+../Mono.Security/Mono.Security.Protocol.Tls/SecurityProtocolType.cs
+../Mono.Security/Mono.Security.Protocol.Tls/ServerContext.cs
+../Mono.Security/Mono.Security.Protocol.Tls/ServerRecordProtocol.cs
+../Mono.Security/Mono.Security.Protocol.Tls/SslCipherSuite.cs
+../Mono.Security/Mono.Security.Protocol.Tls/SslClientStream.cs
+../Mono.Security/Mono.Security.Protocol.Tls/SslHandshakeHash.cs
+../Mono.Security/Mono.Security.Protocol.Tls/SslServerStream.cs
+../Mono.Security/Mono.Security.Protocol.Tls/SslStreamBase.cs
+../Mono.Security/Mono.Security.Protocol.Tls/TlsCipherSuite.cs
+../Mono.Security/Mono.Security.Protocol.Tls/TlsClientSettings.cs
+../Mono.Security/Mono.Security.Protocol.Tls/TlsException.cs
+../Mono.Security/Mono.Security.Protocol.Tls/TlsServerSettings.cs
+../Mono.Security/Mono.Security.Protocol.Tls/TlsStream.cs
+Mono.Http/NtlmClient.cs
+Mono.Net.Security/AsyncProtocolRequest.cs
+Mono.Net.Security/CallbackHelpers.cs
+Mono.Net.Security/ChainValidationHelper.cs
+Mono.Net.Security/IMonoSslStream.cs
+Mono.Net.Security/IMonoTlsProvider.cs
Mono.Net.Security/LegacySslStream.cs
+Mono.Net.Security/LegacySslStream.cs
+Mono.Net.Security/LegacyTlsProvider.cs
Mono.Net.Security/LegacyTlsProvider.cs
+Mono.Net.Security/MobileAuthenticatedStream.cs
+Mono.Net.Security/MobileTlsContext.cs
+Mono.Net.Security/MonoSslStreamImpl.cs
+Mono.Net.Security/MonoSslStreamWrapper.cs
+Mono.Net.Security/MonoTlsProviderFactory.Apple.cs
+Mono.Net.Security/MonoTlsProviderFactory.cs
+Mono.Net.Security/MonoTlsProviderImpl.cs
+Mono.Net.Security/MonoTlsProviderWrapper.cs
+Mono.Net.Security/MonoTlsStream.cs
+Mono.Net.Security/NoReflectionHelper.cs
+Mono.Net.Security/SystemCertificateValidator.cs
System.Net.Mail/SmtpClient.cs
+System.Net.Security/SslStream.cs
System.Net.Sockets/TcpClient.cs
System.Net.Sockets/TcpListener.cs
System.Net.Sockets/UdpClient.cs
System.Net.WebSockets/ClientWebSocket.cs
+System.Net/AuthenticationManager.cs
System.Net/ChunkedInputStream.cs
System.Net/EndPointListener.cs
System.Net/EndPointManager.cs
System.Net/HttpListenerResponse.cs
System.Net/HttpWebRequest.cs
System.Net/HttpWebResponse.cs
+System.Net/IWebConnectionState.cs
System.Net/ListenerAsyncResult.cs
+System.Net/NtlmClient.cs
System.Net/ResponseStream.cs
+System.Net/ServicePoint.cs
+System.Net/ServicePointManager.cs
+System.Net/WebConnection.cs
+System.Net/WebConnectionData.cs
+System.Net/WebConnectionGroup.cs
+System.Net/WebConnectionStream.cs
+Mono.Btls/MonoBtlsBio.cs
+Mono.Btls/MonoBtlsContext.cs
+Mono.Btls/MonoBtlsError.cs
+Mono.Btls/MonoBtlsException.cs
+Mono.Btls/MonoBtlsKey.cs
+Mono.Btls/MonoBtlsObject.cs
+Mono.Btls/MonoBtlsPkcs12.cs
+Mono.Btls/MonoBtlsProvider.cs
+Mono.Btls/MonoBtlsSsl.cs
+Mono.Btls/MonoBtlsSslCtx.cs
+Mono.Btls/MonoBtlsSslError.cs
+Mono.Btls/MonoBtlsStream.cs
+Mono.Btls/MonoBtlsUtils.cs
+Mono.Btls/MonoBtlsX509.cs
+Mono.Btls/MonoBtlsX509Chain.cs
+Mono.Btls/MonoBtlsX509Crl.cs
+Mono.Btls/MonoBtlsX509Error.cs
+Mono.Btls/MonoBtlsX509Exception.cs
+Mono.Btls/MonoBtlsX509FileType.cs
+Mono.Btls/MonoBtlsX509Format.cs
+Mono.Btls/MonoBtlsX509Lookup.cs
+Mono.Btls/MonoBtlsX509LookupMonoCollection.cs
+Mono.Btls/MonoBtlsX509LookupMono.cs
+Mono.Btls/MonoBtlsX509LookupType.cs
+Mono.Btls/MonoBtlsX509Name.cs
+Mono.Btls/MonoBtlsX509NameList.cs
+Mono.Btls/MonoBtlsX509NameEntryType.cs
+Mono.Btls/MonoBtlsX509Purpose.cs
+Mono.Btls/MonoBtlsX509Revoked.cs
+Mono.Btls/MonoBtlsX509Store.cs
+Mono.Btls/MonoBtlsX509StoreCtx.cs
+Mono.Btls/MonoBtlsX509StoreManager.cs
+Mono.Btls/MonoBtlsX509StoreType.cs
+Mono.Btls/MonoBtlsX509TrustKind.cs
+Mono.Btls/MonoBtlsX509VerifyFlags.cs
+Mono.Btls/MonoBtlsX509VerifyParam.cs
+Mono.Btls/X509CertificateImplBtls.cs
+Mono.Btls/X509ChainImplBtls.cs
#include monotouch_System.dll.sources
System.Net.Mail/SmtpClient.platformnotsupported.cs
+System.Net.Security/SslStream.platformnotsupported.cs
System.Net.Sockets/TcpClient.platformnotsupported.cs
System.Net.Sockets/TcpListener.platformnotsupported.cs
System.Net.Sockets/UdpClient.platformnotsupported.cs
System.Net.WebSockets/ClientWebSocket.platformnotsupported.cs
+System.Net/AuthenticationManager.platformnotsupported.cs
System.Net/FtpRequestCreator.platformnotsupported.cs
System.Net/FtpWebRequest.platformnotsupported.cs
System.Net/FtpWebResponse.platformnotsupported.cs
System.Net/HttpListenerResponse.platformnotsupported.cs
System.Net/HttpWebRequest.platformnotsupported.cs
System.Net/HttpWebResponse.platformnotsupported.cs
+System.Net/ServicePoint.platformnotsupported.cs
+System.Net/ServicePointManager.platformnotsupported.cs
--- /dev/null
+#include monotouch_watch_System.dll.exclude.sources
-#include monotouch_System.dll.sources
+#include monotouch_watch_System.dll.sources
[assembly: InternalsVisibleTo ("Xamarin.TVOS, PublicKey=0024000004800000940000000602000000240000525341310004000011000000438ac2a5acfbf16cbd2b2b47a62762f273df9cb2795ceccdf77d10bf508e69e7a362ea7a45455bbf3ac955e1f2e2814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b2c9733db")]
#elif MONOTOUCH_WATCH
[assembly: InternalsVisibleTo ("Xamarin.WatchOS, PublicKey=0024000004800000940000000602000000240000525341310004000011000000438ac2a5acfbf16cbd2b2b47a62762f273df9cb2795ceccdf77d10bf508e69e7a362ea7a45455bbf3ac955e1f2e2814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b2c9733db")]
+[assembly: InternalsVisibleTo ("System.Security, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]
#else
[assembly: InternalsVisibleTo ("monotouch, PublicKey=0024000004800000940000000602000000240000525341310004000011000000438ac2a5acfbf16cbd2b2b47a62762f273df9cb2795ceccdf77d10bf508e69e7a362ea7a45455bbf3ac955e1f2e2814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b2c9733db")]
[assembly: InternalsVisibleTo ("Xamarin.iOS, PublicKey=0024000004800000940000000602000000240000525341310004000011000000438ac2a5acfbf16cbd2b2b47a62762f273df9cb2795ceccdf77d10bf508e69e7a362ea7a45455bbf3ac955e1f2e2814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b2c9733db")]
namespace Mono {
-#if MOBILE
+#if MOBILE || XAMMAC_4_5
public
#endif
static class Runtime
[MethodImplAttribute (MethodImplOptions.InternalCall)]
private static extern void mono_runtime_install_handlers ();
-#if MOBILE
+#if MOBILE || XAMMAC_4_5
public
#else
internal
mono_runtime_install_handlers ();
}
-#if MOBILE
+#if MOBILE || XAMMAC_4_5
[MethodImplAttribute (MethodImplOptions.InternalCall)]
static extern void mono_runtime_cleanup_handlers ();
// Safe to be called using reflection
// Format is undefined only for use as a string for reporting
[MethodImplAttribute (MethodImplOptions.InternalCall)]
-#if MOBILE
+#if MOBILE || XAMMAC_4_5
public
#else
internal
return UnderlyingSystemType;
}
+ // Called from the runtime to return the corresponding finished Type object
+ internal virtual Type RuntimeResolve ()
+ {
+ throw new NotImplementedException ();
+ }
+
internal virtual bool IsUserType {
get {
return true;
+++ /dev/null
-namespace System.Reflection.Emit
-{
- abstract class TypeBuilderInstantiation : TypeInfo
- {
- internal static Type MakeGenericType (Type type, Type[] typeArguments)
- {
-#if FULL_AOT_RUNTIME
- throw new NotSupportedException ("User types are not supported under full aot");
-#else
- return new MonoGenericClass (type, typeArguments);
-#endif
- }
- }
-}
\ No newline at end of file
{
}
+ [CLSCompliant (false)]
public void Write<T> (string eventName, ref EventSourceOptions options, ref T data)
{
}
/*Warning, @typeArguments must be a mscorlib internal array. So make a copy before passing it in*/
internal Type MakeGenericType (Type gtd, Type[] typeArguments)
{
- return new MonoGenericClass (gtd, typeArguments);
+ return new TypeBuilderInstantiation (gtd, typeArguments);
}
void _AssemblyBuilder.GetIDsOfNames([In] ref Guid riid, IntPtr rgszNames, uint cNames, uint lcid, IntPtr rgDispId)
internal override Type GetParameterType (int pos) {
return parameters [pos];
}
-
+
+ internal MethodBase RuntimeResolve () {
+ return type.RuntimeResolve ().GetConstructor (this);
+ }
+
public override Object Invoke (Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
{
throw not_supported ();
TypeBuilder.ResolveUserTypes (types);
}
}
+
+ internal void FixupTokens (Dictionary<int, int> token_map, Dictionary<int, MemberInfo> member_map) {
+ if (ilgen != null)
+ ilgen.FixupTokens (token_map, member_map);
+ }
internal void GenerateDebugInfo (ISymbolWriter symbolWriter)
{
internal class ConstructorOnTypeBuilderInst : ConstructorInfo
{
#region Keep in sync with object-internals.h
- MonoGenericClass instantiation;
- ConstructorInfo cb;
+ internal TypeBuilderInstantiation instantiation;
+ internal ConstructorInfo cb;
#endregion
- public ConstructorOnTypeBuilderInst (MonoGenericClass instantiation, ConstructorInfo cb)
+ public ConstructorOnTypeBuilderInst (TypeBuilderInstantiation instantiation, ConstructorInfo cb)
{
this.instantiation = instantiation;
this.cb = cb;
return res;
}
+ internal override Type[] GetParameterTypes () {
+ if (cb is ConstructorBuilder) {
+ return (cb as ConstructorBuilder).parameters;
+ } else {
+ ParameterInfo[] parms = cb.GetParameters ();
+ var res = new Type [parms.Length];
+ for (int i = 0; i < parms.Length; i++) {
+ res [i] = parms [i].ParameterType;
+ }
+ return res;
+ }
+ }
+
+ // Called from the runtime to return the corresponding finished ConstructorInfo object
+ internal ConstructorInfo RuntimeResolve () {
+ var type = instantiation.InternalResolve ();
+ return type.GetConstructor (cb);
+ }
+
public override int MetadataToken {
get {
return base.MetadataToken;
{
internal Type m_baseType;
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- static extern void create_unmanaged_type (Type type);
-
internal SymbolType (Type elementType)
{
this.m_baseType = elementType;
public override Type UnderlyingSystemType {
get {
- create_unmanaged_type (this);
return this;
}
}
return m_baseType.IsUserType;
}
}
+
+ // Called from the runtime to return the corresponding finished Type object
+ internal override Type RuntimeResolve () {
+ return InternalResolve ();
+ }
}
[StructLayout (LayoutKind.Sequential)]
internal override Type InternalResolve ()
{
- Type et = m_baseType.InternalResolve ();
+ Type et = m_baseType.InternalResolve ();
+ if (rank == 0)
+ return et.MakeArrayType ();
+ return et.MakeArrayType (rank);
+ }
+
+ internal override Type RuntimeResolve ()
+ {
+ Type et = m_baseType.RuntimeResolve ();
if (rank == 0)
- return et.MakeArrayType ();
+ return et.MakeArrayType ();
return et.MakeArrayType (rank);
}
[StructLayout (LayoutKind.Sequential)]
internal class EventOnTypeBuilderInst : EventInfo
{
- MonoGenericClass instantiation;
+ TypeBuilderInstantiation instantiation;
EventBuilder event_builder;
EventInfo event_info;
- internal EventOnTypeBuilderInst (MonoGenericClass instantiation, EventBuilder evt)
+ internal EventOnTypeBuilderInst (TypeBuilderInstantiation instantiation, EventBuilder evt)
{
this.instantiation = instantiation;
this.event_builder = evt;
}
- internal EventOnTypeBuilderInst (MonoGenericClass instantiation, EventInfo evt)
+ internal EventOnTypeBuilderInst (TypeBuilderInstantiation instantiation, EventInfo evt)
{
this.instantiation = instantiation;
this.event_info = evt;
marshal_info.marshaltyperef = TypeBuilder.ResolveUserType (marshal_info.marshaltyperef);
}
+ internal FieldInfo RuntimeResolve () {
+ return typeb.CreateType ().GetField (this);
+ }
+
public override Module Module {
get {
return base.Module;
internal class FieldOnTypeBuilderInst : FieldInfo
{
#region Keep in sync with object-internals.h
- internal MonoGenericClass instantiation;
+ internal TypeBuilderInstantiation instantiation;
internal FieldInfo fb;
#endregion
- public FieldOnTypeBuilderInst (MonoGenericClass instantiation, FieldInfo fb) {
+ public FieldOnTypeBuilderInst (TypeBuilderInstantiation instantiation, FieldInfo fb) {
this.instantiation = instantiation;
this.fb = fb;
}
public override void SetValue (object obj, object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture) {
throw new NotSupportedException ();
}
+
+ // Called from the runtime to return the corresponding finished FieldInfo object
+ internal FieldInfo RuntimeResolve () {
+ var type = instantiation.RuntimeResolve ();
+ return type.GetField (fb);
+ }
}
}
#endif
this.mbuilder = mbuilder;
this.name = name;
this.index = index;
-
- initialize ();
}
internal override Type InternalResolve ()
{
- return tbuilder.InternalResolve ().GetGenericArguments () [index];
+ if (mbuilder != null)
+ return MethodBase.GetMethodFromHandle (mbuilder.MethodHandleInternal).GetGenericArguments () [index];
+ return tbuilder.InternalResolve ().GetGenericArguments () [index];
}
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- private extern void initialize ();
+ internal override Type RuntimeResolve ()
+ {
+ if (mbuilder != null)
+ return MethodBase.GetMethodFromHandle (mbuilder.MethodHandleInternal, mbuilder.TypeBuilder.RuntimeResolve ().TypeHandle).GetGenericArguments () [index];
+ return tbuilder.RuntimeResolve ().GetGenericArguments () [index];
+ }
[ComVisible (true)]
public override bool IsSubclassOf (Type c)
int token = token_gen.GetToken (con, true);
make_room (6);
ll_emit (opcode);
- if (con.DeclaringType.Module == module)
+ if (con.DeclaringType.Module == module || (con is ConstructorOnTypeBuilderInst) || (con is ConstructorBuilder))
add_token_fixup (con);
emit_int (token);
int token = token_gen.GetToken (field, true);
make_room (6);
ll_emit (opcode);
- if (field.DeclaringType.Module == module)
+ if (field.DeclaringType.Module == module || (field is FieldOnTypeBuilderInst) || (field is FieldBuilder))
add_token_fixup (field);
emit_int (token);
}
Type declaringType = meth.DeclaringType;
// Might be a DynamicMethod with no declaring type
if (declaringType != null) {
- if (declaringType.Module == module)
+ if (declaringType.Module == module || meth is MethodOnTypeBuilderInst || meth is MethodBuilder)
add_token_fixup (meth);
}
emit_int (token);
// Might be a DynamicMethod with no declaring type
Type declaringType = method.DeclaringType;
if (declaringType != null) {
- if (declaringType.Module == module)
+ if (declaringType.Module == module || method is MethodBuilder)
add_token_fixup (method);
}
emit_int (token);
make_room (6);
ll_emit (opcode);
- emit_int (token_gen.GetToken (cls, opcode != OpCodes.Ldtoken));
+ int token = token_gen.GetToken (cls, opcode != OpCodes.Ldtoken);
+ if (cls is TypeBuilderInstantiation || cls is SymbolType || cls is TypeBuilder || cls is GenericTypeParameterBuilder)
+ add_token_fixup (cls);
+ emit_int (token);
}
[MonoLimitation ("vararg methods are not supported")]
}
}
+ internal void FixupTokens (Dictionary<int, int> token_map, Dictionary<int, MemberInfo> member_map) {
+ for (int i = 0; i < num_token_fixups; ++i) {
+ int pos = token_fixups [i].code_pos;
+ int old_token = code [pos] | (code [pos + 1] << 8) | (code [pos + 2] << 16) | (code [pos + 3] << 24);
+ int new_token;
+ if (token_map.TryGetValue (old_token, out new_token)) {
+ token_fixups [i].member = member_map [old_token];
+ int old_cl = code_len;
+ code_len = pos;
+ emit_int (new_token);
+ code_len = old_cl;
+ }
+ }
+ }
+
// Used by MethodBuilder.SetMethodBody
internal void SetExceptionHandlers (ILExceptionInfo[] exHandlers) {
this.ex_handlers = exHandlers;
}
}
+ internal RuntimeMethodHandle MethodHandleInternal {
+ get {
+ return mhandle;
+ }
+ }
+
public override Type ReturnType {
get { return rtype; }
}
return parameters [pos];
}
+ internal MethodBase RuntimeResolve () {
+ return type.RuntimeResolve ().GetMethod (this);
+ }
+
public Module GetModule ()
{
return type.Module;
}
}
+ internal void FixupTokens (Dictionary<int, int> token_map, Dictionary<int, MemberInfo> member_map) {
+ if (ilgen != null)
+ ilgen.FixupTokens (token_map, member_map);
+ }
+
internal void GenerateDebugInfo (ISymbolWriter symbolWriter)
{
if (ilgen != null && ilgen.HasDebugInfo) {
#endregion
MethodInfo generic_method_definition;
- public MethodOnTypeBuilderInst (MonoGenericClass instantiation, MethodInfo base_method)
+ public MethodOnTypeBuilderInst (TypeBuilderInstantiation instantiation, MethodInfo base_method)
{
this.instantiation = instantiation;
this.base_method = base_method;
return instantiation.GetGenericArguments ();
}
+ // Called from the runtime to return the corresponding finished MethodInfo object
+ internal MethodInfo RuntimeResolve () {
+ var type = instantiation.InternalResolve ();
+ var m = type.GetMethod (base_method);
+ if (method_arguments != null) {
+ var args = new Type [method_arguments.Length];
+ for (int i = 0; i < method_arguments.Length; ++i)
+ args [i] = method_arguments [i].InternalResolve ();
+ m = m.MakeGenericMethod (args);
+ }
+ return m;
+ }
+
//
// MemberInfo members
//
return result;
}
+ static int typeref_tokengen = 0x01ffffff;
+ static int typedef_tokengen = 0x02ffffff;
+ static int typespec_tokengen = 0x1bffffff;
+ static int memberref_tokengen = 0x0affffff;
+ static int methoddef_tokengen = 0x06ffffff;
+ Dictionary<MemberInfo, int> inst_tokens = new Dictionary<MemberInfo, int> ();
+ Dictionary<MemberInfo, int> inst_tokens_open = new Dictionary<MemberInfo, int> ();
+
+ //
+ // Assign a pseudo token to the various TypeBuilderInst objects, so the runtime
+ // doesn't have to deal with them.
+ // For Save assemblies, the tokens will be fixed up later during Save ().
+ // For Run assemblies, the tokens will not be fixed up, so the runtime will
+ // still encounter these objects, it will resolve them by calling their
+ // RuntimeResolve () methods.
+ //
+ int GetPseudoToken (MemberInfo member, bool create_open_instance) {
+ int token;
+
+ if (create_open_instance) {
+ if (inst_tokens_open.TryGetValue (member, out token))
+ return token;
+ } else {
+ if (inst_tokens.TryGetValue (member, out token))
+ return token;
+ }
+ // Count backwards to avoid collisions with the tokens
+ // allocated by the runtime
+ if (member is TypeBuilderInstantiation || member is SymbolType)
+ token = typespec_tokengen --;
+ else if (member is FieldOnTypeBuilderInst)
+ token = memberref_tokengen --;
+ else if (member is ConstructorOnTypeBuilderInst)
+ token = memberref_tokengen --;
+ else if (member is MethodOnTypeBuilderInst)
+ token = memberref_tokengen --;
+ else if (member is FieldBuilder)
+ token = memberref_tokengen --;
+ else if (member is TypeBuilder) {
+ if (create_open_instance && (member as TypeBuilder).ContainsGenericParameters)
+ token = typespec_tokengen --;
+ else if (member.Module == this)
+ token = typedef_tokengen --;
+ else
+ token = typeref_tokengen --;
+ } else if (member is ConstructorBuilder) {
+ if (member.Module == this && !(member as ConstructorBuilder).TypeBuilder.ContainsGenericParameters)
+ token = methoddef_tokengen --;
+ else
+ token = memberref_tokengen --;
+ } else if (member is MethodBuilder) {
+ var mb = member as MethodBuilder;
+ if (member.Module == this && !mb.TypeBuilder.ContainsGenericParameters && !mb.IsGenericMethodDefinition)
+ token = methoddef_tokengen --;
+ else
+ token = memberref_tokengen --;
+ } else if (member is GenericTypeParameterBuilder) {
+ token = typespec_tokengen --;
+ } else
+ throw new NotImplementedException ();
+ if (create_open_instance)
+ inst_tokens_open [member] = token;
+ else
+ inst_tokens [member] = token;
+ RegisterToken (member, token);
+ return token;
+ }
+
internal int GetToken (MemberInfo member) {
+ if (member is ConstructorBuilder || member is MethodBuilder)
+ return GetPseudoToken (member, false);
return getToken (this, member, true);
}
internal int GetToken (MemberInfo member, bool create_open_instance) {
+ if (member is TypeBuilderInstantiation || member is FieldOnTypeBuilderInst || member is ConstructorOnTypeBuilderInst || member is MethodOnTypeBuilderInst || member is SymbolType || member is FieldBuilder || member is TypeBuilder || member is ConstructorBuilder || member is MethodBuilder || member is GenericTypeParameterBuilder)
+ return GetPseudoToken (member, create_open_instance);
return getToken (this, member, create_open_instance);
}
internal int GetToken (MethodBase method, IEnumerable<Type> opt_param_types) {
+ if (method is ConstructorBuilder || method is MethodBuilder)
+ return GetPseudoToken (method, false);
+
if (opt_param_types == null)
return getToken (this, method, true);
}
internal int GetToken (MethodBase method, Type[] opt_param_types) {
+ if (method is ConstructorBuilder || method is MethodBuilder)
+ return GetPseudoToken (method, false);
return getMethodToken (this, method, opt_param_types);
}
return token_gen;
}
+ // Called from the runtime to return the corresponding finished reflection object
+ internal static object RuntimeResolve (object obj) {
+ if (obj is MethodBuilder)
+ return (obj as MethodBuilder).RuntimeResolve ();
+ if (obj is ConstructorBuilder)
+ return (obj as ConstructorBuilder).RuntimeResolve ();
+ if (obj is FieldBuilder)
+ return (obj as FieldBuilder).RuntimeResolve ();
+ if (obj is GenericTypeParameterBuilder)
+ return (obj as GenericTypeParameterBuilder).RuntimeResolve ();
+ if (obj is FieldOnTypeBuilderInst)
+ return (obj as FieldOnTypeBuilderInst).RuntimeResolve ();
+ if (obj is MethodOnTypeBuilderInst)
+ return (obj as MethodOnTypeBuilderInst).RuntimeResolve ();
+ if (obj is ConstructorOnTypeBuilderInst)
+ return (obj as ConstructorOnTypeBuilderInst).RuntimeResolve ();
+ if (obj is Type)
+ return (obj as Type).RuntimeResolve ();
+ throw new NotImplementedException (obj.GetType ().FullName);
+ }
+
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern void build_metadata (ModuleBuilder mb);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private extern void WriteToFile (IntPtr handle);
+ void FixupTokens (Dictionary<int, int> token_map, Dictionary<int, MemberInfo> member_map, Dictionary<MemberInfo, int> inst_tokens,
+ bool open) {
+ foreach (var v in inst_tokens) {
+ var member = v.Key;
+ var old_token = v.Value;
+ MemberInfo finished = null;
+
+ // Construct the concrete reflection object corresponding to the
+ // TypeBuilderInst object, and request a token for it instead.
+ if (member is TypeBuilderInstantiation || member is SymbolType) {
+ finished = (member as Type).RuntimeResolve ();
+ } else if (member is FieldOnTypeBuilderInst) {
+ finished = (member as FieldOnTypeBuilderInst).RuntimeResolve ();
+ } else if (member is ConstructorOnTypeBuilderInst) {
+ finished = (member as ConstructorOnTypeBuilderInst).RuntimeResolve ();
+ } else if (member is MethodOnTypeBuilderInst) {
+ finished = (member as MethodOnTypeBuilderInst).RuntimeResolve ();
+ } else if (member is FieldBuilder) {
+ finished = (member as FieldBuilder).RuntimeResolve ();
+ } else if (member is TypeBuilder) {
+ finished = (member as TypeBuilder).RuntimeResolve ();
+ } else if (member is ConstructorBuilder) {
+ finished = (member as ConstructorBuilder).RuntimeResolve ();
+ } else if (member is MethodBuilder) {
+ finished = (member as MethodBuilder).RuntimeResolve ();
+ } else if (member is GenericTypeParameterBuilder) {
+ finished = (member as GenericTypeParameterBuilder).RuntimeResolve ();
+ } else {
+ throw new NotImplementedException ();
+ }
+
+ int new_token = GetToken (finished, open);
+ token_map [old_token] = new_token;
+ member_map [old_token] = finished;
+ // Replace the token mapping in the runtime so it points to the new object
+ RegisterToken (finished, old_token);
+ }
+ }
+
+ //
+ // Fixup the pseudo tokens assigned to the various SRE objects
+ //
+ void FixupTokens () {
+ var token_map = new Dictionary<int, int> ();
+ var member_map = new Dictionary<int, MemberInfo> ();
+ FixupTokens (token_map, member_map, inst_tokens, false);
+ FixupTokens (token_map, member_map, inst_tokens_open, true);
+
+ // Replace the tokens in the IL stream
+ if (types != null) {
+ for (int i = 0; i < num_types; ++i)
+ types [i].FixupTokens (token_map, member_map);
+ }
+ }
+
internal void Save ()
{
if (transient && !is_main)
throw new NotSupportedException ("Type '" + types [i].FullName + "' was not completed.");
}
+ FixupTokens ();
+
if ((global_type != null) && (global_type_created == null))
global_type_created = global_type.CreateType ();
[StructLayout (LayoutKind.Sequential)]
internal class PropertyOnTypeBuilderInst : PropertyInfo
{
- MonoGenericClass instantiation;
+ TypeBuilderInstantiation instantiation;
PropertyInfo prop;
- internal PropertyOnTypeBuilderInst (MonoGenericClass instantiation, PropertyInfo prop)
+ internal PropertyOnTypeBuilderInst (TypeBuilderInstantiation instantiation, PropertyInfo prop)
{
this.instantiation = instantiation;
this.prop = prop;
using System.Runtime.InteropServices;
using System.Globalization;
using System.Collections;
+using System.Collections.Generic;
using System.Security;
using System.Security.Permissions;
using System.Diagnostics.SymbolStore;
{
return attrs;
}
-
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- private extern void setup_internal_class ();
-
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- private extern void create_generic_class ();
-
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- private extern EventInfo get_event_info (EventBuilder eb);
internal TypeBuilder (ModuleBuilder mb, TypeAttributes attr, int table_idx)
{
this.nspace = String.Empty;
this.fullname = TypeIdentifiers.WithoutEscape(this.tname);
pmodule = mb;
- setup_internal_class ();
}
internal TypeBuilder (ModuleBuilder mb, string name, TypeAttributes attr, Type parent, Type[] interfaces, PackingSize packing_size, int type_size, Type nesting_type)
// skip .<Module> ?
table_idx = mb.get_next_table_index (this, 0x02, true);
- setup_internal_class ();
fullname = GetFullName ();
}
SetParent (pmodule.assemblyb.corlib_object_type);
}
- create_generic_class ();
-
// Fire TypeResolve events for fields whose type is an unfinished
// value type.
if (fields != null) {
}
}
+ internal void FixupTokens (Dictionary<int, int> token_map, Dictionary<int, MemberInfo> member_map) {
+ if (methods != null) {
+ for (int i = 0; i < num_methods; ++i)
+ methods[i].FixupTokens (token_map, member_map);
+ }
+ if (ctors != null) {
+ foreach (var cb in ctors)
+ cb.FixupTokens (token_map, member_map);
+ }
+ }
+
internal void GenerateDebugInfo (ISymbolWriter symbolWriter)
{
symbolWriter.OpenNamespace (this.Namespace);
throw new NotSupportedException ();
}
- // This is only used from MonoGenericInst.initialize().
- internal EventInfo[] GetEvents_internal (BindingFlags bindingAttr)
- {
- if (events == null)
- return new EventInfo [0];
- ArrayList l = new ArrayList ();
- bool match;
- MethodAttributes mattrs;
- MethodInfo accessor;
-
- foreach (EventBuilder eb in events) {
- if (eb == null)
- continue;
- EventInfo c = get_event_info (eb);
- match = false;
- accessor = c.GetAddMethod (true);
- if (accessor == null)
- accessor = c.GetRemoveMethod (true);
- if (accessor == null)
- continue;
- mattrs = accessor.Attributes;
- if ((mattrs & MethodAttributes.MemberAccessMask) == MethodAttributes.Public) {
- if ((bindingAttr & BindingFlags.Public) != 0)
- match = true;
- } else {
- if ((bindingAttr & BindingFlags.NonPublic) != 0)
- match = true;
- }
- if (!match)
- continue;
- match = false;
- if ((mattrs & MethodAttributes.Static) != 0) {
- if ((bindingAttr & BindingFlags.Static) != 0)
- match = true;
- } else {
- if ((bindingAttr & BindingFlags.Instance) != 0)
- match = true;
- }
- if (!match)
- continue;
- l.Add (c);
- }
- EventInfo[] result = new EventInfo [l.Count];
- l.CopyTo (result);
- return result;
- }
-
public override FieldInfo GetField (string name, BindingFlags bindingAttr)
{
if (created != null)
this.parent = parent;
}
this.parent = ResolveUserType (this.parent);
-
- // will just set the parent-related bits if called a second time
- setup_internal_class ();
}
internal int get_next_table_index (object obj, int table, bool inc) {
return created;
}
+ internal override Type RuntimeResolve ()
+ {
+ check_created ();
+ return created;
+ }
+
internal bool is_created {
get {
return createTypeCalled;
}
}
- public extern override bool IsGenericParameter {
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- get;
+ public override bool IsGenericParameter {
+ get {
+ return false;
+ }
}
public override GenericParameterAttributes GenericParameterAttributes {
static bool IsValidGetMethodType (Type type)
{
- if (type is TypeBuilder || type is MonoGenericClass)
+ if (type is TypeBuilder || type is TypeBuilderInstantiation)
return true;
/*GetMethod() must work with TypeBuilders after CreateType() was called.*/
if (type.Module is ModuleBuilder)
--- /dev/null
+//
+// System.Reflection.Emit.TypeBuilderInstantiation
+//
+// Sean MacIsaac (macisaac@ximian.com)
+// Paolo Molaro (lupus@ximian.com)
+// Patrik Torstensson (patrik.torstensson@labs2.com)
+//
+// (C) 2001 Ximian, Inc.
+//
+
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.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.
+//
+
+#if !FULL_AOT_RUNTIME
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Collections;
+using System.Runtime.CompilerServices;
+using System.Globalization;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace System.Reflection.Emit
+{
+ /*
+ * TypeBuilderInstantiation represents an instantiation of a generic TypeBuilder.
+ */
+ [StructLayout (LayoutKind.Sequential)]
+ sealed class TypeBuilderInstantiation :
+ TypeInfo
+ {
+ #region Keep in sync with object-internals.h
+#pragma warning disable 649
+ internal Type generic_type;
+ Type[] type_arguments;
+#pragma warning restore 649
+ #endregion
+
+ Hashtable fields, ctors, methods;
+
+ internal TypeBuilderInstantiation ()
+ {
+ // this should not be used
+ throw new InvalidOperationException ();
+ }
+
+ internal TypeBuilderInstantiation (Type tb, Type[] args)
+ {
+ this.generic_type = tb;
+ this.type_arguments = args;
+ }
+
+ internal override Type InternalResolve ()
+ {
+ Type gtd = generic_type.InternalResolve ();
+ Type[] args = new Type [type_arguments.Length];
+ for (int i = 0; i < type_arguments.Length; ++i)
+ args [i] = type_arguments [i].InternalResolve ();
+ return gtd.MakeGenericType (args);
+ }
+
+ // Called from the runtime to return the corresponding finished Type object
+ internal override Type RuntimeResolve ()
+ {
+ if (generic_type is TypeBuilder && !(generic_type as TypeBuilder).IsCreated ())
+ AppDomain.CurrentDomain.DoTypeResolve (generic_type);
+ for (int i = 0; i < type_arguments.Length; ++i) {
+ var t = type_arguments [i];
+ if (t is TypeBuilder && !(t as TypeBuilder).IsCreated ())
+ AppDomain.CurrentDomain.DoTypeResolve (t);
+ }
+ return InternalResolve ();
+ }
+
+ internal bool IsCreated {
+ get {
+ TypeBuilder tb = generic_type as TypeBuilder;
+ return tb != null ? tb.is_created : true;
+ }
+ }
+
+ private const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic |
+ BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
+
+ Type GetParentType ()
+ {
+ return InflateType (generic_type.BaseType);
+ }
+
+ internal Type InflateType (Type type)
+ {
+ return InflateType (type, type_arguments, null);
+ }
+
+ internal Type InflateType (Type type, Type[] method_args)
+ {
+ return InflateType (type, type_arguments, method_args);
+ }
+
+ internal static Type InflateType (Type type, Type[] type_args, Type[] method_args)
+ {
+ if (type == null)
+ return null;
+ if (!type.IsGenericParameter && !type.ContainsGenericParameters)
+ return type;
+ if (type.IsGenericParameter) {
+ if (type.DeclaringMethod == null)
+ return type_args == null ? type : type_args [type.GenericParameterPosition];
+ return method_args == null ? type : method_args [type.GenericParameterPosition];
+ }
+ if (type.IsPointer)
+ return InflateType (type.GetElementType (), type_args, method_args).MakePointerType ();
+ if (type.IsByRef)
+ return InflateType (type.GetElementType (), type_args, method_args).MakeByRefType ();
+ if (type.IsArray) {
+ if (type.GetArrayRank () > 1)
+ return InflateType (type.GetElementType (), type_args, method_args).MakeArrayType (type.GetArrayRank ());
+
+ if (type.ToString ().EndsWith ("[*]", StringComparison.Ordinal)) /*FIXME, the reflection API doesn't offer a way around this*/
+ return InflateType (type.GetElementType (), type_args, method_args).MakeArrayType (1);
+ return InflateType (type.GetElementType (), type_args, method_args).MakeArrayType ();
+ }
+
+ Type[] args = type.GetGenericArguments ();
+ for (int i = 0; i < args.Length; ++i)
+ args [i] = InflateType (args [i], type_args, method_args);
+
+ Type gtd = type.IsGenericTypeDefinition ? type : type.GetGenericTypeDefinition ();
+ return gtd.MakeGenericType (args);
+ }
+
+ public override Type BaseType {
+ get { return generic_type.BaseType; }
+ }
+
+ public override Type[] GetInterfaces ()
+ {
+ throw new NotSupportedException ();
+ }
+
+ protected override bool IsValueTypeImpl ()
+ {
+ return generic_type.IsValueType;
+ }
+
+ internal override MethodInfo GetMethod (MethodInfo fromNoninstanciated)
+ {
+ if (methods == null)
+ methods = new Hashtable ();
+ if (!methods.ContainsKey (fromNoninstanciated))
+ methods [fromNoninstanciated] = new MethodOnTypeBuilderInst (this, fromNoninstanciated);
+ return (MethodInfo)methods [fromNoninstanciated];
+ }
+
+ internal override ConstructorInfo GetConstructor (ConstructorInfo fromNoninstanciated)
+ {
+ if (ctors == null)
+ ctors = new Hashtable ();
+ if (!ctors.ContainsKey (fromNoninstanciated))
+ ctors [fromNoninstanciated] = new ConstructorOnTypeBuilderInst (this, fromNoninstanciated);
+ return (ConstructorInfo)ctors [fromNoninstanciated];
+ }
+
+ internal override FieldInfo GetField (FieldInfo fromNoninstanciated)
+ {
+ if (fields == null)
+ fields = new Hashtable ();
+ if (!fields.ContainsKey (fromNoninstanciated))
+ fields [fromNoninstanciated] = new FieldOnTypeBuilderInst (this, fromNoninstanciated);
+ return (FieldInfo)fields [fromNoninstanciated];
+ }
+
+ public override MethodInfo[] GetMethods (BindingFlags bf)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override ConstructorInfo[] GetConstructors (BindingFlags bf)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override FieldInfo[] GetFields (BindingFlags bf)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override PropertyInfo[] GetProperties (BindingFlags bf)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override EventInfo[] GetEvents (BindingFlags bf)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override Type[] GetNestedTypes (BindingFlags bf)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override bool IsAssignableFrom (Type c)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override Type UnderlyingSystemType {
+ get { return this; }
+ }
+
+ public override Assembly Assembly {
+ get { return generic_type.Assembly; }
+ }
+
+ public override Module Module {
+ get { return generic_type.Module; }
+ }
+
+ public override string Name {
+ get { return generic_type.Name; }
+ }
+
+ public override string Namespace {
+ get { return generic_type.Namespace; }
+ }
+
+ public override string FullName {
+ get { return format_name (true, false); }
+ }
+
+ public override string AssemblyQualifiedName {
+ get { return format_name (true, true); }
+ }
+
+ public override Guid GUID {
+ get { throw new NotSupportedException (); }
+ }
+
+ string format_name (bool full_name, bool assembly_qualified)
+ {
+ StringBuilder sb = new StringBuilder (generic_type.FullName);
+
+ sb.Append ("[");
+ for (int i = 0; i < type_arguments.Length; ++i) {
+ if (i > 0)
+ sb.Append (",");
+
+ string name;
+ if (full_name) {
+ string assemblyName = type_arguments [i].Assembly.FullName;
+ name = type_arguments [i].FullName;
+ if (name != null && assemblyName != null)
+ name = name + ", " + assemblyName;
+ } else {
+ name = type_arguments [i].ToString ();
+ }
+ if (name == null) {
+ return null;
+ }
+ if (full_name)
+ sb.Append ("[");
+ sb.Append (name);
+ if (full_name)
+ sb.Append ("]");
+ }
+ sb.Append ("]");
+ if (assembly_qualified) {
+ sb.Append (", ");
+ sb.Append (generic_type.Assembly.FullName);
+ }
+ return sb.ToString ();
+ }
+
+ public override string ToString ()
+ {
+ return format_name (false, false);
+ }
+
+ public override Type GetGenericTypeDefinition ()
+ {
+ return generic_type;
+ }
+
+ public override Type[] GetGenericArguments ()
+ {
+ Type[] ret = new Type [type_arguments.Length];
+ type_arguments.CopyTo (ret, 0);
+ return ret;
+ }
+
+ public override bool ContainsGenericParameters {
+ get {
+ foreach (Type t in type_arguments) {
+ if (t.ContainsGenericParameters)
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public override bool IsGenericTypeDefinition {
+ get { return false; }
+ }
+
+ public override bool IsGenericType {
+ get { return true; }
+ }
+
+ public override Type DeclaringType {
+ get { return generic_type.DeclaringType; }
+ }
+
+ public override RuntimeTypeHandle TypeHandle {
+ get {
+ throw new NotSupportedException ();
+ }
+ }
+
+ public override Type MakeArrayType ()
+ {
+ return new ArrayType (this, 0);
+ }
+
+ public override Type MakeArrayType (int rank)
+ {
+ if (rank < 1)
+ throw new IndexOutOfRangeException ();
+ return new ArrayType (this, rank);
+ }
+
+ public override Type MakeByRefType ()
+ {
+ return new ByRefType (this);
+ }
+
+ public override Type MakePointerType ()
+ {
+ return new PointerType (this);
+ }
+
+ public override Type GetElementType ()
+ {
+ throw new NotSupportedException ();
+ }
+
+ protected override bool HasElementTypeImpl ()
+ {
+ return false;
+ }
+
+ protected override bool IsCOMObjectImpl ()
+ {
+ return false;
+ }
+
+ protected override bool IsPrimitiveImpl ()
+ {
+ return false;
+ }
+
+ protected override bool IsArrayImpl ()
+ {
+ return false;
+ }
+
+ protected override bool IsByRefImpl ()
+ {
+ return false;
+ }
+
+ protected override bool IsPointerImpl ()
+ {
+ return false;
+ }
+
+ protected override TypeAttributes GetAttributeFlagsImpl ()
+ {
+ return generic_type.Attributes;
+ }
+
+ //stuff that throws
+ public override Type GetInterface (string name, bool ignoreCase)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override EventInfo GetEvent (string name, BindingFlags bindingAttr)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override FieldInfo GetField( string name, BindingFlags bindingAttr)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override MemberInfo[] GetMembers (BindingFlags bindingAttr)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override Type GetNestedType (string name, BindingFlags bindingAttr)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override object InvokeMember (string name, BindingFlags invokeAttr,
+ Binder binder, object target, object[] args,
+ ParameterModifier[] modifiers,
+ CultureInfo culture, string[] namedParameters)
+ {
+ throw new NotSupportedException ();
+ }
+
+ protected override MethodInfo GetMethodImpl (string name, BindingFlags bindingAttr, Binder binder,
+ CallingConventions callConvention, Type[] types,
+ ParameterModifier[] modifiers)
+ {
+ throw new NotSupportedException ();
+ }
+
+ protected override PropertyInfo GetPropertyImpl (string name, BindingFlags bindingAttr, Binder binder,
+ Type returnType, Type[] types, ParameterModifier[] modifiers)
+ {
+ throw new NotSupportedException ();
+ }
+
+ protected override ConstructorInfo GetConstructorImpl (BindingFlags bindingAttr,
+ Binder binder,
+ CallingConventions callConvention,
+ Type[] types,
+ ParameterModifier[] modifiers)
+ {
+ throw new NotSupportedException ();
+ }
+
+ //MemberInfo
+ public override bool IsDefined (Type attributeType, bool inherit)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override object [] GetCustomAttributes (bool inherit)
+ {
+ if (IsCreated)
+ return generic_type.GetCustomAttributes (inherit);
+ throw new NotSupportedException ();
+ }
+
+ public override object [] GetCustomAttributes (Type attributeType, bool inherit)
+ {
+ if (IsCreated)
+ return generic_type.GetCustomAttributes (attributeType, inherit);
+ throw new NotSupportedException ();
+ }
+
+ internal override bool IsUserType {
+ get {
+ foreach (var t in type_arguments) {
+ if (t.IsUserType)
+ return true;
+ }
+ return false;
+ }
+ }
+
+ internal static Type MakeGenericType (Type type, Type[] typeArguments)
+ {
+ return new TypeBuilderInstantiation (type, typeArguments);
+ }
+ }
+}
+#else
+namespace System.Reflection.Emit
+{
+ abstract class TypeBuilderInstantiation : TypeInfo
+ {
+ internal static Type MakeGenericType (Type type, Type[] typeArguments)
+ {
+ throw new NotSupportedException ("User types are not supported under full aot");
+ }
+ }
+}
+#endif
+++ /dev/null
-//
-// System.Reflection.MonoGenericClass
-//
-// Sean MacIsaac (macisaac@ximian.com)
-// Paolo Molaro (lupus@ximian.com)
-// Patrik Torstensson (patrik.torstensson@labs2.com)
-//
-// (C) 2001 Ximian, Inc.
-//
-
-//
-// Copyright (C) 2004 Novell, Inc (http://www.novell.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.
-//
-
-#if !FULL_AOT_RUNTIME
-using System.Reflection;
-using System.Reflection.Emit;
-using System.Collections;
-using System.Runtime.CompilerServices;
-using System.Globalization;
-using System.Runtime.Serialization;
-using System.Text;
-using System.Runtime.InteropServices;
-
-namespace System.Reflection
-{
- /*
- * MonoGenericClass represents an instantiation of a generic TypeBuilder. MS
- * calls this class TypeBuilderInstantiation (a much better name). MS returns
- * NotImplementedException for many of the methods but we can't do that as gmcs
- * depends on them.
- */
- [StructLayout (LayoutKind.Sequential)]
- sealed class MonoGenericClass :
- TypeInfo
- {
- #region Keep in sync with object-internals.h
-#pragma warning disable 649
- internal Type generic_type;
- Type[] type_arguments;
- bool initialized;
-#pragma warning restore 649
- #endregion
-
- Hashtable fields, ctors, methods;
-
- internal MonoGenericClass ()
- {
- // this should not be used
- throw new InvalidOperationException ();
- }
-
- internal MonoGenericClass (Type tb, Type[] args)
- {
- this.generic_type = tb;
- this.type_arguments = args;
- /*
- This is a temporary hack until we can fix the rest of the runtime
- to properly handle this class to be a complete UT.
-
- We must not regisrer this with the runtime after the type is created
- otherwise created_type.MakeGenericType will return an instance of MonoGenericClass,
- which is very very broken.
- */
- if (tb is TypeBuilder && !(tb as TypeBuilder).is_created)
- register_with_runtime (this);
-
- }
-
- internal override Type InternalResolve ()
- {
- Type gtd = generic_type.InternalResolve ();
- Type[] args = new Type [type_arguments.Length];
- for (int i = 0; i < type_arguments.Length; ++i)
- args [i] = type_arguments [i].InternalResolve ();
- return gtd.MakeGenericType (args);
- }
-
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- extern void initialize (FieldInfo[] fields);
-
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal static extern void register_with_runtime (Type type);
-
- internal bool IsCreated {
- get {
- TypeBuilder tb = generic_type as TypeBuilder;
- return tb != null ? tb.is_created : true;
- }
- }
-
- private const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic |
- BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
-
- void initialize ()
- {
- if (initialized)
- return;
-
- MonoGenericClass parent = GetParentType () as MonoGenericClass;
- if (parent != null)
- parent.initialize ();
-
- initialize (generic_type.GetFields (flags));
-
- initialized = true;
- }
-
- Type GetParentType ()
- {
- return InflateType (generic_type.BaseType);
- }
-
- internal Type InflateType (Type type)
- {
- return InflateType (type, type_arguments, null);
- }
-
- internal Type InflateType (Type type, Type[] method_args)
- {
- return InflateType (type, type_arguments, method_args);
- }
-
- internal static Type InflateType (Type type, Type[] type_args, Type[] method_args)
- {
- if (type == null)
- return null;
- if (!type.IsGenericParameter && !type.ContainsGenericParameters)
- return type;
- if (type.IsGenericParameter) {
- if (type.DeclaringMethod == null)
- return type_args == null ? type : type_args [type.GenericParameterPosition];
- return method_args == null ? type : method_args [type.GenericParameterPosition];
- }
- if (type.IsPointer)
- return InflateType (type.GetElementType (), type_args, method_args).MakePointerType ();
- if (type.IsByRef)
- return InflateType (type.GetElementType (), type_args, method_args).MakeByRefType ();
- if (type.IsArray) {
- if (type.GetArrayRank () > 1)
- return InflateType (type.GetElementType (), type_args, method_args).MakeArrayType (type.GetArrayRank ());
-
- if (type.ToString ().EndsWith ("[*]", StringComparison.Ordinal)) /*FIXME, the reflection API doesn't offer a way around this*/
- return InflateType (type.GetElementType (), type_args, method_args).MakeArrayType (1);
- return InflateType (type.GetElementType (), type_args, method_args).MakeArrayType ();
- }
-
- Type[] args = type.GetGenericArguments ();
- for (int i = 0; i < args.Length; ++i)
- args [i] = InflateType (args [i], type_args, method_args);
-
- Type gtd = type.IsGenericTypeDefinition ? type : type.GetGenericTypeDefinition ();
- return gtd.MakeGenericType (args);
- }
-
- public override Type BaseType {
- get { return generic_type.BaseType; }
- }
-
- public override Type[] GetInterfaces ()
- {
- throw new NotSupportedException ();
- }
-
- protected override bool IsValueTypeImpl ()
- {
- return generic_type.IsValueType;
- }
-
- internal override MethodInfo GetMethod (MethodInfo fromNoninstanciated)
- {
- initialize ();
-
- if (methods == null)
- methods = new Hashtable ();
- if (!methods.ContainsKey (fromNoninstanciated))
- methods [fromNoninstanciated] = new MethodOnTypeBuilderInst (this, fromNoninstanciated);
- return (MethodInfo)methods [fromNoninstanciated];
- }
-
- internal override ConstructorInfo GetConstructor (ConstructorInfo fromNoninstanciated)
- {
- initialize ();
-
- if (ctors == null)
- ctors = new Hashtable ();
- if (!ctors.ContainsKey (fromNoninstanciated))
- ctors [fromNoninstanciated] = new ConstructorOnTypeBuilderInst (this, fromNoninstanciated);
- return (ConstructorInfo)ctors [fromNoninstanciated];
- }
-
- internal override FieldInfo GetField (FieldInfo fromNoninstanciated)
- {
- initialize ();
- if (fields == null)
- fields = new Hashtable ();
- if (!fields.ContainsKey (fromNoninstanciated))
- fields [fromNoninstanciated] = new FieldOnTypeBuilderInst (this, fromNoninstanciated);
- return (FieldInfo)fields [fromNoninstanciated];
- }
-
- public override MethodInfo[] GetMethods (BindingFlags bf)
- {
- throw new NotSupportedException ();
- }
-
- public override ConstructorInfo[] GetConstructors (BindingFlags bf)
- {
- throw new NotSupportedException ();
- }
-
- public override FieldInfo[] GetFields (BindingFlags bf)
- {
- throw new NotSupportedException ();
- }
-
- public override PropertyInfo[] GetProperties (BindingFlags bf)
- {
- throw new NotSupportedException ();
- }
-
- public override EventInfo[] GetEvents (BindingFlags bf)
- {
- throw new NotSupportedException ();
- }
-
- public override Type[] GetNestedTypes (BindingFlags bf)
- {
- throw new NotSupportedException ();
- }
-
- public override bool IsAssignableFrom (Type c)
- {
- throw new NotSupportedException ();
- }
-
- public override Type UnderlyingSystemType {
- get { return this; }
- }
-
- public override Assembly Assembly {
- get { return generic_type.Assembly; }
- }
-
- public override Module Module {
- get { return generic_type.Module; }
- }
-
- public override string Name {
- get { return generic_type.Name; }
- }
-
- public override string Namespace {
- get { return generic_type.Namespace; }
- }
-
- public override string FullName {
- get { return format_name (true, false); }
- }
-
- public override string AssemblyQualifiedName {
- get { return format_name (true, true); }
- }
-
- public override Guid GUID {
- get { throw new NotSupportedException (); }
- }
-
- string format_name (bool full_name, bool assembly_qualified)
- {
- StringBuilder sb = new StringBuilder (generic_type.FullName);
-
- sb.Append ("[");
- for (int i = 0; i < type_arguments.Length; ++i) {
- if (i > 0)
- sb.Append (",");
-
- string name;
- if (full_name) {
- string assemblyName = type_arguments [i].Assembly.FullName;
- name = type_arguments [i].FullName;
- if (name != null && assemblyName != null)
- name = name + ", " + assemblyName;
- } else {
- name = type_arguments [i].ToString ();
- }
- if (name == null) {
- return null;
- }
- if (full_name)
- sb.Append ("[");
- sb.Append (name);
- if (full_name)
- sb.Append ("]");
- }
- sb.Append ("]");
- if (assembly_qualified) {
- sb.Append (", ");
- sb.Append (generic_type.Assembly.FullName);
- }
- return sb.ToString ();
- }
-
- public override string ToString ()
- {
- return format_name (false, false);
- }
-
- public override Type GetGenericTypeDefinition ()
- {
- return generic_type;
- }
-
- public override Type[] GetGenericArguments ()
- {
- Type[] ret = new Type [type_arguments.Length];
- type_arguments.CopyTo (ret, 0);
- return ret;
- }
-
- public override bool ContainsGenericParameters {
- get {
- foreach (Type t in type_arguments) {
- if (t.ContainsGenericParameters)
- return true;
- }
- return false;
- }
- }
-
- public override bool IsGenericTypeDefinition {
- get { return false; }
- }
-
- public override bool IsGenericType {
- get { return true; }
- }
-
- public override Type DeclaringType {
- get { return generic_type.DeclaringType; }
- }
-
- public override RuntimeTypeHandle TypeHandle {
- get {
- throw new NotSupportedException ();
- }
- }
-
- public override Type MakeArrayType ()
- {
- return new ArrayType (this, 0);
- }
-
- public override Type MakeArrayType (int rank)
- {
- if (rank < 1)
- throw new IndexOutOfRangeException ();
- return new ArrayType (this, rank);
- }
-
- public override Type MakeByRefType ()
- {
- return new ByRefType (this);
- }
-
- public override Type MakePointerType ()
- {
- return new PointerType (this);
- }
-
- public override Type GetElementType ()
- {
- throw new NotSupportedException ();
- }
-
- protected override bool HasElementTypeImpl ()
- {
- return false;
- }
-
- protected override bool IsCOMObjectImpl ()
- {
- return false;
- }
-
- protected override bool IsPrimitiveImpl ()
- {
- return false;
- }
-
- protected override bool IsArrayImpl ()
- {
- return false;
- }
-
- protected override bool IsByRefImpl ()
- {
- return false;
- }
-
- protected override bool IsPointerImpl ()
- {
- return false;
- }
-
- protected override TypeAttributes GetAttributeFlagsImpl ()
- {
- return generic_type.Attributes;
- }
-
- //stuff that throws
- public override Type GetInterface (string name, bool ignoreCase)
- {
- throw new NotSupportedException ();
- }
-
- public override EventInfo GetEvent (string name, BindingFlags bindingAttr)
- {
- throw new NotSupportedException ();
- }
-
- public override FieldInfo GetField( string name, BindingFlags bindingAttr)
- {
- throw new NotSupportedException ();
- }
-
- public override MemberInfo[] GetMembers (BindingFlags bindingAttr)
- {
- throw new NotSupportedException ();
- }
-
- public override Type GetNestedType (string name, BindingFlags bindingAttr)
- {
- throw new NotSupportedException ();
- }
-
- public override object InvokeMember (string name, BindingFlags invokeAttr,
- Binder binder, object target, object[] args,
- ParameterModifier[] modifiers,
- CultureInfo culture, string[] namedParameters)
- {
- throw new NotSupportedException ();
- }
-
- protected override MethodInfo GetMethodImpl (string name, BindingFlags bindingAttr, Binder binder,
- CallingConventions callConvention, Type[] types,
- ParameterModifier[] modifiers)
- {
- throw new NotSupportedException ();
- }
-
- protected override PropertyInfo GetPropertyImpl (string name, BindingFlags bindingAttr, Binder binder,
- Type returnType, Type[] types, ParameterModifier[] modifiers)
- {
- throw new NotSupportedException ();
- }
-
- protected override ConstructorInfo GetConstructorImpl (BindingFlags bindingAttr,
- Binder binder,
- CallingConventions callConvention,
- Type[] types,
- ParameterModifier[] modifiers)
- {
- throw new NotSupportedException ();
- }
-
- //MemberInfo
- public override bool IsDefined (Type attributeType, bool inherit)
- {
- throw new NotSupportedException ();
- }
-
- public override object [] GetCustomAttributes (bool inherit)
- {
- if (IsCreated)
- return generic_type.GetCustomAttributes (inherit);
- throw new NotSupportedException ();
- }
-
- public override object [] GetCustomAttributes (Type attributeType, bool inherit)
- {
- if (IsCreated)
- return generic_type.GetCustomAttributes (attributeType, inherit);
- throw new NotSupportedException ();
- }
-
- internal override bool IsUserType {
- get {
- foreach (var t in type_arguments) {
- if (t.IsUserType)
- return true;
- }
- return false;
- }
- }
-
- }
-}
-
-#endif
+++ /dev/null
-//
-// System.Reflection.MonoGenericMethod
-//
-// Martin Baulig (martin@ximian.com)
-//
-// (C) 2004 Novell, Inc.
-//
-
-//
-// Copyright (C) 2004 Novell, Inc (http://www.novell.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.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-namespace System.Reflection
-{
- [Serializable]
- [StructLayout (LayoutKind.Sequential)]
- internal class MonoGenericMethod : MonoMethod
- {
- internal MonoGenericMethod ()
- {
- // this should not be used
- throw new InvalidOperationException ();
- }
-
- }
-
- [Serializable]
- [StructLayout (LayoutKind.Sequential)]
- internal class MonoGenericCMethod : MonoCMethod
- {
- internal MonoGenericCMethod ()
- {
- // this should not be used
- throw new InvalidOperationException ();
- }
- }
-}
* of icalls, do not require an increment.
*/
#pragma warning disable 169
- private const int mono_corlib_version = 157;
+ private const int mono_corlib_version = 158;
#pragma warning restore 169
[ComVisible (true)]
}
[Test]
+ // The token is not guaranteed to be 0x0a000001
+ [Category ("NotWorking")]
public void ResolveFieldMemberRefWithGenericArguments ()
{
var assembly = genAssembly ();
}
[Test]
+ // The token is not guaranteed to be 0x0a000002
+ [Category ("NotWorking")]
public void ResolveMethodMemberRefWithGenericArguments ()
{
var assembly = genAssembly ();
}
[Test]
+ // The token is not guaranteed to be 0x2b000001
+ [Category("NotWorking")]
public void ResolveMethodSpecWithGenericArguments ()
{
var assembly = genAssembly ();
//Console.WriteLine (res[0]);
}
+ [Test]
+ public void FieldWithInitializedDataWorksWithCompilerRuntimeHelpers2 ()
+ {
+ TypeBuilder tb = module.DefineType ("Type1", TypeAttributes.Public);
+ var garg = tb.DefineGenericParameters ("T") [0];
+ FieldBuilder fb = tb.DefineInitializedData ("Foo", new byte [] {1,2,3,4}, FieldAttributes.Static|FieldAttributes.Public);
+ tb.CreateType ();
+
+ assembly = Thread.GetDomain ().DefineDynamicAssembly (new AssemblyName (ASSEMBLY_NAME+"2"), AssemblyBuilderAccess.RunAndSave, Path.GetTempPath ());
+ module = assembly.DefineDynamicModule ("Instance.exe");
+
+ TypeBuilder tb2 = module.DefineType ("Type2", TypeAttributes.Public);
+ MethodBuilder mb = tb2.DefineMethod ("Test", MethodAttributes.Public | MethodAttributes.Static, typeof (object), new Type [0]);
+ ILGenerator il = mb.GetILGenerator ();
+
+ il.Emit (OpCodes.Ldc_I4_1);
+ il.Emit (OpCodes.Newarr, typeof (int));
+ il.Emit (OpCodes.Dup);
+ il.Emit (OpCodes.Ldtoken, fb);
+ il.Emit (OpCodes.Call, typeof (RuntimeHelpers).GetMethod ("InitializeArray"));
+ il.Emit (OpCodes.Ret);
+
+ Type t = tb2.CreateType ();
+ int[] res = (int[])t.GetMethod ("Test").Invoke (null, new object[0]);
+ //Console.WriteLine (res[0]);
+ }
+
public interface IDelegateFactory
{
Delegate Create (Delegate del);
<Compile Include="System.Reflection\MonoEvent.cs" />\r
<Compile Include="System.Reflection\MonoField.cs" />\r
<Compile Include="System.Reflection\MonoGenericClass.cs" />\r
- <Compile Include="System.Reflection\MonoGenericMethod.cs" />\r
<Compile Include="System.Reflection\MonoMethod.cs" />\r
<Compile Include="System.Reflection\MonoModule.cs" />\r
<Compile Include="System.Reflection\MonoParameterInfo.cs" />\r
System.Reflection/Module.cs
System.Reflection/ModuleResolveEventHandler.cs
System.Reflection/MonoAssembly.cs
-System.Reflection/MonoGenericClass.cs
-System.Reflection/MonoGenericMethod.cs
System.Reflection/MonoEvent.cs
System.Reflection/MonoField.cs
System.Reflection/MonoMethod.cs
System.Reflection.Emit/StackBehaviour.cs
System.Reflection.Emit/StringToken.cs
System.Reflection.Emit/TypeBuilder.cs
+System.Reflection.Emit/TypeBuilderInstantiation.cs
System.Reflection.Emit/TypeToken.cs
System.Reflection.Emit/UnmanagedMarshal.cs
System.Reflection.Emit/AssemblyBuilder.pns.cs
ReferenceSources/NativeMethods.cs
ReferenceSources/RuntimeHandles.cs
ReferenceSources/CompareInfo.cs
-ReferenceSources/TypeBuilderInstantiation.cs
ReferenceSources/Buffer.cs
ReferenceSources/TextInfo.cs
ReferenceSources/win32native.cs
private const MethodAttributes CtorAttributes = MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public;
private const MethodImplAttributes ImplAttributes = MethodImplAttributes.Runtime | MethodImplAttributes.Managed;
private const MethodAttributes InvokeAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual;
+#if FEATURE_REFEMIT
private static readonly Type[] _DelegateCtorSignature = new Type[] { typeof(object), typeof(IntPtr) };
+#endif
private static Type MakeNewCustomDelegate(Type[] types) {
#if FEATURE_REFEMIT
override public DataTable GetDataSources() {
#if MONO
+ timeoutTime = 0;
throw new NotImplementedException ();
#else
(new NamedPermissionSet("FullTrust")).Demand(); // SQLBUDT 244304
--- /dev/null
+// CS0177: The out parameter `x' must be assigned to before control leaves the current method
+// Line: 6
+
+public class GotoWithOut
+{
+ public static void Test (bool cond, out int x)
+ {
+ if (cond)
+ {
+ goto Label2;
+ }
+ else
+ {
+ goto Label;
+ }
+ Label:
+ x = 0;
+ Label2:
+ return;
+ }
+}
\ No newline at end of file
return da;
}
+ public Dictionary<Statement, List<DefiniteAssignmentBitSet>> CopyLabelStack ()
+ {
+ if (LabelStack == null)
+ return null;
+
+ var dest = new Dictionary<Statement, List<DefiniteAssignmentBitSet>> ();
+ foreach (var entry in LabelStack) {
+ dest.Add (entry.Key, new List<DefiniteAssignmentBitSet> (entry.Value));
+ }
+
+ return dest;
+ }
+
public bool IsDefinitelyAssigned (VariableInfo variable)
{
return variable.IsAssigned (DefiniteAssignment);
return variable.IsStructFieldAssigned (DefiniteAssignment, name);
}
+ public void SetLabelStack (Dictionary<Statement, List<DefiniteAssignmentBitSet>> labelStack)
+ {
+ LabelStack = labelStack;
+ }
+
public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false)
{
variable.SetAssigned (DefiniteAssignment, generatedAssignment);
var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
+ var labels = fc.CopyLabelStack ();
var res = TrueStatement.FlowAnalysis (fc);
+ fc.SetLabelStack (labels);
+
if (FalseStatement == null) {
+
var c = expr as Constant;
if (c != null && !c.IsDefaultValue)
return true_returns;
if (true_returns) {
fc.DefiniteAssignment = da_false;
- return FalseStatement.FlowAnalysis (fc);
+
+ res = FalseStatement.FlowAnalysis (fc);
+ fc.SetLabelStack (labels);
+ return res;
}
var da_true = fc.DefiniteAssignment;
fc.DefiniteAssignment = da_false;
+
res &= FalseStatement.FlowAnalysis (fc);
+ fc.SetLabelStack (labels);
+
if (!TrueStatement.IsUnreachable) {
if (false_returns || FalseStatement.IsUnreachable)
fc.DefiniteAssignment = da_true;
--- /dev/null
+public class GotoCodeFlowBug
+{
+ public static void Test (bool cond, out int x)
+ {
+ if (cond)
+ {
+ goto Label;
+ }
+ Label:
+ x = 0;
+ }
+
+ public static void Test2 (bool cond, out int x)
+ {
+ if (cond)
+ {
+ goto Label;
+ }
+ else
+ {
+ goto Label;
+ }
+ Label:
+ x = 0;
+ }
+
+ public static void Main ()
+ {
+ }
+}
\ No newline at end of file
</method>
</type>
</test>
+ <test name="test-941.cs">
+ <type name="GotoCodeFlowBug">
+ <method name="Void Test(Boolean, Int32 ByRef)" attrs="150">
+ <size>17</size>
+ </method>
+ <method name="Void Test2(Boolean, Int32 ByRef)" attrs="150">
+ <size>23</size>
+ </method>
+ <method name="Void Main()" attrs="150">
+ <size>2</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ </test>
<test name="test-95.cs">
<type name="X">
<method name="Int32 Main()" attrs="150">
mdbrebase \
ikdasm \
mono-symbolicate \
- linker-analyzer
+ linker-analyzer \
+ btls
build_SUBDIRS = gacutil security culevel cil-stringreplacer commoncryptogenerator
net_4_5_SUBDIRS = gacutil
--- /dev/null
+using System;
+using System.Diagnostics;
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle ("btls-cert-sync")]
+[assembly: AssemblyDescription ("btls-cert-sync")]
+[assembly: AssemblyDefaultAlias ("btls-cert-sync")]
+
+[assembly: AssemblyCompany (Consts.MonoCompany)]
+[assembly: AssemblyProduct (Consts.MonoProduct)]
+[assembly: AssemblyCopyright (Consts.MonoCopyright)]
+[assembly: AssemblyVersion (Consts.FxVersion)]
+[assembly: AssemblyFileVersion (Consts.FxFileVersion)]
+[assembly: SatelliteContractVersion (Consts.FxVersion)]
+[assembly: AssemblyInformationalVersion (Consts.FxFileVersion)]
+
+[assembly: CLSCompliant (true)]
+[assembly: NeutralResourcesLanguage ("en-US")]
+
+[assembly: ComVisible (false)]
+
+[assembly: AssemblyDelaySign (true)]
+[assembly: AssemblyKeyFile ("../../class/mono.pub")]
--- /dev/null
+thisdir = tools/btls
+SUBDIRS =
+include ../../build/rules.make
+
+LOCAL_MCS_FLAGS =
+LIB_REFS = System Mono.Security Mono.Btls.Interface
+PROGRAM = btls-cert-sync.exe
+
+include ../../build/executable.make
--- /dev/null
+using System;
+using System.IO;
+using System.Text;
+using System.Security.Cryptography.X509Certificates;
+using Mono.Btls;
+
+namespace Mono.Btls
+{
+ static class BtlsCertSync
+ {
+ static void Main (string[] args)
+ {
+ if (!MonoBtlsProvider.IsSupported ()) {
+ Console.Error.WriteLine ("BTLS is not supported in this runtime!");
+ Environment.Exit (255);
+ }
+
+ var configPath = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
+ configPath = Path.Combine (configPath, ".mono");
+
+ var oldStorePath = Path.Combine (configPath, "certs", "Trust");
+ var newStorePath = MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.UserTrustedRoots);
+
+ if (!Directory.Exists (oldStorePath)) {
+ Console.WriteLine ("Old trust store {0} does not exist.");
+ Environment.Exit (255);
+ }
+
+ if (Directory.Exists (newStorePath))
+ Directory.Delete (newStorePath, true);
+ Directory.CreateDirectory (newStorePath);
+
+ var oldfiles = Directory.GetFiles (oldStorePath, "*.cer");
+ Console.WriteLine ("Found {0} files in the old store.", oldfiles.Length);
+
+ foreach (var file in oldfiles) {
+ Console.WriteLine ("Converting {0}.", file);
+ var data = File.ReadAllBytes (file);
+ using (var x509 = MonoBtlsX509.LoadFromData (data, MonoBtlsX509Format.DER)) {
+ ConvertToNewFormat (newStorePath, x509);
+ }
+ }
+ }
+
+ static void ConvertToNewFormat (string root, MonoBtlsX509 x509)
+ {
+ long hash = x509.GetSubjectNameHash ();
+
+ string newName;
+ int index = 0;
+ do {
+ newName = Path.Combine (root, string.Format ("{0:x8}.{1}", hash, index++));
+ } while (File.Exists (newName));
+ Console.WriteLine (" new name: {0}", newName);
+
+ using (var stream = new FileStream (newName, FileMode.Create))
+ using (var bio = MonoBtlsBio.CreateMonoStream (stream))
+ x509.ExportAsPEM (bio, true);
+ }
+ }
+}
--- /dev/null
+../../build/common/SR.cs
+../../build/common/Consts.cs
+AssemblyInfo.cs
+btls-cert-sync.cs
<type fullname="System.Reflection.MonoEventInfo" preserve="fields" />
<type fullname="System.Reflection.MonoField" preserve="fields" />
<type fullname="System.Reflection.MonoGenericClass" preserve="fields" />
- <type fullname="System.Reflection.MonoGenericMethod" preserve="fields" />
- <type fullname="System.Reflection.MonoGenericCMethod" preserve="fields" />
<type fullname="System.Reflection.MonoMethod" preserve="fields" />
<type fullname="System.Reflection.MonoMethodInfo" preserve="fields" />
<type fullname="System.Reflection.MonoPropertyInfo" preserve="fields" />
<type fullname="System.Reflection.Emit.MethodBuilder" preserve="fields" />
<type fullname="System.Reflection.Emit.ModuleBuilder" preserve="fields">
<method name="Mono_GetGuid" />
+ <method name="RuntimeResolve" />
</type>
<type fullname="System.Reflection.Emit.MonoResource" preserve="fields" />
<type fullname="System.Reflection.Emit.MonoWin32Resource" preserve="fields" />
}
}
- Console.WriteLine ("Using runtime {0} for {1}", runtime, output);
}
GeneratePackage (files);
}
-
+ Console.WriteLine ("Generated {0}", output);
+
return 0;
}
}
var maker = new PackageMaker (output);
+ Console.WriteLine ("Using runtime: " + runtime);
maker.AddFile (runtime);
foreach (var url in files){
string aname = MakeBundle.GetAssemblyName (fname);
maker.Add ("assembly:" + aname, fname);
- if (File.Exists (fname + ".config"))
+ Console.WriteLine (" Assembly: " + fname);
+ if (File.Exists (fname + ".config")){
maker.Add ("config:" + aname, fname + ".config");
+ Console.WriteLine (" Config: " + runtime);
+ }
}
+
if (!MaybeAddFile (maker, "systemconfig:", config_file) || !MaybeAddFile (maker, "machineconfig:", machine_config_file))
return false;
}
if (libraries.Count > 0){
foreach (var alias_and_path in libraries){
+ Console.WriteLine (" Library: " + alias_and_path.Value);
maker.Add ("library:" + alias_and_path.Key, alias_and_path.Value);
}
}
static string inputFile;
static bool quiet;
static bool userStore;
+ static bool btlsStore = true;
static X509Certificate DecodeCertificate (string s)
{
return 0;
}
- X509Stores stores = userStore ? X509StoreManager.CurrentUser : X509StoreManager.LocalMachine;
- X509CertificateCollection trusted = stores.TrustedRoot.Certificates;
+ X509Stores stores;
+ if (userStore)
+ stores = btlsStore ? X509StoreManager.NewCurrentUser : X509StoreManager.CurrentUser;
+ else
+ stores = btlsStore ? X509StoreManager.NewLocalMachine : X509StoreManager.LocalMachine;
+ X509Store store = stores.TrustedRoot;
+ X509CertificateCollection trusted = store.Certificates;
int additions = 0;
WriteLine ("I already trust {0}, your new list has {1}", trusted.Count, roots.Count);
foreach (X509Certificate root in roots) {
if (!trusted.Contains (root)) {
try {
- stores.TrustedRoot.Import (root);
+ store.Import (root);
WriteLine ("Certificate added: {0}", root.SubjectName);
additions++;
} catch (Exception e) {
WriteLine ("{0} previously trusted certificates were removed.", removed.Count);
foreach (X509Certificate old in removed) {
- stores.TrustedRoot.Remove (old);
+ store.Remove (old);
WriteLine ("Certificate removed: {0}", old.SubjectName);
}
}
case "--user":
userStore = true;
break;
+ case "--legacy":
+ btlsStore = false;
+ break;
default:
WriteLine ("Unknown option '{0}'.", args[i]);
return false;
}
}
}
-}
\ No newline at end of file
+}
sgen_dirs = sgen
endif
+if BTLS
+btls_dirs = btls
+endif
+
if CROSS_COMPILING
-SUBDIRS = arch utils io-layer cil metadata $(sgen_dirs) mini dis profiler
+SUBDIRS = $(btls_dirs) arch utils io-layer cil metadata $(sgen_dirs) mini dis profiler
else
if INSTALL_MONOTOUCH
-SUBDIRS = arch utils io-layer metadata $(sgen_dirs) mini profiler
+SUBDIRS = $(btls_dirs) arch utils io-layer metadata $(sgen_dirs) mini profiler
monotouch-do-build:
@list='$(SUBDIRS)'; for subdir in $$list; do \
(cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$target); \
done;
else
-SUBDIRS = arch utils io-layer cil metadata $(sgen_dirs) mini dis tests unit-tests benchmark profiler
+SUBDIRS = $(btls_dirs) arch utils io-layer cil metadata $(sgen_dirs) mini dis tests unit-tests benchmark profiler
endif
endif
-DIST_SUBDIRS = arch utils io-layer cil metadata $(sgen_dirs) mini dis tests unit-tests benchmark profiler
+DIST_SUBDIRS = btls arch utils io-layer cil metadata $(sgen_dirs) mini dis tests unit-tests benchmark profiler
--- /dev/null
+Makefile
+Makefile.in
+build/
+martin-test
+build-shared/
+build-static/
--- /dev/null
+cmake_minimum_required (VERSION 2.8.10)
+
+project (mono-btls)
+
+if(POLICY CMP0026)
+cmake_policy(SET CMP0026 NEW)
+endif()
+if(POLICY CMP0042)
+cmake_policy(SET CMP0042 NEW)
+endif()
+
+enable_language(C)
+enable_language(CXX)
+
+# FIXME: cmake's asm detection is broken when using xcrun.
+set (CMAKE_ASM_COMPILER "${CMAKE_C_COMPILER}")
+set (CMAKE_ASM_COMPILER_ARG1 "${CMAKE_C_COMPILER_ARG1}")
+set (CMAKE_ASM_COMPILER_ID "${CMAKE_C_COMPILER_ID}")
+enable_language(ASM)
+
+if (NOT "${BTLS_ARCH}" STREQUAL "")
+ message (WARNING "SET ARCH: ${BTLS_ARCH}")
+ set (CMAKE_SYSTEM_PROCESSOR "${BTLS_ARCH}")
+endif ()
+set (C_CXX_FLAGS "-Wall -Wsign-compare -Wmissing-field-initializers -ggdb -fvisibility=hidden")
+set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_CXX_FLAGS} ${BTLS_CFLAGS}")
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${C_CXX_FLAGS} ${BTLS_CFLAGS}")
+set (CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${BTLS_CFLAGS}")
+set (CMAKE_MACOSX_RPATH 1)
+set (MONO_BTLS 1)
+
+add_subdirectory (${BTLS_ROOT} boringssl)
+
+include_directories (
+ ${SRC_DIR}
+ ${BTLS_ROOT}/include
+)
+
+set (
+ MONO_BTLS_SOURCES
+
+ btls-bio.c
+ btls-bio.h
+ btls-error.c
+ btls-error.h
+ btls-key.c
+ btls-key.h
+ btls-pkcs12.c
+ btls-pkcs12.h
+ btls-ssl-ctx.c
+ btls-ssl-ctx.h
+ btls-ssl.c
+ btls-ssl.h
+ btls-util.c
+ btls-util.h
+ btls-x509-chain.c
+ btls-x509-chain.h
+ btls-x509-crl.c
+ btls-x509-crl.h
+ btls-x509-lookup.c
+ btls-x509-lookup.h
+ btls-x509-lookup-mono.c
+ btls-x509-lookup-mono.h
+ btls-x509-name.c
+ btls-x509-name.h
+ btls-x509-revoked.c
+ btls-x509-revoked.h
+ btls-x509-store-ctx.c
+ btls-x509-store-ctx.h
+ btls-x509-store.c
+ btls-x509-store.h
+ btls-x509-verify-param.c
+ btls-x509-verify-param.h
+ btls-x509.c
+ btls-x509.h
+
+ btls-android-utils.c
+
+ ${BORINGSSL_OBJECTS}
+)
+
+if (BUILD_SHARED_LIBS)
+ add_library (mono-btls-shared SHARED ${MONO_BTLS_SOURCES})
+ set_target_properties (mono-btls-shared PROPERTIES RULE_LAUNCH_LINK
+ "${PROJECT_SOURCE_DIR}/create-object-library.sh ${CMAKE_BINARY_DIR} mono-btls-shared.txt mono-btls-shared-lo.txt libmono-btls-shared.a shared ${CMAKE_AR} ${CMAKE_RANLIB} <OBJECTS> --"
+ )
+else ()
+ add_library (mono-btls-static STATIC ${MONO_BTLS_SOURCES})
+ set_target_properties (mono-btls-static PROPERTIES RULE_LAUNCH_LINK
+ "${PROJECT_SOURCE_DIR}/create-object-library.sh ${CMAKE_BINARY_DIR} mono-btls-static.txt mono-btls-static-lo.txt libmono-btls-static.a static ${CMAKE_AR} ${CMAKE_RANLIB} <OBJECTS> --"
+ )
+endif ()
+
--- /dev/null
+BTLS_STATIC_LIST = build-static/mono-btls-static-lo.txt
+BTLS_SHARED_LIST = build-shared/mono-btls-shared-lo.txt
+
+BTLS_DEPS = $(BTLS_LIBS) build-shared/Makefile build-static/Makefile
+
+CMAKE_VERBOSE=$(if $(V),VERBOSE=1,)
+
+CMAKE_ARGS = -D CMAKE_INSTALL_PREFIX:PATH=$(prefix) -D BTLS_ROOT:PATH=$(BTLS_ROOT) \
+ -D SRC_DIR:PATH=$(abs_top_srcdir)/mono/btls -D BTLS_CFLAGS:STRING="$(BTLS_CFLAGS)"
+
+all-local: $(BTLS_STATIC_LIST) $(BTLS_SHARED_LIST)
+
+build-shared/Makefile:
+ -mkdir -p build-shared
+ (cd build-shared && $(CMAKE) $(CMAKE_ARGS) $(BTLS_CMAKE_ARGS) -DBUILD_SHARED_LIBS=1 $(abs_top_srcdir)/mono/btls)
+
+build-static/Makefile:
+ -mkdir -p build-static
+ (cd build-static && $(CMAKE) $(CMAKE_ARGS) $(BTLS_CMAKE_ARGS) $(abs_top_srcdir)/mono/btls)
+
+$(BTLS_STATIC_LIST): build-static/Makefile
+ $(MAKE) -C build-static $(CMAKE_VERBOSE)
+
+$(BTLS_SHARED_LIST): build-shared/Makefile
+ $(MAKE) -C build-shared $(CMAKE_VERBOSE)
+
+clean-local:
+ -rm -rf build-static
+ -rm -rf build-shared
+
--- /dev/null
+// Copied from Chromium: https://src.chromium.org/svn/trunk/src/base/os_compat_android.cc
+
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if defined(__ANDROID__)
+
+#include <asm/unistd.h>
+#include <errno.h>
+#include <math.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+
+#if !defined(__LP64__)
+#include <time64.h>
+#endif
+
+#if !defined(__LP64__)
+// 32-bit Android has only timegm64() and not timegm().
+// We replicate the behaviour of timegm() when the result overflows time_t.
+time_t timegm(struct tm* const t) {
+ // time_t is signed on Android.
+ static const time_t kTimeMax = ~(1L << (sizeof(time_t) * CHAR_BIT - 1));
+ static const time_t kTimeMin = (1L << (sizeof(time_t) * CHAR_BIT - 1));
+ time64_t result = timegm64(t);
+ if (result < kTimeMin || result > kTimeMax)
+ return -1;
+ return result;
+}
+#endif
+
+#endif
--- /dev/null
+//
+// btls-bio.c
+// MonoBtls
+//
+// Created by Martin Baulig on 14/11/15.
+// Copyright (c) 2015 Xamarin. All rights reserved.
+//
+
+#include <btls-ssl.h>
+#include <btls-bio.h>
+#include <errno.h>
+
+struct MonoBtlsBio {
+ const void *instance;
+ MonoBtlsReadFunc read_func;
+ MonoBtlsWriteFunc write_func;
+ MonoBtlsControlFunc control_func;
+};
+
+#if 0
+static void
+mono_debug (const char *message)
+{
+ BIO *bio_err;
+ bio_err = BIO_new_fp (stderr, BIO_NOCLOSE);
+ fprintf (stderr, "DEBUG: %s\n", message);
+ ERR_print_errors (bio_err);
+}
+#endif
+
+static int
+mono_read (BIO *bio, char *out, int outl)
+{
+ MonoBtlsBio *mono = (MonoBtlsBio *)bio->ptr;
+ int ret, wantMore;
+
+ if (!mono)
+ return -1;
+
+ ret = mono->read_func (mono->instance, out, outl, &wantMore);
+
+ if (ret < 0)
+ return -1;
+ if (ret > 0)
+ return ret;
+
+ if (wantMore) {
+ errno = EAGAIN;
+ BIO_set_retry_read (bio);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+mono_write (BIO *bio, const char *in, int inl)
+{
+ MonoBtlsBio *mono = (MonoBtlsBio *)bio->ptr;
+
+ if (!mono)
+ return -1;
+
+ return mono->write_func (mono->instance, in, inl);
+}
+
+static long
+mono_ctrl (BIO *bio, int cmd, long num, void *ptr)
+{
+ MonoBtlsBio *mono = (MonoBtlsBio *)bio->ptr;
+
+ if (!mono)
+ return -1;
+
+ // fprintf (stderr, "mono_ctrl: %x - %lx - %p\n", cmd, num, ptr);
+ switch (cmd) {
+ case BIO_CTRL_FLUSH:
+ return mono->control_func (mono->instance, MONO_BTLS_CONTROL_COMMAND_FLUSH, 0);
+ default:
+ return -1;
+ }
+ return -1;
+}
+
+static int
+mono_new (BIO *bio)
+{
+ // mono_debug("mono_new!\n");
+ bio->init = 0;
+ bio->num = -1;
+ bio->flags = 0;
+ return 1;
+}
+
+static int
+mono_free (BIO *bio)
+{
+ // mono_debug ("mono_free!\n");
+ if (bio->ptr) {
+ MonoBtlsBio *mono = (MonoBtlsBio *)bio->ptr;
+
+ bio->ptr = NULL;
+ mono->instance = NULL;
+ mono->read_func = NULL;
+ mono->write_func = NULL;
+ mono->control_func = NULL;
+ free (mono);
+ }
+ return 1;
+}
+
+static const BIO_METHOD mono_method = {
+ BIO_TYPE_NONE, "mono", mono_write, mono_read,
+ NULL, NULL, mono_ctrl, mono_new, mono_free, NULL
+};
+
+BIO *
+mono_btls_bio_mono_new (void)
+{
+ BIO *bio;
+ MonoBtlsBio *monoBio;
+
+ bio = BIO_new (&mono_method);
+ if (!bio)
+ return NULL;
+
+ monoBio = calloc (1, sizeof (MonoBtlsBio));
+ if (!monoBio) {
+ BIO_free (bio);
+ return NULL;
+ }
+
+ bio->ptr = monoBio;
+ bio->init = 0;
+
+ return bio;
+}
+
+void
+mono_btls_bio_mono_initialize (BIO *bio, const void *instance,
+ MonoBtlsReadFunc read_func, MonoBtlsWriteFunc write_func,
+ MonoBtlsControlFunc control_func)
+{
+ MonoBtlsBio *monoBio = bio->ptr;
+
+ monoBio->instance = instance;
+ monoBio->read_func = read_func;
+ monoBio->write_func = write_func;
+ monoBio->control_func = control_func;
+
+ bio->init = 1;
+}
+
+int
+mono_btls_bio_read (BIO *bio, void *data, int len)
+{
+ return BIO_read (bio, data, len);
+}
+
+int
+mono_btls_bio_write (BIO *bio, const void *data, int len)
+{
+ return BIO_write (bio, data, len);
+}
+
+int
+mono_btls_bio_flush (BIO *bio)
+{
+ return BIO_flush (bio);
+}
+
+int
+mono_btls_bio_indent (BIO *bio, unsigned indent, unsigned max_indent)
+{
+ return BIO_indent (bio, indent, max_indent);
+}
+
+int
+mono_btls_bio_hexdump (BIO *bio, const uint8_t *data, int len, unsigned indent)
+{
+ return BIO_hexdump (bio, data, len, indent);
+}
+
+void
+mono_btls_bio_print_errors (BIO *bio)
+{
+ BIO_print_errors (bio);
+}
+
+void
+mono_btls_bio_free (BIO *bio)
+{
+ BIO_free (bio);
+}
+
+BIO *
+mono_btls_bio_mem_new (void)
+{
+ return BIO_new (BIO_s_mem ());
+}
+
+int
+mono_btls_bio_mem_get_data (BIO *bio, void **data)
+{
+ return (int)BIO_get_mem_data (bio, (char**)data);
+}
--- /dev/null
+//
+// btls-bio.h
+// MonoBtls
+//
+// Created by Martin Baulig on 14/11/15.
+// Copyright (c) 2015 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_bio__
+#define __btls__btls_bio__
+
+#include <stdio.h>
+#include <btls-ssl.h>
+
+typedef enum {
+ MONO_BTLS_CONTROL_COMMAND_FLUSH = 1
+} MonoBtlsControlCommand;
+
+typedef int (* MonoBtlsReadFunc) (const void *instance, const void *buf, int size, int *wantMore);
+typedef int (* MonoBtlsWriteFunc) (const void *instance, const void *buf, int size);
+typedef long (* MonoBtlsControlFunc) (const void *instance, MonoBtlsControlCommand command, long arg);
+
+BIO *
+mono_btls_bio_mono_new (void);
+
+void
+mono_btls_bio_mono_initialize (BIO *bio, const void *instance,
+ MonoBtlsReadFunc read_func, MonoBtlsWriteFunc write_func,
+ MonoBtlsControlFunc control_func);
+
+int
+mono_btls_bio_read (BIO *bio, void *data, int len);
+
+int
+mono_btls_bio_write (BIO *bio, const void *data, int len);
+
+int
+mono_btls_bio_flush (BIO *bio);
+
+int
+mono_btls_bio_indent (BIO *bio, unsigned indent, unsigned max_indent);
+
+int
+mono_btls_bio_hexdump (BIO *bio, const uint8_t *data, int len, unsigned indent);
+
+void
+mono_btls_bio_print_errors (BIO *bio);
+
+void
+mono_btls_bio_free (BIO *bio);
+
+BIO *
+mono_btls_bio_mem_new (void);
+
+int
+mono_btls_bio_mem_get_data (BIO *bio, void **data);
+
+#endif /* defined(__btls__btls_bio__) */
--- /dev/null
+//
+// btls-error.c
+// MonoBtls
+//
+// Created by Martin Baulig on 6/19/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#include <btls-error.h>
+#include <assert.h>
+
+int
+mono_btls_error_peek_error (void)
+{
+ return ERR_peek_error ();
+}
+
+int
+mono_btls_error_get_error (void)
+{
+ return ERR_get_error ();
+}
+
+void
+mono_btls_error_clear_error (void)
+{
+ ERR_clear_error ();
+}
+
+void
+mono_btls_error_get_error_string_n (int error, char *buf, int len)
+{
+ ERR_error_string_n (error, buf, len);
+}
+
--- /dev/null
+//
+// btls-util.h
+// MonoBtls
+//
+// Created by Martin Baulig on 3/23/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_error__
+#define __btls__btls_error__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/ssl.h>
+
+int
+mono_btls_error_peek_error (void);
+
+int
+mono_btls_error_get_error (void);
+
+void
+mono_btls_error_clear_error (void);
+
+void
+mono_btls_error_get_error_string_n (int error, char *buf, int len);
+
+#endif /* __btls__btls_error__ */
--- /dev/null
+//
+// btls-key.c
+// MonoBtls
+//
+// Created by Martin Baulig on 3/7/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#include <btls-key.h>
+
+void
+mono_btls_key_free (EVP_PKEY *pkey)
+{
+ EVP_PKEY_free (pkey);
+}
+
+EVP_PKEY *
+mono_btls_key_up_ref (EVP_PKEY *pkey)
+{
+ return EVP_PKEY_up_ref (pkey);
+}
+
+int
+mono_btls_key_get_bits (EVP_PKEY *pkey)
+{
+ return EVP_PKEY_bits (pkey);
+}
+
+int
+mono_btls_key_is_rsa (EVP_PKEY *pkey)
+{
+ return pkey->type == EVP_PKEY_RSA;
+}
+
+int
+mono_btls_key_get_bytes (EVP_PKEY *pkey, uint8_t **buffer, int *size, int include_private_bits)
+{
+ size_t len;
+ RSA *rsa;
+ int ret;
+
+ *size = 0;
+ *buffer = NULL;
+
+ if (pkey->type != EVP_PKEY_RSA)
+ return 0;
+
+ rsa = EVP_PKEY_get1_RSA (pkey);
+ if (!rsa)
+ return 0;
+
+ if (include_private_bits)
+ ret = RSA_private_key_to_bytes (buffer, &len, rsa);
+ else
+ ret = RSA_public_key_to_bytes (buffer, &len, rsa);
+
+ if (ret != 1)
+ return 0;
+
+ *size = (int)len;
+ return 1;
+}
--- /dev/null
+//
+// btls-key.h
+// MonoBtls
+//
+// Created by Martin Baulig on 3/7/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_key__
+#define __btls__btls_key__
+
+#include <stdio.h>
+#include <btls-ssl.h>
+#include <btls-x509.h>
+
+void
+mono_btls_key_free (EVP_PKEY *pkey);
+
+EVP_PKEY *
+mono_btls_key_up_ref (EVP_PKEY *pkey);
+
+int
+mono_btls_key_get_bits (EVP_PKEY *pkey);
+
+int
+mono_btls_key_is_rsa (EVP_PKEY *pkey);
+
+int
+mono_btls_key_get_bytes (EVP_PKEY *pkey, uint8_t **buffer, int *size, int include_private_bits);
+
+#endif /* __btls__btls_key__ */
+
--- /dev/null
+//
+// btls-pkcs12.c
+// MonoBtls
+//
+// Created by Martin Baulig on 3/8/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#include <btls-pkcs12.h>
+#include <openssl/pkcs12.h>
+
+struct MonoBtlsPkcs12 {
+ STACK_OF(X509) *certs;
+ EVP_PKEY *private_key;
+ CRYPTO_refcount_t references;
+};
+
+MonoBtlsPkcs12 *
+mono_btls_pkcs12_new (void)
+{
+ MonoBtlsPkcs12 *pkcs12 = (MonoBtlsPkcs12 *)OPENSSL_malloc (sizeof (MonoBtlsPkcs12));
+ if (pkcs12 == NULL)
+ return NULL;
+
+ memset (pkcs12, 0, sizeof(MonoBtlsPkcs12));
+ pkcs12->certs = sk_X509_new_null ();
+ pkcs12->references = 1;
+ return pkcs12;
+}
+
+int
+mono_btls_pkcs12_get_count (MonoBtlsPkcs12 *pkcs12)
+{
+ return (int)sk_X509_num (pkcs12->certs);
+}
+
+X509 *
+mono_btls_pkcs12_get_cert (MonoBtlsPkcs12 *pkcs12, int index)
+{
+ X509 *cert;
+
+ if ((size_t)index >= sk_X509_num (pkcs12->certs))
+ return NULL;
+ cert = sk_X509_value (pkcs12->certs, index);
+ if (cert)
+ X509_up_ref (cert);
+ return cert;
+}
+
+STACK_OF(X509) *
+mono_btls_pkcs12_get_certs (MonoBtlsPkcs12 *pkcs12)
+{
+ return pkcs12->certs;
+}
+
+int
+mono_btls_pkcs12_free (MonoBtlsPkcs12 *pkcs12)
+{
+ if (!CRYPTO_refcount_dec_and_test_zero (&pkcs12->references))
+ return 0;
+
+ sk_X509_pop_free (pkcs12->certs, X509_free);
+ OPENSSL_free (pkcs12);
+ return 1;
+}
+
+MonoBtlsPkcs12 *
+mono_btls_pkcs12_up_ref (MonoBtlsPkcs12 *pkcs12)
+{
+ CRYPTO_refcount_inc (&pkcs12->references);
+ return pkcs12;
+}
+
+void
+mono_btls_pkcs12_add_cert (MonoBtlsPkcs12 *pkcs12, X509 *x509)
+{
+ X509_up_ref (x509);
+ sk_X509_push (pkcs12->certs, x509);
+}
+
+int
+mono_btls_pkcs12_import (MonoBtlsPkcs12 *pkcs12, const void *data, int len, const void *password)
+{
+ CBS cbs;
+ CBS_init (&cbs, data, len);
+ return PKCS12_get_key_and_certs (&pkcs12->private_key, pkcs12->certs, &cbs, password);
+}
+
+int
+mono_btls_pkcs12_has_private_key (MonoBtlsPkcs12 *pkcs12)
+{
+ return pkcs12->private_key != NULL;
+}
+
+EVP_PKEY *
+mono_btls_pkcs12_get_private_key (MonoBtlsPkcs12 *pkcs12)
+{
+ if (!pkcs12->private_key)
+ return NULL;
+ return EVP_PKEY_up_ref (pkcs12->private_key);
+}
--- /dev/null
+//
+// btls-pkcs12.h
+// MonoBtls
+//
+// Created by Martin Baulig on 3/8/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_pkcs12__
+#define __btls__btls_pkcs12__
+
+#include <stdio.h>
+#include <btls-ssl.h>
+#include <btls-x509.h>
+
+MonoBtlsPkcs12 *
+mono_btls_pkcs12_new (void);
+
+int
+mono_btls_pkcs12_get_count (MonoBtlsPkcs12 *pkcs12);
+
+X509 *
+mono_btls_pkcs12_get_cert (MonoBtlsPkcs12 *pkcs12, int index);
+
+STACK_OF(X509) *
+mono_btls_pkcs12_get_certs (MonoBtlsPkcs12 *pkcs12);
+
+int
+mono_btls_pkcs12_free (MonoBtlsPkcs12 *pkcs12);
+
+MonoBtlsPkcs12 *
+mono_btls_pkcs12_up_ref (MonoBtlsPkcs12 *pkcs12);
+
+void
+mono_btls_pkcs12_add_cert (MonoBtlsPkcs12 *pkcs12, X509 *x509);
+
+int
+mono_btls_pkcs12_import (MonoBtlsPkcs12 *pkcs12, const void *data, int len, const void *password);
+
+int
+mono_btls_pkcs12_has_private_key (MonoBtlsPkcs12 *pkcs12);
+
+EVP_PKEY *
+mono_btls_pkcs12_get_private_key (MonoBtlsPkcs12 *pkcs12);
+
+#endif /* __btls__btls_pkcs12__ */
--- /dev/null
+//
+// btls-ssl-ctx.c
+// MonoBtls
+//
+// Created by Martin Baulig on 4/11/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#include <btls-ssl-ctx.h>
+#include <btls-x509-verify-param.h>
+
+struct MonoBtlsSslCtx {
+ CRYPTO_refcount_t references;
+ SSL_CTX *ctx;
+ BIO *bio;
+ BIO *debug_bio;
+ void *instance;
+ MonoBtlsVerifyFunc verify_func;
+ MonoBtlsSelectFunc select_func;
+};
+
+#define debug_print(ptr,message) \
+do { if (mono_btls_ssl_ctx_is_debug_enabled(ptr)) \
+mono_btls_ssl_ctx_debug_printf (ptr, "%s:%d:%s(): " message, __FILE__, __LINE__, \
+ __func__); } while (0)
+
+#define debug_printf(ptr,fmt, ...) \
+do { if (mono_btls_ssl_ctx_is_debug_enabled(ptr)) \
+mono_btls_ssl_ctx_debug_printf (ptr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, \
+ __func__, __VA_ARGS__); } while (0)
+
+void ssl_cipher_preference_list_free (struct ssl_cipher_preference_list_st *cipher_list);
+
+int
+mono_btls_ssl_ctx_is_debug_enabled (MonoBtlsSslCtx *ctx)
+{
+ return ctx->debug_bio != NULL;
+}
+
+int
+mono_btls_ssl_ctx_debug_printf (MonoBtlsSslCtx *ctx, const char *format, ...)
+{
+ va_list args;
+ int ret;
+
+ if (!ctx->debug_bio)
+ return 0;
+
+ va_start (args, format);
+ ret = mono_btls_debug_printf (ctx->debug_bio, format, args);
+ va_end (args);
+ return ret;
+}
+
+MonoBtlsSslCtx *
+mono_btls_ssl_ctx_new (void)
+{
+ MonoBtlsSslCtx *ctx;
+
+ ctx = OPENSSL_malloc (sizeof (MonoBtlsSslCtx));
+ if (!ctx)
+ return NULL;
+
+ memset (ctx, 0, sizeof (MonoBtlsSslCtx));
+ ctx->references = 1;
+ ctx->ctx = SSL_CTX_new (TLS_method ());
+ return ctx;
+}
+
+MonoBtlsSslCtx *
+mono_btls_ssl_ctx_up_ref (MonoBtlsSslCtx *ctx)
+{
+ CRYPTO_refcount_inc (&ctx->references);
+ return ctx;
+}
+
+int
+mono_btls_ssl_ctx_free (MonoBtlsSslCtx *ctx)
+{
+ if (!CRYPTO_refcount_dec_and_test_zero (&ctx->references))
+ return 0;
+ SSL_CTX_free (ctx->ctx);
+ ctx->instance = NULL;
+ OPENSSL_free (ctx);
+ return 1;
+}
+
+SSL_CTX *
+mono_btls_ssl_ctx_get_ctx (MonoBtlsSslCtx *ctx)
+{
+ return ctx->ctx;
+}
+
+void
+mono_btls_ssl_ctx_set_debug_bio (MonoBtlsSslCtx *ctx, BIO *debug_bio)
+{
+ if (debug_bio)
+ ctx->debug_bio = BIO_up_ref(debug_bio);
+ else
+ ctx->debug_bio = NULL;
+}
+
+void
+mono_btls_ssl_ctx_initialize (MonoBtlsSslCtx *ctx, void *instance)
+{
+ ctx->instance = instance;
+}
+
+static int
+cert_verify_callback (X509_STORE_CTX *storeCtx, void *arg)
+{
+ MonoBtlsSslCtx *ptr = (MonoBtlsSslCtx*)arg;
+ int ret;
+
+ debug_printf (ptr, "cert_verify_callback(): %p\n", ptr->verify_func);
+ ret = X509_verify_cert (storeCtx);
+ debug_printf (ptr, "cert_verify_callback() #1: %d\n", ret);
+
+ if (ptr->verify_func)
+ ret = ptr->verify_func (ptr->instance, ret, storeCtx);
+
+ return ret;
+}
+
+void
+mono_btls_ssl_ctx_set_cert_verify_callback (MonoBtlsSslCtx *ptr, MonoBtlsVerifyFunc func, int cert_required)
+{
+ int mode;
+
+ ptr->verify_func = func;
+ SSL_CTX_set_cert_verify_callback (ptr->ctx, cert_verify_callback, ptr);
+
+ mode = SSL_VERIFY_PEER;
+ if (cert_required)
+ mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+
+ SSL_CTX_set_verify (ptr->ctx, mode, NULL);
+}
+
+static int
+cert_select_callback (SSL *ssl, void *arg)
+{
+ MonoBtlsSslCtx *ptr = (MonoBtlsSslCtx*)arg;
+ int ret = 1;
+
+ debug_printf (ptr, "cert_select_callback(): %p\n", ptr->select_func);
+ if (ptr->select_func)
+ ret = ptr->select_func (ptr->instance);
+ debug_printf (ptr, "cert_select_callback() #1: %d\n", ret);
+
+ return ret;
+}
+
+void
+mono_btls_ssl_ctx_set_cert_select_callback (MonoBtlsSslCtx *ptr, MonoBtlsSelectFunc func)
+{
+ ptr->select_func = func;
+ SSL_CTX_set_cert_cb (ptr->ctx, cert_select_callback, ptr);
+}
+
+X509_STORE *
+mono_btls_ssl_ctx_peek_store (MonoBtlsSslCtx *ctx)
+{
+ return SSL_CTX_get_cert_store (ctx->ctx);
+}
+
+void
+mono_btls_ssl_ctx_set_min_version (MonoBtlsSslCtx *ctx, int version)
+{
+ SSL_CTX_set_min_version (ctx->ctx, version);
+}
+
+void
+mono_btls_ssl_ctx_set_max_version (MonoBtlsSslCtx *ctx, int version)
+{
+ SSL_CTX_set_max_version (ctx->ctx, version);
+}
+
+int
+mono_btls_ssl_ctx_is_cipher_supported (MonoBtlsSslCtx *ctx, uint16_t value)
+{
+ const SSL_CIPHER *cipher;
+
+ cipher = SSL_get_cipher_by_value (value);
+ return cipher != NULL;
+}
+
+int
+mono_btls_ssl_ctx_set_ciphers (MonoBtlsSslCtx *ctx, int count, const uint16_t *data,
+ int allow_unsupported)
+{
+ STACK_OF(SSL_CIPHER) *ciphers = NULL;
+ struct ssl_cipher_preference_list_st *pref_list = NULL;
+ uint8_t *in_group_flags = NULL;
+ int i;
+
+ ciphers = sk_SSL_CIPHER_new_null ();
+ if (!ciphers)
+ goto err;
+
+ for (i = 0; i < count; i++) {
+ const SSL_CIPHER *cipher = SSL_get_cipher_by_value (data [i]);
+ if (!cipher) {
+ debug_printf (ctx, "mono_btls_ssl_ctx_set_ciphers(): unknown cipher %02x", data [i]);
+ if (!allow_unsupported)
+ goto err;
+ continue;
+ }
+ if (!sk_SSL_CIPHER_push (ciphers, cipher))
+ goto err;
+ }
+
+ pref_list = OPENSSL_malloc (sizeof (struct ssl_cipher_preference_list_st));
+ if (!pref_list)
+ goto err;
+
+ memset (pref_list, 0, sizeof (struct ssl_cipher_preference_list_st));
+ pref_list->ciphers = sk_SSL_CIPHER_dup (ciphers);
+ if (!pref_list->ciphers)
+ goto err;
+ pref_list->in_group_flags = OPENSSL_malloc (sk_SSL_CIPHER_num (ciphers));
+ if (!pref_list->in_group_flags)
+ goto err;
+
+ if (ctx->ctx->cipher_list)
+ ssl_cipher_preference_list_free (ctx->ctx->cipher_list);
+ if (ctx->ctx->cipher_list_by_id)
+ sk_SSL_CIPHER_free (ctx->ctx->cipher_list_by_id);
+ if (ctx->ctx->cipher_list_tls10) {
+ ssl_cipher_preference_list_free (ctx->ctx->cipher_list_tls10);
+ ctx->ctx->cipher_list_tls10 = NULL;
+ }
+ if (ctx->ctx->cipher_list_tls11) {
+ ssl_cipher_preference_list_free (ctx->ctx->cipher_list_tls11);
+ ctx->ctx->cipher_list_tls11 = NULL;
+ }
+
+ ctx->ctx->cipher_list = pref_list;
+ ctx->ctx->cipher_list_by_id = ciphers;
+
+ return (int)sk_SSL_CIPHER_num (ciphers);
+
+err:
+ sk_SSL_CIPHER_free (ciphers);
+ OPENSSL_free (pref_list);
+ OPENSSL_free (in_group_flags);
+ return 0;
+}
+
+int
+mono_btls_ssl_ctx_set_verify_param (MonoBtlsSslCtx *ctx, const MonoBtlsX509VerifyParam *param)
+{
+ return SSL_CTX_set1_param (ctx->ctx, mono_btls_x509_verify_param_peek_param (param));
+}
+
--- /dev/null
+//
+// btls-ssl-ctx.h
+// MonoBtls
+//
+// Created by Martin Baulig on 4/11/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#ifndef __btls_ssl_ctx__btls_ssl_ctx__
+#define __btls_ssl_ctx__btls_ssl_ctx__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/ssl.h>
+#include <btls-util.h>
+
+typedef struct MonoBtlsBio MonoBtlsBio;
+typedef struct MonoBtlsX509Chain MonoBtlsX509Chain;
+typedef struct MonoBtlsX509Crl MonoBtlsX509Crl;
+typedef struct MonoBtlsX509Lookup MonoBtlsX509Lookup;
+typedef struct MonoBtlsX509LookupMono MonoBtlsX509LookupMono;
+typedef struct MonoBtlsX509Name MonoBtlsX509Name;
+typedef struct MonoBtlsX509Store MonoBtlsX509Store;
+typedef struct MonoBtlsX509StoreCtx MonoBtlsX509StoreCtx;
+typedef struct MonoBtlsX509Revoked MonoBtlsX509Revoked;
+typedef struct MonoBtlsX509VerifyParam MonoBtlsX509VerifyParam;
+typedef struct MonoBtlsPkcs12 MonoBtlsPkcs12;
+typedef struct MonoBtlsSsl MonoBtlsSsl;
+typedef struct MonoBtlsSslCtx MonoBtlsSslCtx;
+
+typedef int (* MonoBtlsVerifyFunc) (void *instance, int preverify_ok, X509_STORE_CTX *ctx);
+typedef int (* MonoBtlsSelectFunc) (void *instance);
+
+MonoBtlsSslCtx *
+mono_btls_ssl_ctx_new (void);
+
+MonoBtlsSslCtx *
+mono_btls_ssl_ctx_up_ref (MonoBtlsSslCtx *ctx);
+
+int
+mono_btls_ssl_ctx_free (MonoBtlsSslCtx *ctx);
+
+void
+mono_btls_ssl_ctx_initialize (MonoBtlsSslCtx *ctx, void *instance);
+
+SSL_CTX *
+mono_btls_ssl_ctx_get_ctx (MonoBtlsSslCtx *ctx);
+
+int
+mono_btls_ssl_ctx_debug_printf (MonoBtlsSslCtx *ctx, const char *format, ...);
+
+int
+mono_btls_ssl_ctx_is_debug_enabled (MonoBtlsSslCtx *ctx);
+
+void
+mono_btls_ssl_ctx_set_cert_verify_callback (MonoBtlsSslCtx *ptr, MonoBtlsVerifyFunc func, int cert_required);
+
+void
+mono_btls_ssl_ctx_set_cert_select_callback (MonoBtlsSslCtx *ptr, MonoBtlsSelectFunc func);
+
+void
+mono_btls_ssl_ctx_set_debug_bio (MonoBtlsSslCtx *ctx, BIO *debug_bio);
+
+X509_STORE *
+mono_btls_ssl_ctx_peek_store (MonoBtlsSslCtx *ctx);
+
+void
+mono_btls_ssl_ctx_set_min_version (MonoBtlsSslCtx *ctx, int version);
+
+void
+mono_btls_ssl_ctx_set_max_version (MonoBtlsSslCtx *ctx, int version);
+
+int
+mono_btls_ssl_ctx_is_cipher_supported (MonoBtlsSslCtx *ctx, uint16_t value);
+
+int
+mono_btls_ssl_ctx_set_ciphers (MonoBtlsSslCtx *ctx, int count, const uint16_t *data,
+ int allow_unsupported);
+
+int
+mono_btls_ssl_ctx_set_verify_param (MonoBtlsSslCtx *ctx, const MonoBtlsX509VerifyParam *param);
+
+#endif /* __btls_ssl_ctx__btls_ssl_ctx__ */
--- /dev/null
+//
+// btls-ssl.c
+// MonoBtls
+//
+// Created by Martin Baulig on 14/11/15.
+// Copyright (c) 2015 Xamarin. All rights reserved.
+//
+
+#include <btls-ssl.h>
+#include <btls-x509-verify-param.h>
+
+struct MonoBtlsSsl {
+ MonoBtlsSslCtx *ctx;
+ SSL *ssl;
+};
+
+#define debug_print(ptr,message) \
+do { if (mono_btls_ssl_ctx_is_debug_enabled(ptr->ctx)) \
+mono_btls_ssl_ctx_debug_printf (ptr->ctx, "%s:%d:%s(): " message, __FILE__, __LINE__, \
+__func__); } while (0)
+
+#define debug_printf(ptr,fmt, ...) \
+do { if (mono_btls_ssl_ctx_is_debug_enabled(ptr->ctx)) \
+mono_btls_ssl_ctx_debug_printf (ptr->ctx, "%s:%d:%s(): " fmt, __FILE__, __LINE__, \
+__func__, __VA_ARGS__); } while (0)
+
+STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list (SSL *s, const CBS *cbs);
+
+MonoBtlsSsl *
+mono_btls_ssl_new (MonoBtlsSslCtx *ctx)
+{
+ MonoBtlsSsl *ptr;
+
+ ptr = calloc (1, sizeof (MonoBtlsSsl));
+
+ ptr->ctx = mono_btls_ssl_ctx_up_ref (ctx);
+ ptr->ssl = SSL_new (mono_btls_ssl_ctx_get_ctx (ptr->ctx));
+
+ SSL_set_options (ptr->ssl, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+ return ptr;
+}
+
+void
+mono_btls_ssl_destroy (MonoBtlsSsl *ptr)
+{
+ mono_btls_ssl_close (ptr);
+ if (ptr->ssl) {
+ SSL_free (ptr->ssl);
+ ptr->ssl = NULL;
+ }
+ if (ptr->ctx) {
+ mono_btls_ssl_ctx_free (ptr->ctx);
+ ptr->ctx = NULL;
+ }
+ free (ptr);
+}
+
+void
+mono_btls_ssl_close (MonoBtlsSsl *ptr)
+{
+ ;
+}
+
+void
+mono_btls_ssl_set_bio (MonoBtlsSsl *ptr, BIO *bio)
+{
+ BIO_up_ref (bio);
+ SSL_set_bio (ptr->ssl, bio, bio);
+}
+
+void
+mono_btls_ssl_print_errors_cb (ERR_print_errors_callback_t callback, void *ctx)
+{
+ ERR_print_errors_cb (callback, ctx);
+}
+
+int
+mono_btls_ssl_use_certificate (MonoBtlsSsl *ptr, X509 *x509)
+{
+ return SSL_use_certificate (ptr->ssl, x509);
+}
+
+int
+mono_btls_ssl_use_private_key (MonoBtlsSsl *ptr, EVP_PKEY *key)
+{
+ return SSL_use_PrivateKey (ptr->ssl, key);
+}
+
+int
+mono_btls_ssl_add_chain_certificate (MonoBtlsSsl *ptr, X509 *x509)
+{
+ return SSL_add1_chain_cert (ptr->ssl, x509);
+}
+
+int
+mono_btls_ssl_accept (MonoBtlsSsl *ptr)
+{
+ return SSL_accept (ptr->ssl);
+}
+
+int
+mono_btls_ssl_connect (MonoBtlsSsl *ptr)
+{
+ return SSL_connect (ptr->ssl);
+}
+
+int
+mono_btls_ssl_handshake (MonoBtlsSsl *ptr)
+{
+ return SSL_do_handshake (ptr->ssl);
+}
+
+int
+mono_btls_ssl_read (MonoBtlsSsl *ptr, void *buf, int count)
+{
+ return SSL_read (ptr->ssl, buf, count);
+}
+
+int
+mono_btls_ssl_write (MonoBtlsSsl *ptr, void *buf, int count)
+{
+ return SSL_write (ptr->ssl, buf, count);
+}
+
+int
+mono_btls_ssl_get_version (MonoBtlsSsl *ptr)
+{
+ return SSL_version (ptr->ssl);
+}
+
+void
+mono_btls_ssl_set_min_version (MonoBtlsSsl *ptr, int version)
+{
+ SSL_set_min_version (ptr->ssl, version);
+}
+
+void
+mono_btls_ssl_set_max_version (MonoBtlsSsl *ptr, int version)
+{
+ SSL_set_max_version (ptr->ssl, version);
+}
+
+int
+mono_btls_ssl_get_cipher (MonoBtlsSsl *ptr)
+{
+ const SSL_CIPHER *cipher;
+
+ cipher = SSL_get_current_cipher (ptr->ssl);
+ if (!cipher)
+ return 0;
+ return (uint16_t)SSL_CIPHER_get_id (cipher);
+}
+
+int
+mono_btls_ssl_set_cipher_list (MonoBtlsSsl *ptr, const char *str)
+{
+ return SSL_set_cipher_list(ptr->ssl, str);
+}
+
+int
+mono_btls_ssl_get_ciphers (MonoBtlsSsl *ptr, uint16_t **data)
+{
+ STACK_OF(SSL_CIPHER) *ciphers;
+ int count, i;
+
+ *data = NULL;
+
+ ciphers = SSL_get_ciphers (ptr->ssl);
+ if (!ciphers)
+ return 0;
+
+ count = (int)sk_SSL_CIPHER_num (ciphers);
+
+ *data = OPENSSL_malloc (2 * count);
+ if (!*data)
+ return 0;
+
+ for (i = 0; i < count; i++) {
+ const SSL_CIPHER *cipher = sk_SSL_CIPHER_value (ciphers, i);
+ (*data) [i] = (uint16_t) SSL_CIPHER_get_id (cipher);
+ }
+
+ return count;
+}
+
+X509 *
+mono_btls_ssl_get_peer_certificate (MonoBtlsSsl *ptr)
+{
+ return SSL_get_peer_certificate (ptr->ssl);
+}
+
+int
+mono_btls_ssl_get_error (MonoBtlsSsl *ptr, int ret_code)
+{
+ return SSL_get_error (ptr->ssl, ret_code);
+}
+
+int
+mono_btls_ssl_set_verify_param (MonoBtlsSsl *ptr, const MonoBtlsX509VerifyParam *param)
+{
+ return SSL_set1_param (ptr->ssl, mono_btls_x509_verify_param_peek_param (param));
+}
+
+int
+mono_btls_ssl_set_server_name (MonoBtlsSsl *ptr, const char *name)
+{
+ return SSL_set_tlsext_host_name (ptr->ssl, name);
+}
--- /dev/null
+//
+// btls-ssl.h
+// MonoBtls
+//
+// Created by Martin Baulig on 14/11/15.
+// Copyright (c) 2015 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_ssl__
+#define __btls__btls_ssl__
+
+#include <btls-ssl-ctx.h>
+
+MonoBtlsSsl *
+mono_btls_ssl_new (MonoBtlsSslCtx *ctx);
+
+int
+mono_btls_ssl_use_certificate (MonoBtlsSsl *ptr, X509 *x509);
+
+int
+mono_btls_ssl_use_private_key (MonoBtlsSsl *ptr, EVP_PKEY *key);
+
+int
+mono_btls_ssl_add_chain_certificate (MonoBtlsSsl *ptr, X509 *x509);
+
+int
+mono_btls_ssl_accept (MonoBtlsSsl *ptr);
+
+int
+mono_btls_ssl_connect (MonoBtlsSsl *ptr);
+
+int
+mono_btls_ssl_handshake (MonoBtlsSsl *ptr);
+
+void
+mono_btls_ssl_print_errors_cb (ERR_print_errors_callback_t callback, void *ctx);
+
+void
+mono_btls_ssl_set_bio (MonoBtlsSsl *ptr, BIO *bio);
+
+int
+mono_btls_ssl_read (MonoBtlsSsl *ptr, void *buf, int count);
+
+int
+mono_btls_ssl_write (MonoBtlsSsl *ptr, void *buf, int count);
+
+int
+mono_btls_ssl_get_version (MonoBtlsSsl *ptr);
+
+void
+mono_btls_ssl_set_min_version (MonoBtlsSsl *ptr, int version);
+
+void
+mono_btls_ssl_set_max_version (MonoBtlsSsl *ptr, int version);
+
+int
+mono_btls_ssl_get_cipher (MonoBtlsSsl *ptr);
+
+int
+mono_btls_ssl_set_cipher_list (MonoBtlsSsl *ptr, const char *str);
+
+int
+mono_btls_ssl_get_ciphers (MonoBtlsSsl *ptr, uint16_t **data);
+
+X509 *
+mono_btls_ssl_get_peer_certificate (MonoBtlsSsl *ptr);
+
+void
+mono_btls_ssl_close (MonoBtlsSsl *ptr);
+
+int
+mono_btls_ssl_get_error (MonoBtlsSsl *ptr, int ret_code);
+
+int
+mono_btls_ssl_set_verify_param (MonoBtlsSsl *ptr, const MonoBtlsX509VerifyParam *param);
+
+int
+mono_btls_ssl_set_server_name (MonoBtlsSsl *ptr, const char *name);
+
+void
+mono_btls_ssl_destroy (MonoBtlsSsl *ptr);
+
+#endif /* defined(__btls__btls_ssl__) */
--- /dev/null
+//
+// btls-util.c
+// MonoBtls
+//
+// Created by Martin Baulig on 3/23/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#include <btls-util.h>
+#include <assert.h>
+#include <time.h>
+
+#if defined(__ANDROID__) && !defined(__LP64__)
+#include <time64.h>
+extern time_t timegm (struct tm* const t);
+#endif
+
+extern int asn1_generalizedtime_to_tm (struct tm *tm, const ASN1_GENERALIZEDTIME *d);
+
+void
+mono_btls_free (void *data)
+{
+ OPENSSL_free (data);
+}
+
+long
+mono_btls_util_asn1_time_to_ticks (ASN1_TIME *time)
+{
+ ASN1_GENERALIZEDTIME *gtime;
+ struct tm tm;
+ time_t epoch;
+
+ gtime = ASN1_TIME_to_generalizedtime (time, NULL);
+ asn1_generalizedtime_to_tm (&tm, gtime);
+ ASN1_GENERALIZEDTIME_free (gtime);
+ epoch = timegm(&tm);
+
+ return epoch;
+}
+
+// Copied from crypto/bio/printf.c, takes va_list
+int
+mono_btls_debug_printf (BIO *bio, const char *format, va_list args)
+{
+ char buf[256], *out, out_malloced = 0;
+ int out_len, ret;
+
+ out_len = vsnprintf (buf, sizeof(buf), format, args);
+ if (out_len < 0) {
+ return -1;
+ }
+
+ if ((size_t) out_len >= sizeof(buf)) {
+ const int requested_len = out_len;
+ /* The output was truncated. Note that vsnprintf's return value
+ * does not include a trailing NUL, but the buffer must be sized
+ * for it. */
+ out = OPENSSL_malloc (requested_len + 1);
+ out_malloced = 1;
+ if (out == NULL) {
+ OPENSSL_PUT_ERROR(BIO, ERR_R_MALLOC_FAILURE);
+ return -1;
+ }
+ out_len = vsnprintf (out, requested_len + 1, format, args);
+ assert(out_len == requested_len);
+ } else {
+ out = buf;
+ }
+
+ ret = BIO_write(bio, out, out_len);
+ if (out_malloced) {
+ OPENSSL_free(out);
+ }
+
+ return ret;
+}
--- /dev/null
+//
+// btls-util.h
+// MonoBtls
+//
+// Created by Martin Baulig on 3/23/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_util__
+#define __btls__btls_util__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/ssl.h>
+
+void
+mono_btls_free (void *data);
+
+long
+mono_btls_util_asn1_time_to_ticks (ASN1_TIME *time);
+
+int
+mono_btls_debug_printf (BIO *bio, const char *format, va_list args);
+
+OPENSSL_EXPORT void CRYPTO_refcount_inc(CRYPTO_refcount_t *count);
+OPENSSL_EXPORT int CRYPTO_refcount_dec_and_test_zero(CRYPTO_refcount_t *count);
+
+#endif /* __btls__btls_util__ */
--- /dev/null
+//
+// btls-x509-chain.c
+// MonoBtls
+//
+// Created by Martin Baulig on 3/3/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#include <btls-x509-chain.h>
+
+struct MonoBtlsX509Chain {
+ STACK_OF(X509) *certs;
+ CRYPTO_refcount_t references;
+};
+
+MonoBtlsX509Chain *
+mono_btls_x509_chain_new (void)
+{
+ MonoBtlsX509Chain *chain = (MonoBtlsX509Chain *)OPENSSL_malloc (sizeof (MonoBtlsX509Chain));
+ if (chain == NULL)
+ return NULL;
+
+ memset(chain, 0, sizeof(MonoBtlsX509Chain));
+ chain->certs = sk_X509_new_null ();
+ chain->references = 1;
+ return chain;
+}
+
+MonoBtlsX509Chain *
+mono_btls_x509_chain_from_certs (STACK_OF(X509) *certs)
+{
+ MonoBtlsX509Chain *chain = (MonoBtlsX509Chain *)OPENSSL_malloc (sizeof (MonoBtlsX509Chain));
+ if (chain == NULL)
+ return NULL;
+
+ memset(chain, 0, sizeof(MonoBtlsX509Chain));
+ chain->certs = X509_chain_up_ref(certs);
+ chain->references = 1;
+ return chain;
+}
+
+STACK_OF(X509) *
+mono_btls_x509_chain_peek_certs (MonoBtlsX509Chain *chain)
+{
+ return chain->certs;
+}
+
+int
+mono_btls_x509_chain_get_count (MonoBtlsX509Chain *chain)
+{
+ return (int)sk_X509_num(chain->certs);
+}
+
+X509 *
+mono_btls_x509_chain_get_cert (MonoBtlsX509Chain *chain, int index)
+{
+ X509 *cert;
+
+ if ((size_t)index >= sk_X509_num(chain->certs))
+ return NULL;
+ cert = sk_X509_value(chain->certs, index);
+ if (cert)
+ X509_up_ref(cert);
+ return cert;
+}
+
+STACK_OF(X509) *
+mono_btls_x509_chain_get_certs (MonoBtlsX509Chain *chain)
+{
+ return chain->certs;
+}
+
+int
+mono_btls_x509_chain_free (MonoBtlsX509Chain *chain)
+{
+ if (!CRYPTO_refcount_dec_and_test_zero(&chain->references))
+ return 0;
+
+ sk_X509_pop_free(chain->certs, X509_free);
+ OPENSSL_free (chain);
+ return 1;
+}
+
+MonoBtlsX509Chain *
+mono_btls_x509_chain_up_ref (MonoBtlsX509Chain *chain)
+{
+ CRYPTO_refcount_inc(&chain->references);
+ return chain;
+}
+
+void
+mono_btls_x509_chain_add_cert (MonoBtlsX509Chain *chain, X509 *x509)
+{
+ X509_up_ref(x509);
+ sk_X509_push(chain->certs, x509);
+}
--- /dev/null
+//
+// btls-x509-chain.h
+// MonoBtls
+//
+// Created by Martin Baulig on 3/3/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_x509_chain__
+#define __btls__btls_x509_chain__
+
+#include <stdio.h>
+#include <btls-ssl.h>
+#include <btls-x509.h>
+
+MonoBtlsX509Chain *
+mono_btls_x509_chain_new (void);
+
+MonoBtlsX509Chain *
+mono_btls_x509_chain_from_certs (STACK_OF(X509) *certs);
+
+STACK_OF(X509) *
+mono_btls_x509_chain_peek_certs (MonoBtlsX509Chain *chain);
+
+int
+mono_btls_x509_chain_get_count (MonoBtlsX509Chain *chain);
+
+X509 *
+mono_btls_x509_chain_get_cert (MonoBtlsX509Chain *chain, int index);
+
+MonoBtlsX509Chain *
+mono_btls_x509_chain_up_ref (MonoBtlsX509Chain *chain);
+
+int
+mono_btls_x509_chain_free (MonoBtlsX509Chain *chain);
+
+void
+mono_btls_x509_chain_add_cert (MonoBtlsX509Chain *chain, X509 *x509);
+
+#endif /* defined(__btls__btls_x509_chain__) */
+
--- /dev/null
+//
+// btls-x509-crl.c
+// MonoBtls
+//
+// Created by Martin Baulig on 3/23/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#include <btls-x509-crl.h>
+#include <btls-x509-revoked.h>
+
+struct MonoBtlsX509Crl {
+ X509_CRL *crl;
+ CRYPTO_refcount_t references;
+};
+
+MonoBtlsX509Crl *
+mono_btls_x509_crl_from_data (const void *buf, int len, MonoBtlsX509Format format)
+{
+ MonoBtlsX509Crl *crl;
+ BIO *bio;
+
+ crl = OPENSSL_malloc (sizeof (MonoBtlsX509Crl));
+ memset (crl, 0, sizeof(MonoBtlsX509Crl));
+ crl->references = 1;
+
+ bio = BIO_new_mem_buf ((void *)buf, len);
+ switch (format) {
+ case MONO_BTLS_X509_FORMAT_DER:
+ crl->crl = d2i_X509_CRL_bio (bio, NULL);
+ break;
+ case MONO_BTLS_X509_FORMAT_PEM:
+ crl->crl = PEM_read_bio_X509_CRL (bio, NULL, NULL, NULL);
+ break;
+ }
+ BIO_free (bio);
+
+ if (!crl->crl) {
+ OPENSSL_free (crl);
+ return NULL;
+ }
+
+ return crl;
+}
+
+MonoBtlsX509Crl *
+mono_btls_x509_crl_ref (MonoBtlsX509Crl *crl)
+{
+ CRYPTO_refcount_inc (&crl->references);
+ return crl;
+}
+
+int
+mono_btls_x509_crl_free (MonoBtlsX509Crl *crl)
+{
+ if (!CRYPTO_refcount_dec_and_test_zero (&crl->references))
+ return 0;
+
+ X509_CRL_free (crl->crl);
+ OPENSSL_free (crl);
+ return 1;
+}
+
+MonoBtlsX509Revoked *
+mono_btls_x509_crl_get_by_cert (MonoBtlsX509Crl *crl, X509 *x509)
+{
+ X509_REVOKED *revoked;
+ int ret;
+
+ revoked = NULL;
+ ret = X509_CRL_get0_by_cert (crl->crl, &revoked, x509);
+ fprintf (stderr, "mono_btls_x509_crl_get_by_cert: %d - %p\n", ret, revoked);
+
+ if (!ret || !revoked)
+ return NULL;
+
+ return mono_btls_x509_revoked_new (crl, revoked);
+}
+
+MonoBtlsX509Revoked *
+mono_btls_x509_crl_get_by_serial (MonoBtlsX509Crl *crl, void *serial, int len)
+{
+ ASN1_INTEGER si;
+ X509_REVOKED *revoked;
+ int ret;
+
+ si.type = V_ASN1_INTEGER;
+ si.length = len;
+ si.data = serial;
+
+ revoked = NULL;
+ ret = X509_CRL_get0_by_serial (crl->crl, &revoked, &si);
+ fprintf (stderr, "mono_btls_x509_crl_get_by_serial: %d - %p\n", ret, revoked);
+
+ if (!ret || !revoked)
+ return NULL;
+
+ return mono_btls_x509_revoked_new (crl, revoked);
+}
+
+int
+mono_btls_x509_crl_get_revoked_count (MonoBtlsX509Crl *crl)
+{
+ STACK_OF(X509_REVOKED) *stack;
+
+ stack = X509_CRL_get_REVOKED (crl->crl);
+ return (int)sk_X509_REVOKED_num (stack);
+}
+
+MonoBtlsX509Revoked *
+mono_btls_x509_crl_get_revoked (MonoBtlsX509Crl *crl, int index)
+{
+ STACK_OF(X509_REVOKED) *stack;
+ X509_REVOKED *revoked;
+
+ stack = X509_CRL_get_REVOKED (crl->crl);
+ if ((size_t)index >= sk_X509_REVOKED_num (stack))
+ return NULL;
+
+ revoked = sk_X509_REVOKED_value (stack, index);
+ if (!revoked)
+ return NULL;
+
+ return mono_btls_x509_revoked_new (crl, revoked);
+}
+
+long
+mono_btls_x509_crl_get_last_update (MonoBtlsX509Crl *crl)
+{
+ return mono_btls_util_asn1_time_to_ticks (X509_CRL_get_lastUpdate (crl->crl));
+}
+
+long
+mono_btls_x509_crl_get_next_update (MonoBtlsX509Crl *crl)
+{
+ return mono_btls_util_asn1_time_to_ticks (X509_CRL_get_nextUpdate (crl->crl));
+}
+
+long
+mono_btls_x509_crl_get_version (MonoBtlsX509Crl *crl)
+{
+ return X509_CRL_get_version (crl->crl);
+}
+
+MonoBtlsX509Name *
+mono_btls_x509_crl_get_issuer (MonoBtlsX509Crl *crl)
+{
+ return mono_btls_x509_name_copy (X509_CRL_get_issuer (crl->crl));
+}
+
--- /dev/null
+//
+// btls-x509-crl.h
+// MonoBtls
+//
+// Created by Martin Baulig on 3/23/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_x509_crl__
+#define __btls__btls_x509_crl__
+
+#include <stdio.h>
+#include <btls-ssl.h>
+#include <btls-x509.h>
+
+MonoBtlsX509Crl *
+mono_btls_x509_crl_from_data (const void *buf, int len, MonoBtlsX509Format format);
+
+MonoBtlsX509Crl *
+mono_btls_x509_crl_ref (MonoBtlsX509Crl *crl);
+
+int
+mono_btls_x509_crl_free (MonoBtlsX509Crl *crl);
+
+MonoBtlsX509Revoked *
+mono_btls_x509_crl_get_by_cert (MonoBtlsX509Crl *crl, X509 *x509);
+
+MonoBtlsX509Revoked *
+mono_btls_x509_crl_get_by_serial (MonoBtlsX509Crl *crl, void *serial, int len);
+
+int
+mono_btls_x509_crl_get_revoked_count (MonoBtlsX509Crl *crl);
+
+MonoBtlsX509Revoked *
+mono_btls_x509_crl_get_revoked (MonoBtlsX509Crl *crl, int index);
+
+long
+mono_btls_x509_crl_get_last_update (MonoBtlsX509Crl *crl);
+
+long
+mono_btls_x509_crl_get_next_update (MonoBtlsX509Crl *crl);
+
+long
+mono_btls_x509_crl_get_version (MonoBtlsX509Crl *crl);
+
+MonoBtlsX509Name *
+mono_btls_x509_crl_get_issuer (MonoBtlsX509Crl *crl);
+
+#endif /* __btls__btls_x509_crl__ */
--- /dev/null
+//
+// btls-x509-lookup-mono.c
+// MonoBtls
+//
+// Created by Martin Baulig on 3/6/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#include <btls-x509-lookup.h>
+#include <btls-x509-lookup-mono.h>
+#include <openssl/stack.h>
+
+// random high number
+#define MONO_BTLS_X509_L_MONO_ADD 36292
+
+typedef struct MonoLookupNode MonoLookupNode;
+struct MonoLookupNode {
+ MonoBtlsX509LookupMono *mono;
+ MonoLookupNode *next;
+};
+
+typedef struct {
+ MonoLookupNode *nodes;
+} MonoLookup;
+
+struct MonoBtlsX509LookupMono {
+ const void *instance;
+ MonoBtlsX509LookupMono_BySubject by_subject_func;
+ MonoLookup *lookup;
+};
+
+MonoBtlsX509LookupMono *
+mono_btls_x509_lookup_mono_new (void)
+{
+ MonoBtlsX509LookupMono *mono;
+
+ mono = OPENSSL_malloc (sizeof (MonoBtlsX509LookupMono));
+ if (!mono)
+ return NULL;
+
+ memset (mono, 0, sizeof (MonoBtlsX509LookupMono));
+ return mono;
+}
+
+void
+mono_btls_x509_lookup_mono_init (MonoBtlsX509LookupMono *mono, const void *instance,
+ MonoBtlsX509LookupMono_BySubject by_subject_func)
+{
+ mono->instance = instance;
+ mono->by_subject_func = by_subject_func;
+}
+
+static int
+mono_lookup_install (MonoLookup *lookup, MonoBtlsX509LookupMono *mono)
+{
+ MonoLookupNode *node;
+
+ node = OPENSSL_malloc (sizeof (MonoLookupNode));
+ if (!node)
+ return 0;
+
+ memset (node, 0, sizeof (MonoLookupNode));
+ mono->lookup = lookup;
+ node->mono = mono;
+ node->next = lookup->nodes;
+ lookup->nodes = node;
+ return 1;
+}
+
+static int
+mono_lookup_uninstall (MonoBtlsX509LookupMono *mono)
+{
+ MonoLookupNode **ptr;
+
+ if (!mono->lookup)
+ return 0;
+
+ for (ptr = &mono->lookup->nodes; *ptr; ptr = &(*ptr)->next) {
+ if ((*ptr)->mono == mono) {
+ *ptr = (*ptr)->next;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+mono_btls_x509_lookup_mono_free (MonoBtlsX509LookupMono *mono)
+{
+ mono->instance = NULL;
+ mono->by_subject_func = NULL;
+
+ if (mono->lookup) {
+ if (!mono_lookup_uninstall (mono))
+ return 0;
+ }
+
+ mono->lookup = NULL;
+
+ OPENSSL_free (mono);
+ return 1;
+}
+
+static int
+mono_lookup_ctrl (X509_LOOKUP *ctx, int cmd, const char *argp, long argl, char **ret)
+{
+ MonoLookup *lookup = (MonoLookup*)ctx->method_data;
+ MonoBtlsX509LookupMono *mono = (MonoBtlsX509LookupMono*)argp;
+
+ if (!lookup || cmd != MONO_BTLS_X509_L_MONO_ADD)
+ return 0;
+ if (!mono || mono->lookup)
+ return 0;
+
+ return mono_lookup_install (lookup, mono);
+}
+
+static int
+mono_lookup_new (X509_LOOKUP *ctx)
+{
+ MonoLookup *data;
+
+ data = OPENSSL_malloc (sizeof (MonoLookup));
+ if (!data)
+ return 0;
+
+ memset (data, 0, sizeof (MonoLookup));
+ ctx->method_data = (void *)data;
+ return 1;
+}
+
+static void
+mono_lookup_free (X509_LOOKUP *ctx)
+{
+ MonoLookup *lookup;
+ MonoLookupNode *ptr;
+
+ lookup = (MonoLookup *)ctx->method_data;
+ ctx->method_data = NULL;
+ if (!lookup)
+ return;
+
+ ptr = lookup->nodes;
+ lookup->nodes = NULL;
+
+ while (ptr) {
+ MonoLookupNode *node = ptr;
+ ptr = ptr->next;
+
+ if (node->mono)
+ node->mono->lookup = NULL;
+ node->mono = NULL;
+ node->next = NULL;
+ OPENSSL_free (node);
+ }
+
+ OPENSSL_free (lookup);
+}
+
+static int
+mono_lookup_get_by_subject (X509_LOOKUP *ctx, int type, X509_NAME *name, X509_OBJECT *obj_ret)
+{
+ MonoLookup *lookup;
+ MonoBtlsX509Name *name_obj;
+ MonoLookupNode *node;
+ X509 *x509 = NULL;
+ int ret = 0;
+
+ lookup = (MonoLookup *)ctx->method_data;
+
+ if (!lookup || !lookup->nodes)
+ return 0;
+ if (type != X509_LU_X509)
+ return 0;
+
+ name_obj = mono_btls_x509_name_from_name (name);
+ x509 = NULL;
+
+ for (node = lookup->nodes; node; node = node->next) {
+ if (!node->mono || !node->mono->by_subject_func)
+ continue;
+ ret = (* node->mono->by_subject_func) (node->mono->instance, name_obj, &x509);
+ if (ret)
+ break;
+ }
+
+ mono_btls_x509_name_free (name_obj);
+
+ if (!ret) {
+ if (x509)
+ X509_free(x509);
+ return 0;
+ }
+
+ obj_ret->type = X509_LU_X509;
+ obj_ret->data.x509 = x509;
+ return 1;
+}
+
+static X509_LOOKUP_METHOD mono_lookup_method = {
+ "Mono lookup method",
+ mono_lookup_new, /* new */
+ mono_lookup_free, /* free */
+ NULL, /* init */
+ NULL, /* shutdown */
+ mono_lookup_ctrl, /* ctrl */
+ mono_lookup_get_by_subject, /* get_by_subject */
+ NULL, /* get_by_issuer_serial */
+ NULL, /* get_by_fingerprint */
+ NULL, /* get_by_alias */
+};
+
+X509_LOOKUP_METHOD *
+mono_btls_x509_lookup_mono_method (void)
+{
+ return &mono_lookup_method;
+}
+
+int
+mono_btls_x509_lookup_add_mono (MonoBtlsX509Lookup *lookup, MonoBtlsX509LookupMono *mono)
+{
+ if (mono_btls_x509_lookup_get_type (lookup) != MONO_BTLS_X509_LOOKUP_TYPE_MONO)
+ return 0;
+ return X509_LOOKUP_ctrl (mono_btls_x509_lookup_peek_lookup (lookup),
+ MONO_BTLS_X509_L_MONO_ADD,
+ (void*)mono, 0, NULL);
+}
--- /dev/null
+//
+// btls-x509-lookup-mono.h
+// MonoBtls
+//
+// Created by Martin Baulig on 3/3/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_x509_lookup_mono__
+#define __btls__btls_x509_lookup_mono__
+
+#include <stdio.h>
+#include <btls-ssl.h>
+#include <btls-x509.h>
+#include <btls-x509-store.h>
+
+typedef int (* MonoBtlsX509LookupMono_BySubject) (const void *instance, MonoBtlsX509Name *name, X509 **ret);
+
+MonoBtlsX509LookupMono *
+mono_btls_x509_lookup_mono_new (void);
+
+int
+mono_btls_x509_lookup_mono_free (MonoBtlsX509LookupMono *mono);
+
+void
+mono_btls_x509_lookup_mono_init (MonoBtlsX509LookupMono *mono, const void *instance,
+ MonoBtlsX509LookupMono_BySubject by_subject_func);
+
+int
+mono_btls_x509_lookup_add_mono (MonoBtlsX509Lookup *lookup, MonoBtlsX509LookupMono *mono);
+
+X509_LOOKUP_METHOD *
+mono_btls_x509_lookup_mono_method (void);
+
+#endif /* defined(__btls__btls_x509_lookup_mono__) */
+
--- /dev/null
+//
+// btls-x509-lookup.c
+// MonoBtls
+//
+// Created by Martin Baulig on 3/6/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#include <btls-x509-lookup.h>
+#include <btls-x509-lookup-mono.h>
+
+struct MonoBtlsX509Lookup {
+ MonoBtlsX509LookupType type;
+ X509_LOOKUP *lookup;
+ int owns_lookup;
+ MonoBtlsX509Store *store;
+ CRYPTO_refcount_t references;
+};
+
+static X509_LOOKUP_METHOD *
+get_lookup_method (MonoBtlsX509LookupType type)
+{
+ switch (type) {
+ case MONO_BTLS_X509_LOOKUP_TYPE_FILE:
+ return X509_LOOKUP_file ();
+ case MONO_BTLS_X509_LOOKUP_TYPE_HASH_DIR:
+ return X509_LOOKUP_hash_dir ();
+ case MONO_BTLS_X509_LOOKUP_TYPE_MONO:
+ return mono_btls_x509_lookup_mono_method ();
+ default:
+ return NULL;
+ }
+}
+
+MonoBtlsX509Lookup *
+mono_btls_x509_lookup_new (MonoBtlsX509Store *store, MonoBtlsX509LookupType type)
+{
+ MonoBtlsX509Lookup *lookup;
+ X509_LOOKUP *store_lookup;
+ X509_LOOKUP_METHOD *method;
+
+ method = get_lookup_method (type);
+ if (!method)
+ return NULL;
+
+ lookup = OPENSSL_malloc (sizeof(MonoBtlsX509Lookup));
+ if (!lookup)
+ return NULL;
+
+ store_lookup = X509_STORE_add_lookup (mono_btls_x509_store_peek_store (store), method);
+ if (!store_lookup)
+ return NULL;
+
+ memset (lookup, 0, sizeof(MonoBtlsX509Lookup));
+ // The X509_STORE owns the X509_LOOKUP.
+ lookup->store = mono_btls_x509_store_up_ref (store);
+ lookup->lookup = store_lookup;
+ lookup->owns_lookup = 0;
+ lookup->references = 1;
+ lookup->type = type;
+ return lookup;
+}
+
+int
+mono_btls_x509_lookup_load_file (MonoBtlsX509Lookup *lookup, const char *file, MonoBtlsX509FileType type)
+{
+ return X509_LOOKUP_load_file (lookup->lookup, file, type);
+}
+
+int
+mono_btls_x509_lookup_add_dir (MonoBtlsX509Lookup *lookup, const char *dir, MonoBtlsX509FileType type)
+{
+ return X509_LOOKUP_add_dir (lookup->lookup, dir, type);
+}
+
+MonoBtlsX509Lookup *
+mono_btls_x509_lookup_up_ref (MonoBtlsX509Lookup *lookup)
+{
+ CRYPTO_refcount_inc (&lookup->references);
+ return lookup;
+}
+
+int
+mono_btls_x509_lookup_free (MonoBtlsX509Lookup *lookup)
+{
+ if (!CRYPTO_refcount_dec_and_test_zero (&lookup->references))
+ return 0;
+
+ if (lookup->store) {
+ mono_btls_x509_store_free (lookup->store);
+ lookup->store = NULL;
+ }
+
+ if (lookup->lookup) {
+ if (lookup->owns_lookup)
+ X509_LOOKUP_free (lookup->lookup);
+ lookup->lookup = NULL;
+ }
+
+ OPENSSL_free (lookup);
+ return 1;
+}
+
+int
+mono_btls_x509_lookup_init (MonoBtlsX509Lookup *lookup)
+{
+ return X509_LOOKUP_init (lookup->lookup);
+}
+
+int
+mono_btls_x509_lookup_shutdown (MonoBtlsX509Lookup *lookup)
+{
+ return X509_LOOKUP_shutdown (lookup->lookup);
+}
+
+MonoBtlsX509LookupType
+mono_btls_x509_lookup_get_type (MonoBtlsX509Lookup *lookup)
+{
+ return lookup->type;
+}
+
+X509_LOOKUP *
+mono_btls_x509_lookup_peek_lookup (MonoBtlsX509Lookup *lookup)
+{
+ return lookup->lookup;
+}
+
+X509 *
+mono_btls_x509_lookup_by_subject (MonoBtlsX509Lookup *lookup, MonoBtlsX509Name *name)
+{
+ X509_OBJECT obj;
+ X509 *x509;
+ int ret;
+
+ ret = X509_LOOKUP_by_subject (lookup->lookup, X509_LU_X509, mono_btls_x509_name_peek_name (name), &obj);
+ if (ret != X509_LU_X509) {
+ X509_OBJECT_free_contents (&obj);
+ return NULL;
+ }
+
+ x509 = X509_up_ref (obj.data.x509);
+ return x509;
+}
+
+X509 *
+mono_btls_x509_lookup_by_fingerprint (MonoBtlsX509Lookup *lookup, unsigned char *bytes, int len)
+{
+ X509_OBJECT obj;
+ X509 *x509;
+ int ret;
+
+ ret = X509_LOOKUP_by_fingerprint (lookup->lookup, X509_LU_X509, bytes, len, &obj);
+ if (ret != X509_LU_X509) {
+ X509_OBJECT_free_contents (&obj);
+ return NULL;
+ }
+
+ x509 = X509_up_ref (obj.data.x509);
+ return x509;
+}
--- /dev/null
+//
+// btls-x509-lookup.h
+// MonoBtls
+//
+// Created by Martin Baulig on 3/3/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_x509_lookup__
+#define __btls__btls_x509_lookup__
+
+#include <stdio.h>
+#include <btls-ssl.h>
+#include <btls-x509.h>
+#include <btls-x509-store.h>
+
+typedef enum {
+ MONO_BTLS_X509_LOOKUP_TYPE_UNKNOWN = 0,
+ MONO_BTLS_X509_LOOKUP_TYPE_FILE,
+ MONO_BTLS_X509_LOOKUP_TYPE_HASH_DIR,
+ MONO_BTLS_X509_LOOKUP_TYPE_MONO
+} MonoBtlsX509LookupType;
+
+MonoBtlsX509Lookup *
+mono_btls_x509_lookup_new (MonoBtlsX509Store *store, MonoBtlsX509LookupType type);
+
+int
+mono_btls_x509_lookup_load_file (MonoBtlsX509Lookup *lookup, const char *file, MonoBtlsX509FileType type);
+
+int
+mono_btls_x509_lookup_add_dir (MonoBtlsX509Lookup *lookup, const char *dir, MonoBtlsX509FileType type);
+
+MonoBtlsX509Lookup *
+mono_btls_x509_lookup_up_ref (MonoBtlsX509Lookup *lookup);
+
+int
+mono_btls_x509_lookup_free (MonoBtlsX509Lookup *lookup);
+
+int
+mono_btls_x509_lookup_init (MonoBtlsX509Lookup *lookup);
+
+MonoBtlsX509LookupType
+mono_btls_x509_lookup_get_type (MonoBtlsX509Lookup *lookup);
+
+X509_LOOKUP *
+mono_btls_x509_lookup_peek_lookup (MonoBtlsX509Lookup *lookup);
+
+int
+mono_btls_x509_lookup_shutdown (MonoBtlsX509Lookup *lookup);
+
+X509 *
+mono_btls_x509_lookup_by_subject (MonoBtlsX509Lookup *lookup, MonoBtlsX509Name *name);
+
+X509 *
+mono_btls_x509_lookup_by_fingerprint (MonoBtlsX509Lookup *lookup, unsigned char *bytes, int len);
+
+#endif /* defined(__btls__btls_x509_lookup__) */
+
--- /dev/null
+//
+// btls-x509-name.c
+// MonoBtls
+//
+// Created by Martin Baulig on 3/5/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#include <btls-x509-name.h>
+
+struct MonoBtlsX509Name {
+ int owns;
+ X509_NAME *name;
+};
+
+MonoBtlsX509Name *
+mono_btls_x509_name_from_name (X509_NAME *xn)
+{
+ MonoBtlsX509Name *name;
+
+ name = OPENSSL_malloc (sizeof (MonoBtlsX509Name));
+ if (!name)
+ return NULL;
+
+ memset(name, 0, sizeof(MonoBtlsX509Name));
+ name->name = xn;
+ return name;
+}
+
+MonoBtlsX509Name *
+mono_btls_x509_name_copy (X509_NAME *xn)
+{
+ MonoBtlsX509Name *name;
+
+ name = OPENSSL_malloc (sizeof (MonoBtlsX509Name));
+ if (!name)
+ return NULL;
+
+ memset(name, 0, sizeof(MonoBtlsX509Name));
+ name->name = X509_NAME_dup(xn);
+ name->owns = 1;
+ return name;
+}
+
+void
+mono_btls_x509_name_free (MonoBtlsX509Name *name)
+{
+ if (name->owns) {
+ if (name->name) {
+ X509_NAME_free(name->name);
+ name->name = NULL;
+ }
+ }
+ OPENSSL_free(name);
+}
+
+X509_NAME *
+mono_btls_x509_name_peek_name (MonoBtlsX509Name *name)
+{
+ return name->name;
+}
+
+int
+mono_btls_x509_name_print_bio (MonoBtlsX509Name *name, BIO *bio)
+{
+ return X509_NAME_print_ex (bio, name->name, 0, ASN1_STRFLGS_RFC2253 | XN_FLAG_FN_SN | XN_FLAG_SEP_CPLUS_SPC | XN_FLAG_DN_REV);
+}
+
+int
+mono_btls_x509_name_get_raw_data (MonoBtlsX509Name *name, void **buffer, int use_canon_enc)
+{
+ int len;
+ void *ptr;
+
+ if (use_canon_enc) {
+ // make sure canon_enc is initialized.
+ i2d_X509_NAME (name->name, NULL);
+
+ len = name->name->canon_enclen;
+ ptr = name->name->canon_enc;
+ } else {
+ len = (int)name->name->bytes->length;
+ ptr = name->name->bytes->data;
+ }
+
+ *buffer = OPENSSL_malloc (len);
+ if (!*buffer)
+ return 0;
+
+ memcpy (*buffer, ptr, len);
+ return len;
+}
+
+MonoBtlsX509Name *
+mono_btls_x509_name_from_data (const void *data, int len, int use_canon_enc)
+{
+ MonoBtlsX509Name *name;
+ uint8_t *buf;
+ const unsigned char *ptr;
+ X509_NAME *ret;
+
+ name = OPENSSL_malloc (sizeof (MonoBtlsX509Name));
+ if (!name)
+ return NULL;
+
+ memset (name, 0, sizeof(MonoBtlsX509Name));
+ name->owns = 1;
+
+ name->name = X509_NAME_new ();
+ if (!name->name) {
+ OPENSSL_free (name);
+ return NULL;
+ }
+
+ if (use_canon_enc) {
+ CBB cbb, contents;
+ size_t buf_len;
+
+ // re-add ASN1 SEQUENCE header.
+ CBB_init(&cbb, 0);
+ if (!CBB_add_asn1(&cbb, &contents, 0x30) ||
+ !CBB_add_bytes(&contents, data, len) ||
+ !CBB_finish(&cbb, &buf, &buf_len)) {
+ CBB_cleanup (&cbb);
+ mono_btls_x509_name_free (name);
+ return NULL;
+ }
+
+ ptr = buf;
+ len = (int)buf_len;
+ } else {
+ ptr = data;
+ buf = NULL;
+ }
+
+ ret = d2i_X509_NAME (&name->name, &ptr, len);
+
+ if (buf)
+ OPENSSL_free (buf);
+
+ if (ret != name->name) {
+ mono_btls_x509_name_free (name);
+ return NULL;
+ }
+
+ return name;
+}
+
+int
+mono_btls_x509_name_print_string (MonoBtlsX509Name *name, char *buffer, int size)
+{
+ *buffer = 0;
+ return X509_NAME_oneline (name->name, buffer, size) != NULL;
+}
+
+long
+mono_btls_x509_name_hash (MonoBtlsX509Name *name)
+{
+ return X509_NAME_hash (name->name);
+}
+
+long
+mono_btls_x509_name_hash_old (MonoBtlsX509Name *name)
+{
+ return X509_NAME_hash_old (name->name);
+}
+
+int
+mono_btls_x509_name_get_entry_count (MonoBtlsX509Name *name)
+{
+ return X509_NAME_entry_count (name->name);
+}
+
+static MonoBtlsX509NameEntryType
+nid2mono (int nid)
+{
+ switch (nid) {
+ case NID_countryName:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_COUNTRY_NAME;
+ case NID_organizationName:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_ORGANIZATION_NAME;
+ case NID_organizationalUnitName:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_ORGANIZATIONAL_UNIT_NAME;
+ case NID_commonName:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_COMMON_NAME;
+ case NID_localityName:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_LOCALITY_NAME;
+ case NID_stateOrProvinceName:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_STATE_OR_PROVINCE_NAME;
+ case NID_streetAddress:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_STREET_ADDRESS;
+ case NID_serialNumber:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_SERIAL_NUMBER;
+ case NID_domainComponent:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_DOMAIN_COMPONENT;
+ case NID_userId:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_USER_ID;
+ case NID_dnQualifier:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_DN_QUALIFIER;
+ case NID_title:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_TITLE;
+ case NID_surname:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_SURNAME;
+ case NID_givenName:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_GIVEN_NAME;
+ case NID_initials:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_INITIAL;
+ default:
+ return MONO_BTLS_X509_NAME_ENTRY_TYPE_UNKNOWN;
+ }
+}
+
+MonoBtlsX509NameEntryType
+mono_btls_x509_name_get_entry_type (MonoBtlsX509Name *name, int index)
+{
+ X509_NAME_ENTRY *entry;
+ ASN1_OBJECT *obj;
+
+ if (index >= X509_NAME_entry_count (name->name))
+ return -1;
+
+ entry = X509_NAME_get_entry (name->name, index);
+ if (!entry)
+ return -1;
+
+ obj = X509_NAME_ENTRY_get_object (entry);
+ if (!obj)
+ return -1;
+
+ return nid2mono (OBJ_obj2nid (obj));
+}
+
+int
+mono_btls_x509_name_get_entry_oid (MonoBtlsX509Name *name, int index, char *buffer, int size)
+{
+ X509_NAME_ENTRY *entry;
+ ASN1_OBJECT *obj;
+
+ if (index >= X509_NAME_entry_count (name->name))
+ return 0;
+
+ entry = X509_NAME_get_entry (name->name, index);
+ if (!entry)
+ return 0;
+
+ obj = X509_NAME_ENTRY_get_object (entry);
+ if (!obj)
+ return 0;
+
+ return OBJ_obj2txt (buffer, size, obj, 1);
+}
+
+int
+mono_btls_x509_name_get_entry_oid_data (MonoBtlsX509Name *name, int index, const void **data)
+{
+ X509_NAME_ENTRY *entry;
+ ASN1_OBJECT *obj;
+
+ if (index >= X509_NAME_entry_count (name->name))
+ return -1;
+
+ entry = X509_NAME_get_entry (name->name, index);
+ if (!entry)
+ return -1;
+
+ obj = X509_NAME_ENTRY_get_object (entry);
+ if (!obj)
+ return -1;
+
+ *data = obj->data;
+ return obj->length;
+}
+
+int
+mono_btls_x509_name_get_entry_value (MonoBtlsX509Name *name, int index, unsigned char **str)
+{
+ X509_NAME_ENTRY *entry;
+ ASN1_STRING *data;
+
+ *str = NULL;
+
+ if (index >= X509_NAME_entry_count (name->name))
+ return 0;
+
+ entry = X509_NAME_get_entry (name->name, index);
+ if (!entry)
+ return 0;
+
+ data = X509_NAME_ENTRY_get_data (entry);
+ if (!data)
+ return 0;
+
+ return ASN1_STRING_to_UTF8 (str, data);
+}
--- /dev/null
+//
+// btls-x509-name.h
+// MonoBtls
+//
+// Created by Martin Baulig on 3/5/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_x509_name__
+#define __btls__btls_x509_name__
+
+#include <stdio.h>
+#include <btls-ssl.h>
+
+typedef enum {
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_UNKNOWN = 0,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_COUNTRY_NAME,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_ORGANIZATION_NAME,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_ORGANIZATIONAL_UNIT_NAME,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_COMMON_NAME,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_LOCALITY_NAME,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_STATE_OR_PROVINCE_NAME,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_STREET_ADDRESS,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_SERIAL_NUMBER,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_DOMAIN_COMPONENT,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_USER_ID,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_EMAIL,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_DN_QUALIFIER,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_TITLE,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_SURNAME,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_GIVEN_NAME,
+ MONO_BTLS_X509_NAME_ENTRY_TYPE_INITIAL
+} MonoBtlsX509NameEntryType;
+
+MonoBtlsX509Name *
+mono_btls_x509_name_from_name (X509_NAME *name);
+
+MonoBtlsX509Name *
+mono_btls_x509_name_copy (X509_NAME *xn);
+
+void
+mono_btls_x509_name_free (MonoBtlsX509Name *name);
+
+X509_NAME *
+mono_btls_x509_name_peek_name (MonoBtlsX509Name *name);
+
+MonoBtlsX509Name *
+mono_btls_x509_name_from_data (const void *data, int len, int use_canon_enc);
+
+int
+mono_btls_x509_name_print_bio (MonoBtlsX509Name *name, BIO *bio);
+
+int
+mono_btls_x509_name_print_string (MonoBtlsX509Name *name, char *buffer, int size);
+
+int
+mono_btls_x509_name_get_raw_data (MonoBtlsX509Name *name, void **buffer, int use_canon_enc);
+
+long
+mono_btls_x509_name_hash (MonoBtlsX509Name *name);
+
+long
+mono_btls_x509_name_hash_old (MonoBtlsX509Name *name);
+
+int
+mono_btls_x509_name_get_entry_count (MonoBtlsX509Name *name);
+
+MonoBtlsX509NameEntryType
+mono_btls_x509_name_get_entry_type (MonoBtlsX509Name *name, int index);
+
+int
+mono_btls_x509_name_get_entry_oid (MonoBtlsX509Name *name, int index, char *buffer, int size);
+
+int
+mono_btls_x509_name_get_entry_oid_data (MonoBtlsX509Name *name, int index, const void **data);
+
+int
+mono_btls_x509_name_get_entry_value (MonoBtlsX509Name *name, int index, unsigned char **str);
+
+#endif /* __btls__btls_x509_name__ */
--- /dev/null
+//
+// btls-x509-revoked.c
+// MonoBtls
+//
+// Created by Martin Baulig on 3/23/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#include <btls-x509-revoked.h>
+
+struct MonoBtlsX509Revoked {
+ MonoBtlsX509Crl *owner;
+ X509_REVOKED *revoked;
+};
+
+MonoBtlsX509Revoked *
+mono_btls_x509_revoked_new (MonoBtlsX509Crl *owner, X509_REVOKED *revoked)
+{
+ MonoBtlsX509Revoked *instance;
+
+ instance = OPENSSL_malloc (sizeof (MonoBtlsX509Revoked));
+ memset (instance, 0, sizeof (MonoBtlsX509Revoked));
+
+ instance->owner = mono_btls_x509_crl_ref (owner);
+ instance->revoked = revoked;
+ return instance;
+}
+
+void
+mono_btls_x509_revoked_free (MonoBtlsX509Revoked *revoked)
+{
+ mono_btls_x509_crl_free (revoked->owner);
+ OPENSSL_free (revoked);
+}
+
+int
+mono_btls_x509_revoked_get_serial_number (MonoBtlsX509Revoked *revoked, char *buffer, int size)
+{
+ ASN1_INTEGER *serial;
+
+ serial = revoked->revoked->serialNumber;
+ if (serial->length == 0 || serial->length+1 > size)
+ return 0;
+
+ memcpy (buffer, serial->data, serial->length);
+ return serial->length;
+}
+
+long
+mono_btls_x509_revoked_get_revocation_date (MonoBtlsX509Revoked *revoked)
+{
+ ASN1_TIME *date;
+
+ date = revoked->revoked->revocationDate;
+ if (!date)
+ return 0;
+
+ return mono_btls_util_asn1_time_to_ticks (date);
+}
+
+int
+mono_btls_x509_revoked_get_reason (MonoBtlsX509Revoked *revoked)
+{
+ return revoked->revoked->reason;
+}
+
+int
+mono_btls_x509_revoked_get_sequence (MonoBtlsX509Revoked *revoked)
+{
+ return revoked->revoked->sequence;
+}
+
--- /dev/null
+//
+// btls-x509-revoked.h
+// MonoBtls
+//
+// Created by Martin Baulig on 3/23/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_x509_revoked__
+#define __btls__btls_x509_revoked__
+
+#include <stdio.h>
+#include <btls-ssl.h>
+#include <btls-x509-crl.h>
+
+MonoBtlsX509Revoked *
+mono_btls_x509_revoked_new (MonoBtlsX509Crl *owner, X509_REVOKED *revoked);
+
+void
+mono_btls_x509_revoked_free (MonoBtlsX509Revoked *revoked);
+
+int
+mono_btls_x509_revoked_get_serial_number (MonoBtlsX509Revoked *revoked, char *buffer, int size);
+
+long
+mono_btls_x509_revoked_get_revocation_date (MonoBtlsX509Revoked *revoked);
+
+int
+mono_btls_x509_revoked_get_reason (MonoBtlsX509Revoked *revoked);
+
+int
+mono_btls_x509_revoked_get_sequence (MonoBtlsX509Revoked *revoked);
+
+#endif /* __btls__btls_x509_revoked__ */
--- /dev/null
+//
+// btls-x509-store-ctx.c
+// MonoBtls
+//
+// Created by Martin Baulig on 3/5/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#include <btls-x509-store-ctx.h>
+
+struct MonoBtlsX509StoreCtx {
+ int owns;
+ X509_STORE_CTX *ctx;
+ CRYPTO_refcount_t references;
+ MonoBtlsX509Store *store;
+ MonoBtlsX509Chain *chain;
+};
+
+MonoBtlsX509StoreCtx *
+mono_btls_x509_store_ctx_from_ptr (X509_STORE_CTX *ptr)
+{
+ MonoBtlsX509StoreCtx *ctx;
+
+ ctx = OPENSSL_malloc (sizeof(MonoBtlsX509StoreCtx));
+ if (!ctx)
+ return NULL;
+
+ memset (ctx, 0, sizeof (MonoBtlsX509StoreCtx));
+ ctx->ctx = ptr;
+ ctx->references = 1;
+ return ctx;
+}
+
+MonoBtlsX509StoreCtx *
+mono_btls_x509_store_ctx_new (void)
+{
+ MonoBtlsX509StoreCtx *ctx;
+
+ ctx = OPENSSL_malloc (sizeof(MonoBtlsX509StoreCtx));
+ if (!ctx)
+ return NULL;
+
+ memset (ctx, 0, sizeof (MonoBtlsX509StoreCtx));
+ ctx->ctx = X509_STORE_CTX_new ();
+ ctx->references = 1;
+ ctx->owns = 1;
+ return ctx;
+}
+
+MonoBtlsX509StoreCtx *
+mono_btls_x509_store_ctx_up_ref (MonoBtlsX509StoreCtx *ctx)
+{
+ CRYPTO_refcount_inc (&ctx->references);
+ return ctx;
+}
+
+int
+mono_btls_x509_store_ctx_free (MonoBtlsX509StoreCtx *ctx)
+{
+ if (!CRYPTO_refcount_dec_and_test_zero (&ctx->references))
+ return 0;
+
+ if (ctx->owns) {
+ X509_STORE_CTX_cleanup (ctx->ctx);
+ X509_STORE_CTX_free (ctx->ctx);
+ ctx->owns = 0;
+ }
+ if (ctx->store) {
+ mono_btls_x509_store_free (ctx->store);
+ ctx->store = NULL;
+ }
+ if (ctx->chain) {
+ mono_btls_x509_chain_free (ctx->chain);
+ ctx->chain = NULL;
+ }
+ OPENSSL_free (ctx);
+ return 1;
+}
+
+int
+mono_btls_x509_store_ctx_get_error (MonoBtlsX509StoreCtx *ctx, const char **error_string)
+{
+ int error;
+
+ error = X509_STORE_CTX_get_error (ctx->ctx);
+ if (error_string)
+ *error_string = X509_verify_cert_error_string (error);
+ return error;
+}
+
+int
+mono_btls_x509_store_ctx_get_error_depth (MonoBtlsX509StoreCtx *ctx)
+{
+ return X509_STORE_CTX_get_error_depth (ctx->ctx);
+}
+
+MonoBtlsX509Chain *
+mono_btls_x509_store_ctx_get_chain (MonoBtlsX509StoreCtx *ctx)
+{
+ STACK_OF(X509) *certs;
+
+ certs = X509_STORE_CTX_get_chain (ctx->ctx);
+ if (!certs)
+ return NULL;
+
+ return mono_btls_x509_chain_from_certs (certs);
+}
+
+MonoBtlsX509Chain *
+mono_btls_x509_store_ctx_get_untrusted (MonoBtlsX509StoreCtx *ctx)
+{
+ STACK_OF(X509) *untrusted;
+
+ /*
+ * Unfortunately, there is no accessor function for this.
+ *
+ * This is the set of certificate that's passed in by
+ * X509_STORE_CTX_init() and X509_STORE_CTX_set_chain().
+ */
+ untrusted = ctx->ctx->untrusted;
+ if (!untrusted)
+ return NULL;
+
+ return mono_btls_x509_chain_from_certs (untrusted);
+}
+
+int
+mono_btls_x509_store_ctx_init (MonoBtlsX509StoreCtx *ctx,
+ MonoBtlsX509Store *store, MonoBtlsX509Chain *chain)
+{
+ STACK_OF(X509) *certs;
+ X509 *leaf;
+ int ret;
+
+ if (ctx->store)
+ return 0;
+
+ certs = mono_btls_x509_chain_peek_certs (chain);
+ if (!certs || !sk_X509_num (certs))
+ return 0;
+
+ ctx->store = mono_btls_x509_store_up_ref(store);
+ ctx->chain = mono_btls_x509_chain_up_ref(chain);
+
+ leaf = sk_X509_value (certs, 0);
+ ret = X509_STORE_CTX_init (ctx->ctx, mono_btls_x509_store_peek_store (store), leaf, certs);
+ if (ret != 1)
+ return ret;
+
+ X509_STORE_CTX_set_app_data (ctx->ctx, ctx);
+ return 1;
+}
+
+int
+mono_btls_x509_store_ctx_set_param (MonoBtlsX509StoreCtx *ctx, MonoBtlsX509VerifyParam *param)
+{
+ return X509_VERIFY_PARAM_set1 (X509_STORE_CTX_get0_param (ctx->ctx), mono_btls_x509_verify_param_peek_param (param));
+}
+
+int
+mono_btls_x509_store_ctx_verify_cert (MonoBtlsX509StoreCtx *ctx)
+{
+ return X509_verify_cert (ctx->ctx);
+}
+
+X509 *
+mono_btls_x509_store_ctx_get_by_subject (MonoBtlsX509StoreCtx *ctx, MonoBtlsX509Name *name)
+{
+ X509_OBJECT obj;
+ X509 *x509;
+ int ret;
+
+ ret = X509_STORE_get_by_subject (ctx->ctx, X509_LU_X509, mono_btls_x509_name_peek_name (name), &obj);
+ if (ret != X509_LU_X509) {
+ X509_OBJECT_free_contents (&obj);
+ return NULL;
+ }
+
+ x509 = X509_up_ref (obj.data.x509);
+ return x509;
+}
+
+X509 *
+mono_btls_x509_store_ctx_get_current_cert (MonoBtlsX509StoreCtx *ctx)
+{
+ X509 *x509 = X509_STORE_CTX_get_current_cert (ctx->ctx);
+ if (!x509)
+ return NULL;
+ return X509_up_ref (x509);
+}
+
+X509 *
+mono_btls_x509_store_ctx_get_current_issuer (MonoBtlsX509StoreCtx *ctx)
+{
+ X509 *x509 = X509_STORE_CTX_get0_current_issuer (ctx->ctx);
+ if (!x509)
+ return NULL;
+ return X509_up_ref (x509);
+}
+
+MonoBtlsX509VerifyParam *
+mono_btls_x509_store_ctx_get_verify_param (MonoBtlsX509StoreCtx *ctx)
+{
+ X509_VERIFY_PARAM *param;
+
+ param = X509_STORE_CTX_get0_param (ctx->ctx);
+ if (!param)
+ return NULL;
+
+ return mono_btls_x509_verify_param_from_store_ctx (ctx, param);
+}
+
+int
+mono_btls_x509_store_ctx_get_foo (MonoBtlsX509StoreCtx *ctx)
+{
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+//
+// btls-x509-store-ctx.h
+// MonoBtls
+//
+// Created by Martin Baulig on 3/3/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_x509_store_ctx__
+#define __btls__btls_x509_store_ctx__
+
+#include <stdio.h>
+#include <btls-ssl.h>
+#include <btls-x509-chain.h>
+#include <btls-x509-name.h>
+#include <btls-x509-store.h>
+#include <btls-x509-verify-param.h>
+
+MonoBtlsX509StoreCtx *
+mono_btls_x509_store_ctx_from_ptr (X509_STORE_CTX *ptr);
+
+MonoBtlsX509StoreCtx *
+mono_btls_x509_store_ctx_new (void);
+
+MonoBtlsX509StoreCtx *
+mono_btls_x509_store_ctx_up_ref (MonoBtlsX509StoreCtx *ctx);
+
+int
+mono_btls_x509_store_ctx_free (MonoBtlsX509StoreCtx *ctx);
+
+int
+mono_btls_x509_store_ctx_get_error (MonoBtlsX509StoreCtx *ctx, const char **error_string);
+
+int
+mono_btls_x509_store_ctx_get_error_depth (MonoBtlsX509StoreCtx *ctx);
+
+MonoBtlsX509Chain *
+mono_btls_x509_store_ctx_get_chain (MonoBtlsX509StoreCtx *ctx);
+
+X509 *
+mono_btls_x509_store_ctx_get_current_cert (MonoBtlsX509StoreCtx *ctx);
+
+X509 *
+mono_btls_x509_store_ctx_get_current_issuer (MonoBtlsX509StoreCtx *ctx);
+
+int
+mono_btls_x509_store_ctx_init (MonoBtlsX509StoreCtx *ctx,
+ MonoBtlsX509Store *store, MonoBtlsX509Chain *chain);
+
+int
+mono_btls_x509_store_ctx_set_param (MonoBtlsX509StoreCtx *ctx, MonoBtlsX509VerifyParam *param);
+
+X509 *
+mono_btls_x509_store_ctx_get_by_subject (MonoBtlsX509StoreCtx *ctx, MonoBtlsX509Name *name);
+
+int
+mono_btls_x509_store_ctx_verify_cert (MonoBtlsX509StoreCtx *ctx);
+
+MonoBtlsX509VerifyParam *
+mono_btls_x509_store_ctx_get_verify_param (MonoBtlsX509StoreCtx *ctx);
+
+MonoBtlsX509Chain *
+mono_btls_x509_store_ctx_get_untrusted (MonoBtlsX509StoreCtx *ctx);
+
+#endif /* defined(__btls__btls_x509_store_ctx__) */
+
--- /dev/null
+//
+// btls-x509-store.c
+// MonoBtls
+//
+// Created by Martin Baulig on 3/3/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#include <btls-x509-store.h>
+
+struct MonoBtlsX509Store {
+ X509_STORE *store;
+ CRYPTO_refcount_t references;
+};
+
+MonoBtlsX509Store *
+mono_btls_x509_store_from_store (X509_STORE *ctx)
+{
+ MonoBtlsX509Store *store;
+
+ store = OPENSSL_malloc (sizeof(MonoBtlsX509Store));
+ if (!store)
+ return NULL;
+
+ memset (store, 0, sizeof(MonoBtlsX509Store));
+ store->store = ctx;
+ CRYPTO_refcount_inc (&store->store->references);
+ store->references = 1;
+ return store;
+}
+
+MonoBtlsX509Store *
+mono_btls_x509_store_from_ctx (X509_STORE_CTX *ctx)
+{
+ return mono_btls_x509_store_from_store (ctx->ctx);
+}
+
+MonoBtlsX509Store *
+mono_btls_x509_store_new (void)
+{
+ MonoBtlsX509Store *store;
+
+ store = OPENSSL_malloc (sizeof(MonoBtlsX509Store));
+ if (!store)
+ return NULL;
+
+ memset (store, 0, sizeof(MonoBtlsX509Store));
+ store->store = X509_STORE_new ();
+ store->references = 1;
+ return store;
+}
+
+X509_STORE *
+mono_btls_x509_store_peek_store (MonoBtlsX509Store *store)
+{
+ return store->store;
+}
+
+MonoBtlsX509Store *
+mono_btls_x509_store_from_ssl_ctx (MonoBtlsSslCtx *ctx)
+{
+ X509_STORE *store = mono_btls_ssl_ctx_peek_store (ctx);
+ return mono_btls_x509_store_from_store (store);
+}
+
+int
+mono_btls_x509_store_free (MonoBtlsX509Store *store)
+{
+ if (!CRYPTO_refcount_dec_and_test_zero(&store->references))
+ return 0;
+
+ if (store->store) {
+ X509_STORE_free (store->store);
+ store->store = NULL;
+ }
+ OPENSSL_free (store);
+ return 1;
+}
+
+MonoBtlsX509Store *
+mono_btls_x509_store_up_ref (MonoBtlsX509Store *store)
+{
+ CRYPTO_refcount_inc (&store->references);
+ return store;
+}
+
+int
+mono_btls_x509_store_add_cert (MonoBtlsX509Store *store, X509 *cert)
+{
+ return X509_STORE_add_cert (store->store, cert);
+}
+
+int
+mono_btls_x509_store_load_locations (MonoBtlsX509Store *store, const char *file, const char *path)
+{
+ return X509_STORE_load_locations (store->store, file, path);
+}
+
+int
+mono_btls_x509_store_set_default_paths (MonoBtlsX509Store *store)
+{
+ return X509_STORE_set_default_paths (store->store);
+}
+
+int
+mono_btls_x509_store_get_count (MonoBtlsX509Store *store)
+{
+ return (int)sk_X509_OBJECT_num (store->store->objs);
+}
+
--- /dev/null
+//
+// btls-x509-store.h
+// MonoBtls
+//
+// Created by Martin Baulig on 3/3/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_x509_store__
+#define __btls__btls_x509_store__
+
+#include <stdio.h>
+#include <btls-ssl.h>
+
+MonoBtlsX509Store *
+mono_btls_x509_store_new (void);
+
+MonoBtlsX509Store *
+mono_btls_x509_store_from_ctx (X509_STORE_CTX *ctx);
+
+MonoBtlsX509Store *
+mono_btls_x509_store_from_ssl_ctx (MonoBtlsSslCtx *ctx);
+
+MonoBtlsX509Store *
+mono_btls_x509_store_up_ref (MonoBtlsX509Store *store);
+
+int
+mono_btls_x509_store_free (MonoBtlsX509Store *store);
+
+X509_STORE *
+mono_btls_x509_store_peek_store (MonoBtlsX509Store *store);
+
+int
+mono_btls_x509_store_add_cert (MonoBtlsX509Store *store, X509 *cert);
+
+int
+mono_btls_x509_store_load_locations (MonoBtlsX509Store *store, const char *file, const char *path);
+
+int
+mono_btls_x509_store_set_default_paths (MonoBtlsX509Store *store);
+
+int
+mono_btls_x509_store_get_count (MonoBtlsX509Store *store);
+
+#endif /* defined(__btls__btls_x509_store__) */
+
--- /dev/null
+//
+// btls-x509-verify-param.c
+// MonoBtls
+//
+// Created by Martin Baulig on 3/5/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#include <btls-x509-verify-param.h>
+#include <btls-x509-store-ctx.h>
+
+struct MonoBtlsX509VerifyParam {
+ int owns;
+ MonoBtlsX509StoreCtx *owner;
+ X509_VERIFY_PARAM *param;
+};
+
+MonoBtlsX509VerifyParam *
+mono_btls_x509_verify_param_new (void)
+{
+ MonoBtlsX509VerifyParam *param;
+
+ param = OPENSSL_malloc (sizeof(MonoBtlsX509VerifyParam));
+ if (!param)
+ return NULL;
+ memset (param, 0, sizeof (MonoBtlsX509VerifyParam));
+ param->param = X509_VERIFY_PARAM_new();
+ param->owns = 1;
+ return param;
+}
+
+MonoBtlsX509VerifyParam *
+mono_btls_x509_verify_param_from_store_ctx (MonoBtlsX509StoreCtx *ctx, X509_VERIFY_PARAM *param)
+{
+ MonoBtlsX509VerifyParam *instance;
+
+ instance = OPENSSL_malloc (sizeof(MonoBtlsX509VerifyParam));
+ if (!instance)
+ return NULL;
+ memset (instance, 0, sizeof (MonoBtlsX509VerifyParam));
+ instance->param = param;
+ instance->owner = mono_btls_x509_store_ctx_up_ref (ctx);
+ return instance;
+}
+
+MonoBtlsX509VerifyParam *
+mono_btls_x509_verify_param_copy (const MonoBtlsX509VerifyParam *from)
+{
+ MonoBtlsX509VerifyParam *param;
+
+ param = mono_btls_x509_verify_param_new ();
+ if (!param)
+ return NULL;
+
+ X509_VERIFY_PARAM_set1 (param->param, from->param);
+ return param;
+}
+
+const X509_VERIFY_PARAM *
+mono_btls_x509_verify_param_peek_param (const MonoBtlsX509VerifyParam *param)
+{
+ return param->param;
+}
+
+int
+mono_btls_x509_verify_param_can_modify (MonoBtlsX509VerifyParam *param)
+{
+ return param->owns;
+}
+
+MonoBtlsX509VerifyParam *
+mono_btls_x509_verify_param_lookup (const char *name)
+{
+ MonoBtlsX509VerifyParam *param;
+ const X509_VERIFY_PARAM *p;
+
+ p = X509_VERIFY_PARAM_lookup(name);
+ if (!p)
+ return NULL;
+
+ param = OPENSSL_malloc (sizeof(MonoBtlsX509VerifyParam));
+ if (!param)
+ return NULL;
+ memset (param, 0, sizeof (MonoBtlsX509VerifyParam));
+ param->param = (X509_VERIFY_PARAM *)p;
+ return param;
+}
+
+void
+mono_btls_x509_verify_param_free (MonoBtlsX509VerifyParam *param)
+{
+ if (param->owns) {
+ if (param->param) {
+ X509_VERIFY_PARAM_free (param->param);
+ param->param = NULL;
+ }
+ }
+ if (param->owner) {
+ mono_btls_x509_store_ctx_free (param->owner);
+ param->owner = NULL;
+ }
+ OPENSSL_free (param);
+}
+
+int
+mono_btls_x509_verify_param_set_name (MonoBtlsX509VerifyParam *param, const char *name)
+{
+ if (!param->owns)
+ return -1;
+ return X509_VERIFY_PARAM_set1_name (param->param, name);
+}
+
+int
+mono_btls_x509_verify_param_set_host (MonoBtlsX509VerifyParam *param, const char *host, int namelen)
+{
+ if (!param->owns)
+ return -1;
+ return X509_VERIFY_PARAM_set1_host (param->param, host, namelen);
+}
+
+int
+mono_btls_x509_verify_param_add_host (MonoBtlsX509VerifyParam *param, const char *host, int namelen)
+{
+ if (!param->owns)
+ return -1;
+ return X509_VERIFY_PARAM_set1_host (param->param, host, namelen);
+}
+
+unsigned long
+mono_btls_x509_verify_param_get_flags (MonoBtlsX509VerifyParam *param)
+{
+ return X509_VERIFY_PARAM_get_flags (param->param);
+}
+
+int
+mono_btls_x509_verify_param_set_flags (MonoBtlsX509VerifyParam *param, unsigned long flags)
+{
+ if (!param->owns)
+ return -1;
+ return X509_VERIFY_PARAM_set_flags (param->param, flags);
+}
+
+MonoBtlsX509VerifyFlags
+mono_btls_x509_verify_param_get_mono_flags (MonoBtlsX509VerifyParam *param)
+{
+ MonoBtlsX509VerifyFlags current;
+ unsigned long flags;
+
+ if (!param->owns)
+ return -1;
+
+ current = 0;
+ flags = X509_VERIFY_PARAM_get_flags (param->param);
+
+ if (flags & X509_V_FLAG_CRL_CHECK)
+ current |= MONO_BTLS_X509_VERIFY_FLAGS_CRL_CHECK;
+ if (flags & X509_V_FLAG_CRL_CHECK_ALL)
+ current |= MONO_BTLS_X509_VERIFY_FLAGS_CRL_CHECK_ALL;
+ if (flags & X509_V_FLAG_X509_STRICT)
+ current |= MONO_BTLS_X509_VERIFY_FLAGS_X509_STRICT;
+
+ return current;
+}
+
+int
+mono_btls_x509_verify_param_set_mono_flags (MonoBtlsX509VerifyParam *param, MonoBtlsX509VerifyFlags flags)
+{
+ unsigned long current;
+
+ if (!param->owns)
+ return -1;
+
+ current = X509_VERIFY_PARAM_get_flags (param->param);
+ if (flags & MONO_BTLS_X509_VERIFY_FLAGS_CRL_CHECK)
+ current |= X509_V_FLAG_CRL_CHECK;
+ if (flags & MONO_BTLS_X509_VERIFY_FLAGS_CRL_CHECK_ALL)
+ current |= X509_V_FLAG_CRL_CHECK_ALL;
+ if (flags & MONO_BTLS_X509_VERIFY_FLAGS_X509_STRICT)
+ current |= X509_V_FLAG_X509_STRICT;
+
+ return X509_VERIFY_PARAM_set_flags (param->param, current);
+}
+
+int
+mono_btls_x509_verify_param_set_purpose (MonoBtlsX509VerifyParam *param, MonoBtlsX509Purpose purpose)
+{
+ if (!param->owns)
+ return -1;
+ return X509_VERIFY_PARAM_set_purpose (param->param, purpose);
+}
+
+int
+mono_btls_x509_verify_param_get_depth (MonoBtlsX509VerifyParam *param)
+{
+ return X509_VERIFY_PARAM_get_depth (param->param);
+}
+
+int
+mono_btls_x509_verify_param_set_depth (MonoBtlsX509VerifyParam *param, int depth)
+{
+ if (!param->owns)
+ return -1;
+ X509_VERIFY_PARAM_set_depth (param->param, depth);
+ return 1;
+}
+
+int
+mono_btls_x509_verify_param_set_time (MonoBtlsX509VerifyParam *param, long time)
+{
+ if (!param->owns)
+ return -1;
+ X509_VERIFY_PARAM_set_time (param->param, time);
+ return 1;
+}
+
+char *
+mono_btls_x509_verify_param_get_peername (MonoBtlsX509VerifyParam *param)
+{
+ char *peer = X509_VERIFY_PARAM_get0_peername (param->param);
+ return peer;
+}
--- /dev/null
+//
+// btls-x509-verify-param.h
+// MonoBtls
+//
+// Created by Martin Baulig on 3/3/16.
+// Copyright © 2016 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_x509_verify_param__
+#define __btls__btls_x509_verify_param__
+
+#include <stdio.h>
+#include <btls-ssl.h>
+#include <btls-x509.h>
+
+typedef enum {
+ MONO_BTLS_X509_VERIFY_FLAGS_DEFAULT = 0,
+ MONO_BTLS_X509_VERIFY_FLAGS_CRL_CHECK = 1,
+ MONO_BTLS_X509_VERIFY_FLAGS_CRL_CHECK_ALL = 2,
+ MONO_BTLS_X509_VERIFY_FLAGS_X509_STRICT = 4
+} MonoBtlsX509VerifyFlags;
+
+MonoBtlsX509VerifyParam *
+mono_btls_x509_verify_param_new (void);
+
+MonoBtlsX509VerifyParam *
+mono_btls_x509_verify_param_from_store_ctx (MonoBtlsX509StoreCtx *ctx, X509_VERIFY_PARAM *param);
+
+MonoBtlsX509VerifyParam *
+mono_btls_x509_verify_param_copy (const MonoBtlsX509VerifyParam *from);
+
+void
+mono_btls_x509_verify_param_free (MonoBtlsX509VerifyParam *param);
+
+const X509_VERIFY_PARAM *
+mono_btls_x509_verify_param_peek_param (const MonoBtlsX509VerifyParam *param);
+
+int
+mono_btls_x509_verify_param_can_modify (MonoBtlsX509VerifyParam *param);
+
+MonoBtlsX509VerifyParam *
+mono_btls_x509_verify_param_lookup (const char *name);
+
+int
+mono_btls_x509_verify_param_set_name (MonoBtlsX509VerifyParam *param, const char *name);
+
+int
+mono_btls_x509_verify_param_set_host (MonoBtlsX509VerifyParam *param, const char *host, int namelen);
+
+int
+mono_btls_x509_verify_param_add_host (MonoBtlsX509VerifyParam *param, const char *host, int namelen);
+
+unsigned long
+mono_btls_x509_verify_param_get_flags (MonoBtlsX509VerifyParam *param);
+
+int
+mono_btls_x509_verify_param_set_flags (MonoBtlsX509VerifyParam *param, unsigned long flags);
+
+MonoBtlsX509VerifyFlags
+mono_btls_x509_verify_param_get_mono_flags (MonoBtlsX509VerifyParam *param);
+
+int
+mono_btls_x509_verify_param_set_mono_flags (MonoBtlsX509VerifyParam *param, MonoBtlsX509VerifyFlags flags);
+
+int
+mono_btls_x509_verify_param_set_purpose (MonoBtlsX509VerifyParam *param, MonoBtlsX509Purpose purpose);
+
+int
+mono_btls_x509_verify_param_get_depth (MonoBtlsX509VerifyParam *param);
+
+int
+mono_btls_x509_verify_param_set_depth (MonoBtlsX509VerifyParam *param, int depth);
+
+int
+mono_btls_x509_verify_param_set_time (MonoBtlsX509VerifyParam *param, long time);
+
+char *
+mono_btls_x509_verify_param_get_peername (MonoBtlsX509VerifyParam *param);
+
+#endif /* defined(__btls__btls_x509_verify_param__) */
+
--- /dev/null
+//
+// btls-x509.c
+// MonoBtls
+//
+// Created by Martin Baulig on 14/11/15.
+// Copyright (c) 2015 Xamarin. All rights reserved.
+//
+
+#include <btls-x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pkcs12.h>
+
+X509 *
+mono_btls_x509_from_data (const void *buf, int len, MonoBtlsX509Format format)
+{
+ BIO *bio;
+ X509 *cert = NULL;
+
+ bio = BIO_new_mem_buf ((void *)buf, len);
+ switch (format) {
+ case MONO_BTLS_X509_FORMAT_DER:
+ cert = d2i_X509_bio (bio, NULL);
+ break;
+ case MONO_BTLS_X509_FORMAT_PEM:
+ cert = PEM_read_bio_X509 (bio, NULL, NULL, NULL);
+ break;
+ }
+ BIO_free (bio);
+ return cert;
+}
+
+X509 *
+mono_btls_x509_up_ref (X509 *x509)
+{
+ X509_up_ref (x509);
+ return x509;
+}
+
+void
+mono_btls_x509_free (X509 *x509)
+{
+ X509_free (x509);
+}
+
+X509 *
+mono_btls_x509_dup (X509 *x509)
+{
+ return X509_dup (x509);
+}
+
+MonoBtlsX509Name *
+mono_btls_x509_get_subject_name (X509 *x509)
+{
+ return mono_btls_x509_name_copy (X509_get_subject_name (x509));
+}
+
+MonoBtlsX509Name *
+mono_btls_x509_get_issuer_name (X509 *x509)
+{
+ return mono_btls_x509_name_copy (X509_get_issuer_name (x509));
+}
+
+int
+mono_btls_x509_get_subject_name_string (X509 *name, char *buffer, int size)
+{
+ *buffer = 0;
+ return X509_NAME_oneline (X509_get_subject_name (name), buffer, size) != NULL;
+}
+
+int
+mono_btls_x509_get_issuer_name_string (X509 *name, char *buffer, int size)
+{
+ *buffer = 0;
+ return X509_NAME_oneline (X509_get_issuer_name (name), buffer, size) != NULL;
+}
+
+int
+mono_btls_x509_get_raw_data (X509 *x509, BIO *bio, MonoBtlsX509Format format)
+{
+ switch (format) {
+ case MONO_BTLS_X509_FORMAT_DER:
+ return i2d_X509_bio (bio, x509);
+ case MONO_BTLS_X509_FORMAT_PEM:
+ return PEM_write_bio_X509 (bio, x509);
+ default:
+ return 0;
+ }
+}
+
+int
+mono_btls_x509_cmp (const X509 *a, const X509 *b)
+{
+ return X509_cmp (a, b);
+}
+
+int
+mono_btls_x509_get_hash (X509 *x509, const void **data)
+{
+ X509_check_purpose (x509, -1, 0);
+ *data = x509->sha1_hash;
+ return SHA_DIGEST_LENGTH;
+}
+
+long
+mono_btls_x509_get_not_before (X509 *x509)
+{
+ return mono_btls_util_asn1_time_to_ticks (X509_get_notBefore (x509));
+}
+
+long
+mono_btls_x509_get_not_after (X509 *x509)
+{
+ return mono_btls_util_asn1_time_to_ticks (X509_get_notAfter (x509));
+}
+
+int
+mono_btls_x509_get_public_key (X509 *x509, BIO *bio)
+{
+ EVP_PKEY *pkey;
+ uint8_t *data = NULL;
+ int ret;
+
+ pkey = X509_get_pubkey (x509);
+ if (!pkey)
+ return -1;
+
+ ret = i2d_PublicKey (pkey, &data);
+
+ if (ret > 0 && data) {
+ ret = BIO_write (bio, data, ret);
+ OPENSSL_free (data);
+ }
+
+ EVP_PKEY_free (pkey);
+ return ret;
+}
+
+int
+mono_btls_x509_get_serial_number (X509 *x509, char *buffer, int size, int mono_style)
+{
+ ASN1_INTEGER *serial;
+ char *pos;
+ int len, idx;
+
+ serial = X509_get_serialNumber (x509);
+ if (serial->length == 0 || serial->length+1 > size)
+ return 0;
+
+ if (!mono_style) {
+ memcpy (buffer, serial->data, serial->length);
+ return serial->length;
+ }
+
+ pos = buffer;
+ len = 0;
+
+ for (idx = serial->length - 1; idx >= 0; idx--) {
+ *pos++ = serial->data [idx];
+ len++;
+ }
+
+ if (serial->data [0] >= 0x80) {
+ *pos++ = 0;
+ len++;
+ }
+
+ return len;
+}
+
+int
+mono_btls_x509_get_public_key_algorithm (X509 *x509, char *buffer, int size)
+{
+ X509_PUBKEY *pkey;
+ ASN1_OBJECT *ppkalg;
+ int ret;
+
+ *buffer = 0;
+ pkey = X509_get_X509_PUBKEY (x509);
+ if (!pkey)
+ return 0;
+
+ ret = X509_PUBKEY_get0_param (&ppkalg, NULL, NULL, NULL, pkey);
+ if (!ret || !ppkalg)
+ return ret;
+
+ return OBJ_obj2txt (buffer, size, ppkalg, 1);
+}
+
+int
+mono_btls_x509_get_version (X509 *x509)
+{
+ return (int)X509_get_version (x509) + 1;
+}
+
+int
+mono_btls_x509_get_signature_algorithm (X509 *x509, char *buffer, int size)
+{
+ const ASN1_OBJECT *obj;
+ int nid;
+
+ *buffer = 0;
+
+ nid = X509_get_signature_nid (x509);
+
+ obj = OBJ_nid2obj (nid);
+ if (!obj)
+ return 0;
+
+ return OBJ_obj2txt (buffer, size, obj, 1);
+}
+
+int
+mono_btls_x509_get_public_key_asn1 (X509 *x509, char *out_oid, int oid_len, uint8_t **buffer, int *size)
+{
+ X509_PUBKEY *pkey;
+ ASN1_OBJECT *ppkalg;
+ const unsigned char *pk;
+ int pk_len;
+ int ret;
+
+ if (out_oid)
+ *out_oid = 0;
+
+ pkey = X509_get_X509_PUBKEY (x509);
+ if (!pkey || !pkey->public_key)
+ return 0;
+
+ ret = X509_PUBKEY_get0_param (&ppkalg, &pk, &pk_len, NULL, pkey);
+ if (ret != 1 || !ppkalg || !pk)
+ return 0;
+
+ if (out_oid) {
+ OBJ_obj2txt (out_oid, oid_len, ppkalg, 1);
+ }
+
+ if (buffer) {
+ *size = pk_len;
+ *buffer = OPENSSL_malloc (pk_len);
+ if (!*buffer)
+ return 0;
+
+ memcpy (*buffer, pk, pk_len);
+ }
+
+ return 1;
+
+}
+
+int
+mono_btls_x509_get_public_key_parameters (X509 *x509, char *out_oid, int oid_len, uint8_t **buffer, int *size)
+{
+ X509_PUBKEY *pkey;
+ X509_ALGOR *algor;
+ ASN1_OBJECT *paobj;
+ int ptype;
+ void *pval;
+ int ret;
+
+ if (out_oid)
+ *out_oid = 0;
+
+ pkey = X509_get_X509_PUBKEY (x509);
+
+ ret = X509_PUBKEY_get0_param (NULL, NULL, NULL, &algor, pkey);
+ if (ret != 1 || !algor)
+ return 0;
+
+ X509_ALGOR_get0 (&paobj, &ptype, &pval, algor);
+
+ if (ptype != V_ASN1_NULL && ptype != V_ASN1_SEQUENCE)
+ return 0;
+
+ if (ptype == V_ASN1_NULL) {
+ uint8_t *ptr;
+
+ *size = 2;
+ *buffer = OPENSSL_malloc (2);
+ if (!*buffer)
+ return 0;
+
+ ptr = *buffer;
+ *ptr++ = 0x05;
+ *ptr++ = 0x00;
+
+ if (out_oid)
+ OBJ_obj2txt (out_oid, oid_len, paobj, 1);
+
+ return 1;
+ } else if (ptype == V_ASN1_SEQUENCE) {
+ ASN1_STRING *pstr = pval;
+
+ *size = pstr->length;
+ *buffer = OPENSSL_malloc (pstr->length);
+ if (!*buffer)
+ return 0;
+
+ memcpy (*buffer, pstr->data, pstr->length);
+
+ if (out_oid)
+ OBJ_obj2txt (out_oid, oid_len, paobj, 1);
+
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+EVP_PKEY *
+mono_btls_x509_get_pubkey (X509 *x509)
+{
+ return X509_get_pubkey (x509);
+}
+
+int
+mono_btls_x509_get_subject_key_identifier (X509 *x509, uint8_t **buffer, int *size)
+{
+ ASN1_OCTET_STRING *skid;
+
+ *size = 0;
+ *buffer = NULL;
+
+ if (X509_get_version (x509) != 2)
+ return 0;
+
+ skid = X509_get_ext_d2i (x509, NID_subject_key_identifier, NULL, NULL);
+ if (!skid)
+ return 0;
+
+ *size = skid->length;
+ *buffer = OPENSSL_malloc (*size);
+ if (!*buffer)
+ return 0;
+
+ memcpy (*buffer, skid->data, *size);
+ return 1;
+}
+
+int
+mono_btls_x509_print (X509 *x509, BIO *bio)
+{
+ return X509_print_ex (bio, x509, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
+}
+
+static int
+get_trust_nid (MonoBtlsX509Purpose purpose)
+{
+ switch (purpose) {
+ case MONO_BTLS_X509_PURPOSE_SSL_CLIENT:
+ return NID_client_auth;
+ case MONO_BTLS_X509_PURPOSE_SSL_SERVER:
+ return NID_server_auth;
+ default:
+ return 0;
+ }
+}
+
+int
+mono_btls_x509_add_trust_object (X509 *x509, MonoBtlsX509Purpose purpose)
+{
+ ASN1_OBJECT *trust;
+ int nid;
+
+ nid = get_trust_nid (purpose);
+ if (!nid)
+ return 0;
+
+ trust = ASN1_OBJECT_new ();
+ if (!trust)
+ return 0;
+
+ trust->nid = nid;
+ return X509_add1_trust_object (x509, trust);
+}
+
+int
+mono_btls_x509_add_reject_object (X509 *x509, MonoBtlsX509Purpose purpose)
+{
+ ASN1_OBJECT *reject;
+ int nid;
+
+ nid = get_trust_nid (purpose);
+ if (!nid)
+ return 0;
+
+ reject = ASN1_OBJECT_new ();
+ if (!reject)
+ return 0;
+
+ reject->nid = nid;
+ return X509_add1_reject_object (x509, reject);
+}
+
+int
+mono_btls_x509_add_explicit_trust (X509 *x509, MonoBtlsX509TrustKind kind)
+{
+ int ret = 0;
+
+ if ((kind & MONO_BTLS_X509_TRUST_KIND_REJECT_ALL) != 0)
+ kind |= MONO_BTLS_X509_TRUST_KIND_REJECT_CLIENT | MONO_BTLS_X509_TRUST_KIND_REJECT_SERVER;
+
+ if ((kind & MONO_BTLS_X509_TRUST_KIND_TRUST_ALL) != 0)
+ kind |= MONO_BTLS_X509_TRUST_KIND_TRUST_CLIENT | MONO_BTLS_X509_TRUST_KIND_TRUST_SERVER;
+
+
+ if ((kind & MONO_BTLS_X509_TRUST_KIND_REJECT_CLIENT) != 0) {
+ ret = mono_btls_x509_add_reject_object (x509, MONO_BTLS_X509_PURPOSE_SSL_CLIENT);
+ if (!ret)
+ return ret;
+ }
+
+ if ((kind & MONO_BTLS_X509_TRUST_KIND_REJECT_SERVER) != 0) {
+ ret = mono_btls_x509_add_reject_object (x509, MONO_BTLS_X509_PURPOSE_SSL_SERVER);
+ if (!ret)
+ return ret;
+ }
+
+ if (ret) {
+ // Ignore any MONO_BTLS_X509_TRUST_KIND_TRUST_* settings if we added
+ // any kind of MONO_BTLS_X509_TRUST_KIND_REJECT_* before.
+ return ret;
+ }
+
+ if ((kind & MONO_BTLS_X509_TRUST_KIND_TRUST_CLIENT) != 0) {
+ ret = mono_btls_x509_add_trust_object (x509, MONO_BTLS_X509_PURPOSE_SSL_CLIENT);
+ if (!ret)
+ return ret;
+ }
+
+ if ((kind & MONO_BTLS_X509_TRUST_KIND_TRUST_SERVER) != 0) {
+ ret = mono_btls_x509_add_trust_object (x509, MONO_BTLS_X509_PURPOSE_SSL_SERVER);
+ if (!ret)
+ return ret;
+ }
+
+ return ret;
+}
--- /dev/null
+//
+// btls-x509.h
+// MonoBtls
+//
+// Created by Martin Baulig on 14/11/15.
+// Copyright (c) 2015 Xamarin. All rights reserved.
+//
+
+#ifndef __btls__btls_x509__
+#define __btls__btls_x509__
+
+#include <stdio.h>
+#include <btls-ssl.h>
+#include <btls-x509-name.h>
+
+typedef enum {
+ MONO_BTLS_X509_FORMAT_DER = 1,
+ MONO_BTLS_X509_FORMAT_PEM = 2
+} MonoBtlsX509Format;
+
+typedef enum {
+ MONO_BTLS_x509_FILE_TYPE_PEM = 1, // X509_FILETYPE_PEM
+ MONO_BTLS_x509_FILE_TYPE_ASN1 = 2, // X509_FILETYPE_ASN1
+ MONO_BTLS_x509_FILE_TYPE_DEFAULT = 3, // X509_FILETYPE_DEFAULT
+} MonoBtlsX509FileType;
+
+typedef enum {
+ MONO_BTLS_X509_PURPOSE_SSL_CLIENT = 1,
+ MONO_BTLS_X509_PURPOSE_SSL_SERVER = 2,
+ MONO_BTLS_X509_PURPOSE_NS_SSL_SERVER = 3,
+ MONO_BTLS_X509_PURPOSE_SMIME_SIGN = 4,
+ MONO_BTLS_X509_PURPOSE_SMIME_ENCRYPT = 5,
+ MONO_BTLS_X509_PURPOSE_CRL_SIGN = 6,
+ MONO_BTLS_X509_PURPOSE_ANY = 7,
+ MONO_BTLS_X509_PURPOSE_OCSP_HELPER = 8,
+ MONO_BTLS_X509_PURPOSE_TIMESTAMP_SIGN = 9,
+} MonoBtlsX509Purpose;
+
+typedef enum {
+ MONO_BTLS_X509_TRUST_KIND_DEFAULT = 0,
+ MONO_BTLS_X509_TRUST_KIND_TRUST_CLIENT = 1,
+ MONO_BTLS_X509_TRUST_KIND_TRUST_SERVER = 2,
+ MONO_BTLS_X509_TRUST_KIND_TRUST_ALL = 4,
+ MONO_BTLS_X509_TRUST_KIND_REJECT_CLIENT = 32,
+ MONO_BTLS_X509_TRUST_KIND_REJECT_SERVER = 64,
+ MONO_BTLS_X509_TRUST_KIND_REJECT_ALL = 128
+} MonoBtlsX509TrustKind;
+
+X509 *
+mono_btls_x509_from_data (const void *buf, int len, MonoBtlsX509Format format);
+
+X509 *
+mono_btls_x509_up_ref (X509 *x509);
+
+void
+mono_btls_x509_free (X509 *x509);
+
+X509 *
+mono_btls_x509_dup (X509 *x509);
+
+MonoBtlsX509Name *
+mono_btls_x509_get_subject_name (X509 *x509);
+
+MonoBtlsX509Name *
+mono_btls_x509_get_issuer_name (X509 *x509);
+
+int
+mono_btls_x509_get_subject_name_string (X509 *name, char *buffer, int size);
+
+int
+mono_btls_x509_get_issuer_name_string (X509 *name, char *buffer, int size);
+
+int
+mono_btls_x509_get_raw_data (X509 *x509, BIO *bio, MonoBtlsX509Format format);
+
+int
+mono_btls_x509_cmp (const X509 *a, const X509 *b);
+
+int
+mono_btls_x509_get_hash (X509 *x509, const void **data);
+
+long
+mono_btls_x509_get_not_before (X509 *x509);
+
+long
+mono_btls_x509_get_not_after (X509 *x509);
+
+int
+mono_btls_x509_get_public_key (X509 *x509, BIO *bio);
+
+int
+mono_btls_x509_get_public_key_parameters (X509 *x509, char *out_oid, int oid_len, uint8_t **buffer, int *size);
+
+int
+mono_btls_x509_get_serial_number (X509 *x509, char *buffer, int size, int mono_style);
+
+int
+mono_btls_x509_get_public_key_algorithm (X509 *x509, char *buffer, int size);
+
+int
+mono_btls_x509_get_version (X509 *x509);
+
+int
+mono_btls_x509_get_signature_algorithm (X509 *x509, char *buffer, int size);
+
+int
+mono_btls_x509_get_public_key_asn1 (X509 *x509, char *out_oid, int oid_len, uint8_t **buffer, int *size);
+
+EVP_PKEY *
+mono_btls_x509_get_pubkey (X509 *x509);
+
+int
+mono_btls_x509_get_subject_key_identifier (X509 *x509, uint8_t **buffer, int *size);
+
+int
+mono_btls_x509_print (X509 *x509, BIO *bio);
+
+int
+mono_btls_x509_add_trust_object (X509 *x509, MonoBtlsX509Purpose purpose);
+
+int
+mono_btls_x509_add_reject_object (X509 *x509, MonoBtlsX509Purpose purpose);
+
+int
+mono_btls_x509_add_explicit_trust (X509 *x509, MonoBtlsX509TrustKind kind);
+
+#endif /* defined(__btls__btls_x509__) */
--- /dev/null
+#!/bin/sh
+
+DIR=$1; shift
+FILELIST=$1; shift
+LOFILELIST=$1 ; shift
+TARGET=$1; shift
+STATIC=$1; shift
+AR=$1; shift
+RANLIB=$1; shift
+
+HEADER="# Generated by Martin's tool $0, not libtool"
+
+test -f $TARGET && exit 0
+
+rm -f $FILELIST
+rm -f $LOFILELIST
+
+while [ "$1" != "--" ]; do
+ file=$1; shift
+ filename=`basename $file`
+ LOFILE=$file.lo
+ echo "$HEADER" > $LOFILE
+ if [ "$STATIC" = "static" ]; then
+ echo "non_pic_object='$filename'" >> $LOFILE
+ else
+ echo "pic_object='$filename'" >> $LOFILE
+ fi
+ echo "$DIR/$file " >> $FILELIST
+ echo "$DIR/$LOFILE " >> $LOFILELIST
+done
+
+(cd $DIR && $AR cr $TARGET `cat $FILELIST` && $RANLIB $TARGET)
+
#ifndef _MONO_IOLAYER_IOLAYER_H_
#define _MONO_IOLAYER_IOLAYER_H_
+#include <config.h>
+#include <glib.h>
+
#if defined(__WIN32__) || defined(_WIN32)
/* Native win32 */
#define __USE_W32_SOCKETS
#include <ws2tcpip.h>
#endif
#include <psapi.h>
-#include <shlobj.h>
-/*
+
+ /*
* Workaround for missing WSAPOLLFD typedef in mingw's winsock2.h that is required for mswsock.h below.
* Remove once http://sourceforge.net/p/mingw/bugs/1980/ is fixed.
*/
short revents;
} WSAPOLLFD, *PWSAPOLLFD, *LPWSAPOLLFD;
#endif
+
+#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
#include <mswsock.h>
+#endif
+
#else /* EVERYONE ELSE */
#include "mono/io-layer/wapi.h"
#include "mono/io-layer/uglify.h"
platform_sources += ../../support/libm/complex.c
endif
+if BTLS
+btls_file_list := $(shell cat ../btls/build-shared/mono-btls-shared-lo.txt)
+btls_static_file_list := $(shell cat ../btls/build-static/mono-btls-static-lo.txt)
+btls_libs = $(btls_file_list)
+btls_static_libs = $(btls_static_file_list)
+btls_cflags = -I$(top_srcdir)/external/boringssl/include -I$(top_srcdir)/mono/btls
+endif
+
#
# libtool is not capable of creating static/shared versions of the same
# convenience lib, so we have to do it ourselves
noinst_LTLIBRARIES = libmonoruntime-config.la $(boehm_libraries) $(sgen_libraries)
endif
-AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/mono $(LIBGC_CPPFLAGS) $(GLIB_CFLAGS) $(SHARED_CFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/mono $(LIBGC_CPPFLAGS) $(GLIB_CFLAGS) $(SHARED_CFLAGS) $(btls_cflags)
#
# Make sure any prefix changes are updated in the binaries too.
libmonoruntime_la_SOURCES = $(common_sources) $(gc_dependent_sources) $(null_gc_sources) $(boehm_sources)
libmonoruntime_la_CFLAGS = $(BOEHM_DEFINES)
-libmonoruntime_la_LIBADD = libmonoruntime-config.la
+libmonoruntime_la_LIBADD = libmonoruntime-config.la $(btls_libs)
libmonoruntimesgen_la_SOURCES = $(common_sources) $(gc_dependent_sources) $(sgen_sources)
libmonoruntimesgen_la_CFLAGS = $(SGEN_DEFINES)
-libmonoruntimesgen_la_LIBADD = libmonoruntime-config.la
+libmonoruntimesgen_la_LIBADD = libmonoruntime-config.la $(btls_libs)
libmonoruntime_static_la_SOURCES = $(libmonoruntime_la_SOURCES)
libmonoruntime_static_la_LDFLAGS = -static
libmonoruntime_static_la_CFLAGS = $(BOEHM_DEFINES)
-libmonoruntime_static_la_LIBADD = $(bundle_obj) $(libmonoruntime_la_LIBADD)
+libmonoruntime_static_la_LIBADD = $(bundle_obj) libmonoruntime-config.la $(btls_static_libs)
libmonoruntimesgen_static_la_SOURCES = $(libmonoruntimesgen_la_SOURCES)
libmonoruntimesgen_static_la_LDFLAGS = -static
libmonoruntimesgen_static_la_CFLAGS = $(SGEN_DEFINES)
-libmonoruntimesgen_static_la_LIBADD = $(libmonoruntimesgen_la_LIBADD)
+libmonoruntimesgen_static_la_LIBADD = libmonoruntime-config.la $(btls_static_libs)
libmonoruntimeincludedir = $(includedir)/mono-$(API_VER)/mono/metadata
* Changes which are already detected at runtime, like the addition
* of icalls, do not require an increment.
*/
-#define MONO_CORLIB_VERSION 157
+#define MONO_CORLIB_VERSION 158
typedef struct
{
mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
}
+static void on_gc_notification (GC_EventType event);
+static void on_gc_heap_resize (size_t new_size);
+
void
mono_gc_base_init (void)
{
mono_thread_info_attach (&dummy);
- mono_gc_enable_events ();
+ GC_set_on_collection_event (on_gc_notification);
+ GC_on_heap_resize = on_gc_heap_resize;
MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
mono_profiler_gc_heap_resize (new_size);
}
-void
-mono_gc_enable_events (void)
-{
- GC_set_on_collection_event (on_gc_notification);
- GC_on_heap_resize = on_gc_heap_resize;
-}
-
-static gboolean alloc_events = FALSE;
-
-void
-mono_gc_enable_alloc_events (void)
-{
- alloc_events = TRUE;
-}
-
int
mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
{
obj->vtable = vtable;
}
- if (G_UNLIKELY (alloc_events))
+ if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
mono_profiler_allocation (obj);
return obj;
obj->max_length = max_length;
- if (G_UNLIKELY (alloc_events))
+ if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
mono_profiler_allocation (&obj->obj);
return obj;
if (bounds_size)
obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
- if (G_UNLIKELY (alloc_events))
+ if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
mono_profiler_allocation (&obj->obj);
return obj;
obj->length = len;
obj->chars [len] = 0;
- if (G_UNLIKELY (alloc_events))
+ if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
mono_profiler_allocation (&obj->object);
return obj;
#include <string.h>
#include <errno.h>
+#if defined(HOST_WIN32)
+#include <oleauto.h>
+#endif
+
/*
Code shared between the DISABLE_COM and !DISABLE_COM
*/
MonoReflectionMethod *rmethod = (MonoReflectionMethod*)obj;
cinfo = mono_custom_attrs_from_method_checked (rmethod->method, error);
return_val_if_nok (error, NULL);
- } else if ((strcmp ("MonoGenericMethod", klass->name) == 0) || (strcmp ("MonoGenericCMethod", klass->name) == 0)) {
- MonoReflectionMethod *rmethod = (MonoReflectionMethod*)obj;
- cinfo = mono_custom_attrs_from_method_checked (rmethod->method, error);
- return_val_if_nok (error, NULL);
} else if (strcmp ("ParameterInfo", klass->name) == 0 || strcmp ("MonoParameterInfo", klass->name) == 0) {
MonoReflectionParameter *param = (MonoReflectionParameter*)obj;
MonoClass *member_class = mono_object_class (param->MemberImpl);
}
#ifndef DISABLE_REFLECTION_EMIT
else if (mono_is_sre_method_on_tb_inst (member_class)) {/*XXX This is a workaround for Compiler Context*/
- MonoMethod *method = mono_reflection_method_on_tb_inst_get_handle ((MonoReflectionMethodOnTypeBuilderInst*)param->MemberImpl, error);
- return_val_if_nok (error, NULL);
- cinfo = mono_custom_attrs_from_param_checked (method, param->PositionImpl + 1, error);
- return_val_if_nok (error, NULL);
+ // FIXME: Is this still needed ?
+ g_assert_not_reached ();
} else if (mono_is_sre_ctor_on_tb_inst (member_class)) { /*XX This is a workaround for Compiler Context*/
- MonoReflectionCtorOnTypeBuilderInst *c = (MonoReflectionCtorOnTypeBuilderInst*)param->MemberImpl;
- MonoMethod *method = NULL;
- if (mono_is_sre_ctor_builder (mono_object_class (c->cb)))
- method = ((MonoReflectionCtorBuilder *)c->cb)->mhandle;
- else if (mono_is_sr_mono_cmethod (mono_object_class (c->cb)))
- method = ((MonoReflectionMethod *)c->cb)->method;
- else
- g_error ("mono_reflection_get_custom_attrs_info:: can't handle a CTBI with base_method of type %s", mono_type_get_full_name (member_class));
-
- cinfo = mono_custom_attrs_from_param_checked (method, param->PositionImpl + 1, error);
- return_val_if_nok (error, NULL);
+ // FIXME: Is this still needed ?
+ g_assert_not_reached ();
}
#endif
else {
if (source_files)
(*source_files) [i] = (*source_file_list)->len - 1;
}
- if ((*source_file_list)->len == 0 && stm.file) {
- MonoDebugSourceInfo *info = get_source_info (symfile, stm.file);
-
- g_ptr_array_add (*source_file_list, info);
- }
}
if (n_seq_points) {
g_slist_free (domain->domain_assemblies);
domain->domain_assemblies = NULL;
+ /*
+ * Send this after the assemblies have been unloaded and the domain is still in a
+ * usable state.
+ */
+ mono_profiler_appdomain_event (domain, MONO_PROFILE_END_UNLOAD);
+
if (free_domain_hook)
free_domain_hook (domain);
image->handleref_managed = mono_g_hash_table_new_type ((GHashFunc)mono_object_hash, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_REFLECTION, "dynamic module reference-to-token table");
image->tokens = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_REFLECTION, "dynamic module tokens table");
image->generic_def_objects = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_REFLECTION, "dynamic module generic definitions table");
- image->methodspec = mono_g_hash_table_new_type ((GHashFunc)mono_object_hash, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_REFLECTION, "dynamic module method specifications table");
image->typespec = g_hash_table_new ((GHashFunc)mono_metadata_type_hash, (GCompareFunc)mono_metadata_type_equal);
image->typeref = g_hash_table_new ((GHashFunc)mono_metadata_type_hash, (GCompareFunc)mono_metadata_type_equal);
image->blob_cache = g_hash_table_new ((GHashFunc)mono_blob_entry_hash, (GCompareFunc)mono_blob_entry_equal);
release_hashtable (&image->tokens);
release_hashtable (&image->remapped_tokens);
release_hashtable (&image->generic_def_objects);
- release_hashtable (&image->methodspec);
}
// Free dynamic image pass one: Free resources but not image itself
GList *list;
int i;
- if (di->methodspec)
- mono_g_hash_table_destroy (di->methodspec);
if (di->typespec)
g_hash_table_destroy (di->typespec);
if (di->typeref)
gboolean mono_object_is_alive (MonoObject* obj);
gboolean mono_gc_is_finalizer_thread (MonoThread *thread);
gpointer mono_gc_out_of_memory (size_t size);
-void mono_gc_enable_events (void);
-void mono_gc_enable_alloc_events (void);
void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
mono_gc_finalize_threadpool_threads ();
}
- mono_profiler_appdomain_event (domain, MONO_PROFILE_END_UNLOAD);
-
done:
if (InterlockedDecrement (&req->ref) == 0) {
mono_coop_sem_destroy (&req->done);
ICALL(NATIVEMETHODS_11, "WaitForInputIdle", ves_icall_Microsoft_Win32_NativeMethods_WaitForInputIdle)
#endif /* !DISABLE_PROCESS_HANDLING */
+#if HAVE_BTLS
+ICALL_TYPE(BTLS_BIO, "Mono.Btls.MonoBtlsBio", BTLS_BIO_1)
+ICALL(BTLS_BIO_1, "mono_btls_bio_flush", mono_btls_bio_flush)
+ICALL(BTLS_BIO_2, "mono_btls_bio_free", mono_btls_bio_free)
+ICALL(BTLS_BIO_3, "mono_btls_bio_hexdump", mono_btls_bio_hexdump)
+ICALL(BTLS_BIO_4, "mono_btls_bio_indent", mono_btls_bio_indent)
+ICALL(BTLS_BIO_5, "mono_btls_bio_print_errors", mono_btls_bio_print_errors)
+ICALL(BTLS_BIO_6, "mono_btls_bio_read", mono_btls_bio_read)
+ICALL(BTLS_BIO_7, "mono_btls_bio_write", mono_btls_bio_write)
+
+ICALL_TYPE(BTLS_BIO_MEM, "Mono.Btls.MonoBtlsBioMemory", BTLS_BIO_MEM_1)
+ICALL(BTLS_BIO_MEM_1, "mono_btls_bio_mem_get_data", mono_btls_bio_mem_get_data)
+ICALL(BTLS_BIO_MEM_2, "mono_btls_bio_mem_new", mono_btls_bio_mem_new)
+
+ICALL_TYPE(BTLS_BIO_MONO, "Mono.Btls.MonoBtlsBioMono", BTLS_BIO_MONO_1)
+ICALL(BTLS_BIO_MONO_1, "mono_btls_bio_mono_initialize", mono_btls_bio_mono_initialize)
+ICALL(BTLS_BIO_MONO_2, "mono_btls_bio_mono_new", mono_btls_bio_mono_new)
+
+ICALL_TYPE(BTLS_ERROR, "Mono.Btls.MonoBtlsError", BTLS_ERROR_1)
+ICALL(BTLS_ERROR_1, "mono_btls_error_clear_error", mono_btls_error_clear_error)
+ICALL(BTLS_ERROR_2, "mono_btls_error_get_error", mono_btls_error_get_error)
+ICALL(BTLS_ERROR_3, "mono_btls_error_get_error_string_n", mono_btls_error_get_error_string_n)
+ICALL(BTLS_ERROR_4, "mono_btls_error_peek_error", mono_btls_error_peek_error)
+
+ICALL_TYPE(BTLS_KEY, "Mono.Btls.MonoBtlsKey", BTLS_KEY_1)
+ICALL(BTLS_KEY_1, "mono_btls_key_free", mono_btls_key_free)
+ICALL(BTLS_KEY_2, "mono_btls_key_get_bits", mono_btls_key_get_bits)
+ICALL(BTLS_KEY_3, "mono_btls_key_get_bytes", mono_btls_key_get_bytes)
+ICALL(BTLS_KEY_4, "mono_btls_key_is_rsa", mono_btls_key_is_rsa)
+ICALL(BTLS_KEY_5, "mono_btls_key_up_ref", mono_btls_key_up_ref)
+
+ICALL_TYPE(BTLS_OBJECT, "Mono.Btls.MonoBtlsObject", BTLS_OBJECT_1)
+ICALL(BTLS_OBJECT_1, "mono_btls_free", mono_btls_free)
+
+ICALL_TYPE(BTLS_PKCS12, "Mono.Btls.MonoBtlsPkcs12", BTLS_PKCS12_1)
+ICALL(BTLS_PKCS12_1, "mono_btls_pkcs12_add_cert", mono_btls_pkcs12_add_cert)
+ICALL(BTLS_PKCS12_2, "mono_btls_pkcs12_free", mono_btls_pkcs12_free)
+ICALL(BTLS_PKCS12_3, "mono_btls_pkcs12_get_cert", mono_btls_pkcs12_get_cert)
+ICALL(BTLS_PKCS12_4, "mono_btls_pkcs12_get_certs", mono_btls_pkcs12_get_certs)
+ICALL(BTLS_PKCS12_5, "mono_btls_pkcs12_get_count", mono_btls_pkcs12_get_count)
+ICALL(BTLS_PKCS12_6, "mono_btls_pkcs12_get_private_key", mono_btls_pkcs12_get_private_key)
+ICALL(BTLS_PKCS12_7, "mono_btls_pkcs12_has_private_key", mono_btls_pkcs12_has_private_key)
+ICALL(BTLS_PKCS12_8, "mono_btls_pkcs12_import", mono_btls_pkcs12_import)
+ICALL(BTLS_PKCS12_9, "mono_btls_pkcs12_new", mono_btls_pkcs12_new)
+ICALL(BTLS_PKCS12_10, "mono_btls_pkcs12_up_ref", mono_btls_pkcs12_up_ref)
+
+ICALL_TYPE(BTLS_PROVIDER, "Mono.Btls.MonoBtlsProvider", BTLS_PROVIDER_1)
+ICALL(BTLS_PROVIDER_1, "IsSupported", ves_icall_Mono_Btls_Provider_IsSupported)
+
+ICALL_TYPE(BTLS_SSL, "Mono.Btls.MonoBtlsSsl", BTLS_SSL_1)
+ICALL(BTLS_SSL_1, "mono_btls_ssl_accept", mono_btls_ssl_accept)
+ICALL(BTLS_SSL_2, "mono_btls_ssl_add_chain_certificate", mono_btls_ssl_add_chain_certificate)
+ICALL(BTLS_SSL_3, "mono_btls_ssl_close", mono_btls_ssl_close)
+ICALL(BTLS_SSL_4, "mono_btls_ssl_connect", mono_btls_ssl_connect)
+ICALL(BTLS_SSL_5, "mono_btls_ssl_destroy", mono_btls_ssl_destroy)
+ICALL(BTLS_SSL_6, "mono_btls_ssl_get_cipher", mono_btls_ssl_get_cipher)
+ICALL(BTLS_SSL_7, "mono_btls_ssl_get_ciphers", mono_btls_ssl_get_ciphers)
+ICALL(BTLS_SSL_8, "mono_btls_ssl_get_error", mono_btls_ssl_get_error)
+ICALL(BTLS_SSL_9, "mono_btls_ssl_get_peer_certificate", mono_btls_ssl_get_peer_certificate)
+ICALL(BTLS_SSL_10, "mono_btls_ssl_get_version", mono_btls_ssl_get_version)
+ICALL(BTLS_SSL_11, "mono_btls_ssl_handshake", mono_btls_ssl_handshake)
+ICALL(BTLS_SSL_12, "mono_btls_ssl_new", mono_btls_ssl_new)
+ICALL(BTLS_SSL_13, "mono_btls_ssl_print_errors_cb", mono_btls_ssl_print_errors_cb)
+ICALL(BTLS_SSL_14, "mono_btls_ssl_read", mono_btls_ssl_read)
+ICALL(BTLS_SSL_15, "mono_btls_ssl_set_bio", mono_btls_ssl_set_bio)
+ICALL(BTLS_SSL_16, "mono_btls_ssl_set_cipher_list", mono_btls_ssl_set_cipher_list)
+ICALL(BTLS_SSL_17, "mono_btls_ssl_set_max_version", mono_btls_ssl_set_max_version)
+ICALL(BTLS_SSL_18, "mono_btls_ssl_set_min_version", mono_btls_ssl_set_min_version)
+ICALL(BTLS_SSL_19, "mono_btls_ssl_set_server_name", mono_btls_ssl_set_server_name)
+ICALL(BTLS_SSL_20, "mono_btls_ssl_set_verify_param", mono_btls_ssl_set_verify_param)
+ICALL(BTLS_SSL_21, "mono_btls_ssl_use_certificate", mono_btls_ssl_use_certificate)
+ICALL(BTLS_SSL_22, "mono_btls_ssl_use_private_key", mono_btls_ssl_use_private_key)
+ICALL(BTLS_SSL_23, "mono_btls_ssl_write", mono_btls_ssl_write)
+
+ICALL_TYPE(BTLS_SSL_CTX, "Mono.Btls.MonoBtlsSslCtx", BTLS_SSL_CTX_1)
+ICALL(BTLS_SSL_CTX_1, "mono_btls_ssl_ctx_debug_printf", mono_btls_ssl_ctx_debug_printf)
+ICALL(BTLS_SSL_CTX_2, "mono_btls_ssl_ctx_free", mono_btls_ssl_ctx_free)
+ICALL(BTLS_SSL_CTX_3, "mono_btls_ssl_ctx_get_ctx", mono_btls_ssl_ctx_get_ctx)
+ICALL(BTLS_SSL_CTX_4, "mono_btls_ssl_ctx_initialize", mono_btls_ssl_ctx_initialize)
+ICALL(BTLS_SSL_CTX_5, "mono_btls_ssl_ctx_is_cipher_supported", mono_btls_ssl_ctx_is_cipher_supported)
+ICALL(BTLS_SSL_CTX_6, "mono_btls_ssl_ctx_is_debug_enabled", mono_btls_ssl_ctx_is_debug_enabled)
+ICALL(BTLS_SSL_CTX_7, "mono_btls_ssl_ctx_new", mono_btls_ssl_ctx_new)
+ICALL(BTLS_SSL_CTX_8, "mono_btls_ssl_ctx_peek_store", mono_btls_ssl_ctx_peek_store)
+ICALL(BTLS_SSL_CTX_9, "mono_btls_ssl_ctx_set_cert_select_callback", mono_btls_ssl_ctx_set_cert_select_callback)
+ICALL(BTLS_SSL_CTX_10, "mono_btls_ssl_ctx_set_cert_verify_callback", mono_btls_ssl_ctx_set_cert_verify_callback)
+ICALL(BTLS_SSL_CTX_11, "mono_btls_ssl_ctx_set_ciphers", mono_btls_ssl_ctx_set_ciphers)
+ICALL(BTLS_SSL_CTX_12, "mono_btls_ssl_ctx_set_debug_bio", mono_btls_ssl_ctx_set_debug_bio)
+ICALL(BTLS_SSL_CTX_13, "mono_btls_ssl_ctx_set_max_version", mono_btls_ssl_ctx_set_max_version)
+ICALL(BTLS_SSL_CTX_14, "mono_btls_ssl_ctx_set_min_version", mono_btls_ssl_ctx_set_min_version)
+ICALL(BTLS_SSL_CTX_15, "mono_btls_ssl_ctx_set_verify_param", mono_btls_ssl_ctx_set_verify_param)
+ICALL(BTLS_SSL_CTX_16, "mono_btls_ssl_ctx_up_ref", mono_btls_ssl_ctx_up_ref)
+
+ICALL_TYPE(BTLS_X509, "Mono.Btls.MonoBtlsX509", BTLS_X509_1)
+ICALL(BTLS_X509_1, "mono_btls_x509_add_explicit_trust", mono_btls_x509_add_explicit_trust)
+ICALL(BTLS_X509_2, "mono_btls_x509_add_reject_object", mono_btls_x509_add_reject_object)
+ICALL(BTLS_X509_3, "mono_btls_x509_add_trust_object", mono_btls_x509_add_trust_object)
+ICALL(BTLS_X509_4, "mono_btls_x509_cmp", mono_btls_x509_cmp)
+ICALL(BTLS_X509_5, "mono_btls_x509_dup", mono_btls_x509_dup)
+ICALL(BTLS_X509_6, "mono_btls_x509_free", mono_btls_x509_free)
+ICALL(BTLS_X509_7, "mono_btls_x509_from_data", mono_btls_x509_from_data)
+ICALL(BTLS_X509_8, "mono_btls_x509_get_hash", mono_btls_x509_get_hash)
+ICALL(BTLS_X509_9, "mono_btls_x509_get_issuer_name", mono_btls_x509_get_issuer_name)
+ICALL(BTLS_X509_10, "mono_btls_x509_get_issuer_name_string", mono_btls_x509_get_issuer_name_string)
+ICALL(BTLS_X509_11, "mono_btls_x509_get_not_after", mono_btls_x509_get_not_after)
+ICALL(BTLS_X509_12, "mono_btls_x509_get_not_before", mono_btls_x509_get_not_before)
+ICALL(BTLS_X509_13, "mono_btls_x509_get_pubkey", mono_btls_x509_get_pubkey)
+ICALL(BTLS_X509_14, "mono_btls_x509_get_public_key", mono_btls_x509_get_public_key)
+ICALL(BTLS_X509_15, "mono_btls_x509_get_public_key_algorithm", mono_btls_x509_get_public_key_algorithm)
+ICALL(BTLS_X509_16, "mono_btls_x509_get_public_key_asn1", mono_btls_x509_get_public_key_asn1)
+ICALL(BTLS_X509_17, "mono_btls_x509_get_public_key_parameters", mono_btls_x509_get_public_key_parameters)
+ICALL(BTLS_X509_18, "mono_btls_x509_get_raw_data", mono_btls_x509_get_raw_data)
+ICALL(BTLS_X509_19, "mono_btls_x509_get_serial_number", mono_btls_x509_get_serial_number)
+ICALL(BTLS_X509_20, "mono_btls_x509_get_signature_algorithm", mono_btls_x509_get_signature_algorithm)
+ICALL(BTLS_X509_21, "mono_btls_x509_get_subject_key_identifier", mono_btls_x509_get_subject_key_identifier)
+ICALL(BTLS_X509_22, "mono_btls_x509_get_subject_name", mono_btls_x509_get_subject_name)
+ICALL(BTLS_X509_23, "mono_btls_x509_get_subject_name_string", mono_btls_x509_get_subject_name_string)
+ICALL(BTLS_X509_24, "mono_btls_x509_get_version", mono_btls_x509_get_version)
+ICALL(BTLS_X509_25, "mono_btls_x509_print", mono_btls_x509_print)
+ICALL(BTLS_X509_26, "mono_btls_x509_up_ref", mono_btls_x509_up_ref)
+
+ICALL_TYPE(BTLS_X509_CHAIN, "Mono.Btls.MonoBtlsX509Chain", BTLS_X509_CHAIN_1)
+ICALL(BTLS_X509_CHAIN_1, "mono_btls_x509_chain_add_cert", mono_btls_x509_chain_add_cert)
+ICALL(BTLS_X509_CHAIN_2, "mono_btls_x509_chain_free", mono_btls_x509_chain_free)
+ICALL(BTLS_X509_CHAIN_3, "mono_btls_x509_chain_from_certs", mono_btls_x509_chain_from_certs)
+ICALL(BTLS_X509_CHAIN_4, "mono_btls_x509_chain_get_cert", mono_btls_x509_chain_get_cert)
+ICALL(BTLS_X509_CHAIN_5, "mono_btls_x509_chain_get_count", mono_btls_x509_chain_get_count)
+ICALL(BTLS_X509_CHAIN_6, "mono_btls_x509_chain_new", mono_btls_x509_chain_new)
+ICALL(BTLS_X509_CHAIN_7, "mono_btls_x509_chain_peek_certs", mono_btls_x509_chain_peek_certs)
+ICALL(BTLS_X509_CHAIN_8, "mono_btls_x509_chain_up_ref", mono_btls_x509_chain_up_ref)
+
+ICALL_TYPE(BTLS_X509_CRL, "Mono.Btls.MonoBtlsX509Crl", BTLS_X509_CRL_1)
+ICALL(BTLS_X509_CRL_1, "mono_btls_x509_crl_free", mono_btls_x509_crl_free)
+ICALL(BTLS_X509_CRL_2, "mono_btls_x509_crl_from_data", mono_btls_x509_crl_from_data)
+ICALL(BTLS_X509_CRL_3, "mono_btls_x509_crl_get_by_cert", mono_btls_x509_crl_get_by_cert)
+ICALL(BTLS_X509_CRL_4, "mono_btls_x509_crl_get_by_serial", mono_btls_x509_crl_get_by_serial)
+ICALL(BTLS_X509_CRL_5, "mono_btls_x509_crl_get_issuer", mono_btls_x509_crl_get_issuer)
+ICALL(BTLS_X509_CRL_6, "mono_btls_x509_crl_get_last_update", mono_btls_x509_crl_get_last_update)
+ICALL(BTLS_X509_CRL_7, "mono_btls_x509_crl_get_next_update", mono_btls_x509_crl_get_next_update)
+ICALL(BTLS_X509_CRL_8, "mono_btls_x509_crl_get_revoked", mono_btls_x509_crl_get_revoked)
+ICALL(BTLS_X509_CRL_9, "mono_btls_x509_crl_get_revoked_count", mono_btls_x509_crl_get_revoked_count)
+ICALL(BTLS_X509_CRL_10, "mono_btls_x509_crl_get_version", mono_btls_x509_crl_get_version)
+ICALL(BTLS_X509_CRL_11, "mono_btls_x509_crl_ref", mono_btls_x509_crl_ref)
+
+ICALL_TYPE(BTLS_X509_LOOKUP, "Mono.Btls.MonoBtlsX509Lookup", BTLS_X509_LOOKUP_1)
+ICALL(BTLS_X509_LOOKUP_1, "mono_btls_x509_lookup_add_dir", mono_btls_x509_lookup_add_dir)
+ICALL(BTLS_X509_LOOKUP_2, "mono_btls_x509_lookup_add_mono", mono_btls_x509_lookup_add_mono)
+ICALL(BTLS_X509_LOOKUP_3, "mono_btls_x509_lookup_by_fingerprint", mono_btls_x509_lookup_by_fingerprint)
+ICALL(BTLS_X509_LOOKUP_4, "mono_btls_x509_lookup_by_subject", mono_btls_x509_lookup_by_subject)
+ICALL(BTLS_X509_LOOKUP_5, "mono_btls_x509_lookup_free", mono_btls_x509_lookup_free)
+ICALL(BTLS_X509_LOOKUP_6, "mono_btls_x509_lookup_init", mono_btls_x509_lookup_init)
+ICALL(BTLS_X509_LOOKUP_7, "mono_btls_x509_lookup_load_file", mono_btls_x509_lookup_load_file)
+ICALL(BTLS_X509_LOOKUP_8, "mono_btls_x509_lookup_mono_init", mono_btls_x509_lookup_mono_init)
+ICALL(BTLS_X509_LOOKUP_9, "mono_btls_x509_lookup_new", mono_btls_x509_lookup_new)
+ICALL(BTLS_X509_LOOKUP_10, "mono_btls_x509_lookup_peek_lookup", mono_btls_x509_lookup_peek_lookup)
+ICALL(BTLS_X509_LOOKUP_11, "mono_btls_x509_lookup_shutdown", mono_btls_x509_lookup_shutdown)
+ICALL(BTLS_X509_LOOKUP_12, "mono_btls_x509_lookup_up_ref", mono_btls_x509_lookup_up_ref)
+
+ICALL_TYPE(BTLS_X509_LOOKUP_MONO, "Mono.Btls.MonoBtlsX509LookupMono", BTLS_X509_LOOKUP_MONO_1)
+ICALL(BTLS_X509_LOOKUP_MONO_1, "mono_btls_x509_lookup_mono_free", mono_btls_x509_lookup_mono_free)
+ICALL(BTLS_X509_LOOKUP_MONO_2, "mono_btls_x509_lookup_mono_init", mono_btls_x509_lookup_mono_init)
+ICALL(BTLS_X509_LOOKUP_MONO_3, "mono_btls_x509_lookup_mono_new", mono_btls_x509_lookup_mono_new)
+
+ICALL_TYPE(BTLS_X509_NAME, "Mono.Btls.MonoBtlsX509Name", BTLS_X509_NAME_1)
+ICALL(BTLS_X509_NAME_1, "mono_btls_x509_name_copy", mono_btls_x509_name_copy)
+ICALL(BTLS_X509_NAME_2, "mono_btls_x509_name_free", mono_btls_x509_name_free)
+ICALL(BTLS_X509_NAME_3, "mono_btls_x509_name_from_data", mono_btls_x509_name_from_data)
+ICALL(BTLS_X509_NAME_4, "mono_btls_x509_name_from_name", mono_btls_x509_name_from_name)
+ICALL(BTLS_X509_NAME_5, "mono_btls_x509_name_get_entry_count", mono_btls_x509_name_get_entry_count)
+ICALL(BTLS_X509_NAME_6, "mono_btls_x509_name_get_entry_oid", mono_btls_x509_name_get_entry_oid)
+ICALL(BTLS_X509_NAME_7, "mono_btls_x509_name_get_entry_oid_data", mono_btls_x509_name_get_entry_oid_data)
+ICALL(BTLS_X509_NAME_8, "mono_btls_x509_name_get_entry_type", mono_btls_x509_name_get_entry_type)
+ICALL(BTLS_X509_NAME_9, "mono_btls_x509_name_get_entry_value", mono_btls_x509_name_get_entry_value)
+ICALL(BTLS_X509_NAME_10, "mono_btls_x509_name_get_raw_data", mono_btls_x509_name_get_raw_data)
+ICALL(BTLS_X509_NAME_11, "mono_btls_x509_name_hash", mono_btls_x509_name_hash)
+ICALL(BTLS_X509_NAME_12, "mono_btls_x509_name_hash_old", mono_btls_x509_name_hash_old)
+ICALL(BTLS_X509_NAME_13, "mono_btls_x509_name_peek_name", mono_btls_x509_name_peek_name)
+ICALL(BTLS_X509_NAME_14, "mono_btls_x509_name_print_bio", mono_btls_x509_name_print_bio)
+ICALL(BTLS_X509_NAME_15, "mono_btls_x509_name_print_string", mono_btls_x509_name_print_string)
+
+ICALL_TYPE(BTLS_X509_REVOKED, "Mono.Btls.MonoBtlsX509Revoked", BTLS_X509_REVOKED_1)
+ICALL(BTLS_X509_REVOKED_1, "mono_btls_x509_revoked_free", mono_btls_x509_revoked_free)
+ICALL(BTLS_X509_REVOKED_2, "mono_btls_x509_revoked_get_reason", mono_btls_x509_revoked_get_reason)
+ICALL(BTLS_X509_REVOKED_2a, "mono_btls_x509_revoked_get_revocation_date", mono_btls_x509_revoked_get_revocation_date)
+ICALL(BTLS_X509_REVOKED_3, "mono_btls_x509_revoked_get_sequence", mono_btls_x509_revoked_get_sequence)
+ICALL(BTLS_X509_REVOKED_4, "mono_btls_x509_revoked_get_serial_number", mono_btls_x509_revoked_get_serial_number)
+ICALL(BTLS_X509_REVOKED_5, "mono_btls_x509_revoked_new", mono_btls_x509_revoked_new)
+
+ICALL_TYPE(BTLS_X509_STORE, "Mono.Btls.MonoBtlsX509Store", BTLS_X509_STORE_1)
+ICALL(BTLS_X509_STORE_1, "mono_btls_x509_store_add_cert", mono_btls_x509_store_add_cert)
+ICALL(BTLS_X509_STORE_2, "mono_btls_x509_store_free", mono_btls_x509_store_free)
+ICALL(BTLS_X509_STORE_3, "mono_btls_x509_store_from_ctx", mono_btls_x509_store_from_ctx)
+ICALL(BTLS_X509_STORE_4, "mono_btls_x509_store_from_ssl_ctx", mono_btls_x509_store_from_ssl_ctx)
+ICALL(BTLS_X509_STORE_5, "mono_btls_x509_store_get_count", mono_btls_x509_store_get_count)
+ICALL(BTLS_X509_STORE_6, "mono_btls_x509_store_load_locations", mono_btls_x509_store_load_locations)
+ICALL(BTLS_X509_STORE_7, "mono_btls_x509_store_new", mono_btls_x509_store_new)
+ICALL(BTLS_X509_STORE_8, "mono_btls_x509_store_peek_store", mono_btls_x509_store_peek_store)
+ICALL(BTLS_X509_STORE_9, "mono_btls_x509_store_set_default_paths", mono_btls_x509_store_set_default_paths)
+ICALL(BTLS_X509_STORE_10, "mono_btls_x509_store_up_ref", mono_btls_x509_store_up_ref)
+
+ICALL_TYPE(BTLS_X509_STORE_CTX, "Mono.Btls.MonoBtlsX509StoreCtx", BTLS_X509_STORE_CTX_1)
+ICALL(BTLS_X509_STORE_CTX_1, "mono_btls_x509_store_ctx_free", mono_btls_x509_store_ctx_free)
+ICALL(BTLS_X509_STORE_CTX_2, "mono_btls_x509_store_ctx_from_ptr", mono_btls_x509_store_ctx_from_ptr)
+ICALL(BTLS_X509_STORE_CTX_3, "mono_btls_x509_store_ctx_get_by_subject", mono_btls_x509_store_ctx_get_by_subject)
+ICALL(BTLS_X509_STORE_CTX_4, "mono_btls_x509_store_ctx_get_chain", mono_btls_x509_store_ctx_get_chain)
+ICALL(BTLS_X509_STORE_CTX_5, "mono_btls_x509_store_ctx_get_current_cert", mono_btls_x509_store_ctx_get_current_cert)
+ICALL(BTLS_X509_STORE_CTX_6, "mono_btls_x509_store_ctx_get_current_issuer", mono_btls_x509_store_ctx_get_current_issuer)
+ICALL(BTLS_X509_STORE_CTX_7, "mono_btls_x509_store_ctx_get_error", mono_btls_x509_store_ctx_get_error)
+ICALL(BTLS_X509_STORE_CTX_8, "mono_btls_x509_store_ctx_get_error_depth", mono_btls_x509_store_ctx_get_error_depth)
+ICALL(BTLS_X509_STORE_CTX_9, "mono_btls_x509_store_ctx_get_untrusted", mono_btls_x509_store_ctx_get_untrusted)
+ICALL(BTLS_X509_STORE_CTX_10, "mono_btls_x509_store_ctx_get_verify_param", mono_btls_x509_store_ctx_get_verify_param)
+ICALL(BTLS_X509_STORE_CTX_11, "mono_btls_x509_store_ctx_init", mono_btls_x509_store_ctx_init)
+ICALL(BTLS_X509_STORE_CTX_12, "mono_btls_x509_store_ctx_new", mono_btls_x509_store_ctx_new)
+ICALL(BTLS_X509_STORE_CTX_13, "mono_btls_x509_store_ctx_set_param", mono_btls_x509_store_ctx_set_param)
+ICALL(BTLS_X509_STORE_CTX_15, "mono_btls_x509_store_ctx_up_ref", mono_btls_x509_store_ctx_up_ref)
+ICALL(BTLS_X509_STORE_CTX_16, "mono_btls_x509_store_ctx_verify_cert", mono_btls_x509_store_ctx_verify_cert)
+
+ICALL_TYPE(BTLS_X509_VERIFY_PARAM, "Mono.Btls.MonoBtlsX509VerifyParam", BTLS_X509_VERIFY_PARAM_1)
+ICALL(BTLS_X509_VERIFY_PARAM_1, "mono_btls_x509_verify_param_add_host", mono_btls_x509_verify_param_add_host)
+ICALL(BTLS_X509_VERIFY_PARAM_2, "mono_btls_x509_verify_param_can_modify", mono_btls_x509_verify_param_can_modify)
+ICALL(BTLS_X509_VERIFY_PARAM_3, "mono_btls_x509_verify_param_copy", mono_btls_x509_verify_param_copy)
+ICALL(BTLS_X509_VERIFY_PARAM_4, "mono_btls_x509_verify_param_free", mono_btls_x509_verify_param_free)
+ICALL(BTLS_X509_VERIFY_PARAM_5, "mono_btls_x509_verify_param_from_store_ctx", mono_btls_x509_verify_param_from_store_ctx)
+ICALL(BTLS_X509_VERIFY_PARAM_6, "mono_btls_x509_verify_param_get_depth", mono_btls_x509_verify_param_get_depth)
+ICALL(BTLS_X509_VERIFY_PARAM_7, "mono_btls_x509_verify_param_get_flags", mono_btls_x509_verify_param_get_flags)
+ICALL(BTLS_X509_VERIFY_PARAM_8, "mono_btls_x509_verify_param_get_mono_flags", mono_btls_x509_verify_param_get_mono_flags)
+ICALL(BTLS_X509_VERIFY_PARAM_9, "mono_btls_x509_verify_param_get_peername", mono_btls_x509_verify_param_get_peername)
+ICALL(BTLS_X509_VERIFY_PARAM_10, "mono_btls_x509_verify_param_lookup", mono_btls_x509_verify_param_lookup)
+ICALL(BTLS_X509_VERIFY_PARAM_11, "mono_btls_x509_verify_param_new", mono_btls_x509_verify_param_new)
+ICALL(BTLS_X509_VERIFY_PARAM_12, "mono_btls_x509_verify_param_peek_param", mono_btls_x509_verify_param_peek_param)
+ICALL(BTLS_X509_VERIFY_PARAM_13, "mono_btls_x509_verify_param_set_depth", mono_btls_x509_verify_param_set_depth)
+ICALL(BTLS_X509_VERIFY_PARAM_14, "mono_btls_x509_verify_param_set_flags", mono_btls_x509_verify_param_set_flags)
+ICALL(BTLS_X509_VERIFY_PARAM_15, "mono_btls_x509_verify_param_set_host", mono_btls_x509_verify_param_set_host)
+ICALL(BTLS_X509_VERIFY_PARAM_16, "mono_btls_x509_verify_param_set_mono_flags", mono_btls_x509_verify_param_set_mono_flags)
+ICALL(BTLS_X509_VERIFY_PARAM_17, "mono_btls_x509_verify_param_set_name", mono_btls_x509_verify_param_set_name)
+ICALL(BTLS_X509_VERIFY_PARAM_18, "mono_btls_x509_verify_param_set_purpose", mono_btls_x509_verify_param_set_purpose)
+ICALL(BTLS_X509_VERIFY_PARAM_19, "mono_btls_x509_verify_param_set_time", mono_btls_x509_verify_param_set_time)
+#else
+ICALL_TYPE(BTLS_PROVIDER, "Mono.Btls.MonoBtlsProvider", BTLS_PROVIDER_1)
+ICALL(BTLS_PROVIDER_1, "IsSupported", ves_icall_Mono_Btls_Provider_IsSupported)
+#endif
+
#ifndef DISABLE_COM
ICALL_TYPE(COMPROX, "Mono.Interop.ComInteropProxy", COMPROX_1)
ICALL(COMPROX_1, "AddProxy", ves_icall_Mono_Interop_ComInteropProxy_AddProxy)
ICALL_TYPE(ENUMB, "System.Reflection.Emit.EnumBuilder", ENUMB_1)
ICALL(ENUMB_1, "setup_enum_type", ves_icall_EnumBuilder_setup_enum_type)
-ICALL_TYPE(GPARB, "System.Reflection.Emit.GenericTypeParameterBuilder", GPARB_1)
-ICALL(GPARB_1, "initialize", ves_icall_GenericTypeParameterBuilder_initialize_generic_parameter)
-
-ICALL_TYPE(METHODB, "System.Reflection.Emit.MethodBuilder", METHODB_1)
-ICALL(METHODB_1, "MakeGenericMethod", ves_icall_MethodBuilder_MakeGenericMethod)
-
ICALL_TYPE(MODULEB, "System.Reflection.Emit.ModuleBuilder", MODULEB_10)
ICALL(MODULEB_10, "GetRegisteredToken", ves_icall_ModuleBuilder_GetRegisteredToken)
ICALL(MODULEB_8, "RegisterToken", ves_icall_ModuleBuilder_RegisterToken)
ICALL(SIGH_1, "get_signature_field", ves_icall_SignatureHelper_get_signature_field)
ICALL(SIGH_2, "get_signature_local", ves_icall_SignatureHelper_get_signature_local)
-#ifndef DISABLE_REFLECTION_EMIT
-ICALL_TYPE(SYMBOLTYPE, "System.Reflection.Emit.SymbolType", SYMBOLTYPE_1)
-ICALL(SYMBOLTYPE_1, "create_unmanaged_type", ves_icall_SymbolType_create_unmanaged_type)
-#endif
-
ICALL_TYPE(TYPEB, "System.Reflection.Emit.TypeBuilder", TYPEB_1)
-ICALL(TYPEB_1, "create_generic_class", ves_icall_TypeBuilder_create_generic_class)
-ICALL(TYPEB_3, "create_runtime_class", ves_icall_TypeBuilder_create_runtime_class)
-ICALL(TYPEB_4, "get_IsGenericParameter", ves_icall_TypeBuilder_get_IsGenericParameter)
-ICALL(TYPEB_5, "get_event_info", ves_icall_TypeBuilder_get_event_info)
-ICALL(TYPEB_7, "setup_internal_class", ves_icall_TypeBuilder_setup_internal_class)
+ICALL(TYPEB_1, "create_runtime_class", ves_icall_TypeBuilder_create_runtime_class)
ICALL_TYPE(EVENTI, "System.Reflection.EventInfo", EVENTI_1)
ICALL(EVENTI_1, "internal_from_handle_type", ves_icall_System_Reflection_EventInfo_internal_from_handle_type)
ICALL(MFIELD_4, "SetValueInternal", ves_icall_MonoField_SetValueInternal)
ICALL(MFIELD_7, "get_core_clr_security_level", ves_icall_MonoField_get_core_clr_security_level)
-ICALL_TYPE(MGENCL, "System.Reflection.MonoGenericClass", MGENCL_5)
-ICALL(MGENCL_5, "initialize", mono_reflection_generic_class_initialize)
-ICALL(MGENCL_6, "register_with_runtime", mono_reflection_register_with_runtime)
-
ICALL_TYPE(MMETH, "System.Reflection.MonoMethod", MMETH_2)
ICALL(MMETH_2, "GetGenericArguments", ves_icall_MonoMethod_GetGenericArguments)
ICALL(MMETH_3, "GetGenericMethodDefinition_impl", ves_icall_MonoMethod_GetGenericMethodDefinition)
#include <sys/utsname.h>
#endif
+#if HAVE_BTLS
+#include <btls/btls-ssl.h>
+#include <btls/btls-bio.h>
+#include <btls/btls-error.h>
+#include <btls/btls-key.h>
+#include <btls/btls-pkcs12.h>
+#include <btls/btls-x509-crl.h>
+#include <btls/btls-x509-chain.h>
+#include <btls/btls-x509-lookup.h>
+#include <btls/btls-x509-lookup-mono.h>
+#include <btls/btls-x509-name.h>
+#include <btls/btls-x509-revoked.h>
+#include <btls/btls-x509-store-ctx.h>
+#include <btls/btls-x509-verify-param.h>
+#endif
+
extern MonoString* ves_icall_System_Environment_GetOSVersionString (void);
ICALL_EXPORT MonoReflectionAssembly* ves_icall_System_Reflection_Assembly_GetCallingAssembly (void);
return SetPriorityClass (handle, priorityClass);
}
+ICALL_EXPORT MonoBoolean
+ves_icall_Mono_Btls_Provider_IsSupported (void)
+{
+#if HAVE_BTLS
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
#ifndef DISABLE_ICALL_TABLES
#define ICALL_TYPE(id,name,first)
#include <string.h>
#include <errno.h>
+#if defined(HOST_WIN32)
+#include <objbase.h>
+#endif
+
/* #define DEBUG_RUNTIME_CODE */
#define OPDEF(a,b,c,d,e,f,g,h,i,j) \
GHashTable *method_aux_hash;
GHashTable *vararg_aux_hash;
MonoGHashTable *generic_def_objects;
- MonoGHashTable *methodspec;
/*
* Maps final token values to the object they describe.
*/
return TRUE;
}
-void
-mono_gc_enable_events (void)
-{
-}
-
-void
-mono_gc_enable_alloc_events (void)
-{
-}
-
int
mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
{
guint32 attrs;
} MonoReflectionGenericParam;
+typedef struct {
+ MonoReflectionType type;
+ MonoReflectionTypeBuilder *tb;
+} MonoReflectionEnumBuilder;
+
typedef struct _MonoReflectionGenericClass MonoReflectionGenericClass;
struct _MonoReflectionGenericClass {
MonoReflectionType type;
MonoReflectionType *generic_type; /*Can be either a MonoType or a TypeBuilder*/
MonoArray *type_arguments;
- guint32 initialized;
};
typedef struct {
MonoArray *modopts;
} MonoReflectionSigHelper;
-typedef struct {
- MonoObject object;
- MonoReflectionGenericClass *inst;
- MonoObject *fb; /*can be either a MonoField or a FieldBuilder*/
-} MonoReflectionFieldOnTypeBuilderInst;
-
-typedef struct {
- MonoObject object;
- MonoReflectionGenericClass *inst;
- MonoObject *cb; /*can be either a MonoCMethod or ConstructorBuilder*/
-} MonoReflectionCtorOnTypeBuilderInst;
-
-typedef struct {
- MonoObject object;
- MonoReflectionType *inst;
- MonoObject *mb; /*can be either a MonoMethod or MethodBuilder*/
- MonoArray *method_args;
- MonoReflectionMethodBuilder *generic_method_definition;
-} MonoReflectionMethodOnTypeBuilderInst;
-
typedef struct {
MonoObject object;
MonoBoolean visible;
void mono_reflection_setup_internal_class (MonoReflectionTypeBuilder *tb);
-MonoReflectionType*
-ves_icall_TypeBuilder_create_runtime_class (MonoReflectionTypeBuilder *tb);
-
-void
-ves_icall_TypeBuilder_setup_internal_class (MonoReflectionTypeBuilder *tb);
-
void mono_reflection_get_dynamic_overrides (MonoClass *klass, MonoMethod ***overrides, int *num_overrides, MonoError *error);
void mono_reflection_destroy_dynamic_method (MonoReflectionDynamicMethod *mb);
MonoReflectionModule*
ves_icall_AssemblyBuilder_InternalAddModule (MonoReflectionAssemblyBuilder *ab, MonoString *fileName);
-void
-ves_icall_TypeBuilder_create_generic_class (MonoReflectionTypeBuilder *tb);
-
MonoArray*
ves_icall_CustomAttributeBuilder_GetBlob (MonoReflectionAssembly *assembly, MonoObject *ctor, MonoArray *ctorArgs, MonoArray *properties, MonoArray *propValues, MonoArray *fields, MonoArray* fieldValues);
void
ves_icall_DynamicMethod_create_dynamic_method (MonoReflectionDynamicMethod *mb);
-MonoBoolean
-ves_icall_TypeBuilder_get_IsGenericParameter (MonoReflectionTypeBuilder *tb);
+MonoReflectionType*
+ves_icall_TypeBuilder_create_runtime_class (MonoReflectionTypeBuilder *tb);
void
ves_icall_EnumBuilder_setup_enum_type (MonoReflectionType *enumtype,
void
ves_icall_ModuleBuilder_set_wrappers_type (MonoReflectionModuleBuilder *moduleb, MonoReflectionType *type);
-void
-ves_icall_GenericTypeParameterBuilder_initialize_generic_parameter (MonoReflectionGenericParam *gparam);
-
-MonoReflectionMethod*
-ves_icall_MethodBuilder_MakeGenericMethod (MonoReflectionMethod *rmethod, MonoArray *types);
-
#endif /* __MONO_OBJECT_INTERNALS_H__ */
*pass_size_in_words = FALSE;
- if (mono_class_has_finalizer (vtable->klass) || mono_class_is_marshalbyref (vtable->klass) || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
+ if (mono_class_has_finalizer (vtable->klass) || mono_class_is_marshalbyref (vtable->klass))
return ves_icall_object_new_specific;
if (vtable->gc_descr != MONO_GC_DESCRIPTOR_NULL) {
void
mono_profiler_install_allocation (MonoProfileAllocFunc callback)
{
- mono_gc_enable_alloc_events ();
if (!prof_list)
return;
prof_list->allocation_cb = callback;
void
mono_profiler_install_gc (MonoProfileGCFunc callback, MonoProfileGCResizeFunc heap_resize_callback)
{
- mono_gc_enable_events ();
if (!prof_list)
return;
prof_list->gc_event = callback;
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_object_end = end_obj;
prof_list->gc_finalize_end = end;
}
/* Class lazy loading functions */
static GENERATE_GET_CLASS_WITH_CACHE (mono_assembly, System.Reflection, MonoAssembly)
static GENERATE_GET_CLASS_WITH_CACHE (mono_module, System.Reflection, MonoModule)
-static GENERATE_GET_CLASS_WITH_CACHE (mono_generic_method, System.Reflection, MonoGenericMethod);
-static GENERATE_GET_CLASS_WITH_CACHE (mono_generic_cmethod, System.Reflection, MonoGenericCMethod);
static GENERATE_GET_CLASS_WITH_CACHE (mono_method, System.Reflection, MonoMethod);
static GENERATE_GET_CLASS_WITH_CACHE (mono_cmethod, System.Reflection, MonoCMethod);
static GENERATE_GET_CLASS_WITH_CACHE (mono_field, System.Reflection, MonoField);
if (klass->byval_arg.type == MONO_TYPE_MVAR || klass->byval_arg.type == MONO_TYPE_VAR) {
MonoGenericParam *gparam = klass->byval_arg.data.generic_param;
- if (gparam->owner && gparam->owner->is_method) {
+ if (gparam->owner && gparam->owner->is_method && !gparam->owner->is_anonymous) {
MonoMethod *method = gparam->owner->owner.method;
if (method && mono_class_get_generic_type_definition (method->klass)->wastypebuilder)
is_type_done = FALSE;
mono_error_init (error);
- if (method->is_inflated) {
- MonoReflectionGenericMethod *gret;
-
- if (!refclass)
- refclass = method->klass;
- CHECK_OBJECT (MonoReflectionMethod *, method, refclass);
- if ((*method->name == '.') && (!strcmp (method->name, ".ctor") || !strcmp (method->name, ".cctor"))) {
- klass = mono_class_get_mono_generic_cmethod_class ();
- } else {
- klass = mono_class_get_mono_generic_method_class ();
- }
- gret = (MonoReflectionGenericMethod*)mono_object_new_checked (domain, klass, error);
- if (!mono_error_ok (error))
- goto leave;
- gret->method.method = method;
-
- MONO_OBJECT_SETREF (gret, method.name, mono_string_new (domain, method->name));
-
- rt = mono_type_get_object_checked (domain, &refclass->byval_arg, error);
- if (!mono_error_ok (error))
- goto leave;
-
- MONO_OBJECT_SETREF (gret, method.reftype, rt);
-
- CACHE_OBJECT (MonoReflectionMethod *, method, (MonoReflectionMethod*)gret, refclass);
- }
-
if (!refclass)
refclass = method->klass;
token = mc->type_token;
} else if (strcmp (klass->name, "MonoCMethod") == 0 ||
- strcmp (klass->name, "MonoMethod") == 0 ||
- strcmp (klass->name, "MonoGenericMethod") == 0 ||
- strcmp (klass->name, "MonoGenericCMethod") == 0) {
+ strcmp (klass->name, "MonoMethod") == 0) {
MonoReflectionMethod *m = (MonoReflectionMethod *)obj;
if (m->method->is_inflated) {
MonoMethodInflated *inflated = (MonoMethodInflated *) m->method;
mono_reflection_bind_generic_parameters (MonoReflectionType *type, int type_argc, MonoType **types, MonoError *error)
{
MonoClass *klass;
- MonoReflectionTypeBuilder *tb = NULL;
gboolean is_dynamic = FALSE;
MonoClass *geninst;
mono_loader_lock ();
if (mono_is_sre_type_builder (mono_object_class (type))) {
- tb = (MonoReflectionTypeBuilder *) type;
-
is_dynamic = TRUE;
} else if (mono_is_sre_generic_instance (mono_object_class (type))) {
MonoReflectionGenericClass *rgi = (MonoReflectionGenericClass *) type;
MonoReflectionType *gtd = rgi->generic_type;
- if (mono_is_sre_type_builder (mono_object_class (gtd))) {
- tb = (MonoReflectionTypeBuilder *)gtd;
+ if (mono_is_sre_type_builder (mono_object_class (gtd)))
is_dynamic = TRUE;
- }
- }
-
- /* FIXME: fix the CreateGenericParameters protocol to avoid the two stage setup of TypeBuilders */
- if (tb && tb->generic_container) {
- if (!mono_reflection_create_generic_class (tb, error)) {
- mono_loader_unlock ();
- return NULL;
- }
}
MonoType *t = mono_reflection_type_get_handle (type, error);
return NULL;
}
- if (klass->wastypebuilder) {
- tb = (MonoReflectionTypeBuilder *) mono_class_get_ref_info (klass);
-
+ if (klass->wastypebuilder)
is_dynamic = TRUE;
- }
mono_loader_unlock ();
mono_error_init (error);
- /*FIXME but this no longer should happen*/
- if (!strcmp (rmethod->object.vtable->klass->name, "MethodBuilder")) {
- method = mono_reflection_method_builder_to_mono_method ((MonoReflectionMethodBuilder*)rmethod, error);
- return_val_if_nok (error, NULL);
- } else {
- method = rmethod->method;
- }
+ g_assert (strcmp (rmethod->object.vtable->klass->name, "MethodBuilder"));
+
+ method = rmethod->method;
klass = method->klass;
return NULL;
}
- MonoReflectionMethod *ret = mono_method_get_object_checked (mono_object_domain (rmethod), inflated, NULL, error);
- return ret;
-}
-
-MonoReflectionMethod*
-ves_icall_MethodBuilder_MakeGenericMethod (MonoReflectionMethod *rmethod, MonoArray *types)
-{
- MonoError error;
- MonoReflectionMethod *result = reflection_bind_generic_method_parameters (rmethod, types, &error);
- mono_error_set_pending_exception (&error);
- return result;
+ return mono_method_get_object_checked (mono_object_domain (rmethod), inflated, NULL, error);
}
MonoReflectionMethod*
enum {
INTERNAL_MEM_EPHEMERON_LINK = INTERNAL_MEM_FIRST_CLIENT,
+ INTERNAL_MEM_MOVED_OBJECT,
INTERNAL_MEM_MAX
};
#include "sgen/sgen-client.h"
#include "sgen/sgen-cardtable.h"
#include "sgen/sgen-pinning.h"
+#include "sgen/sgen-thread-pool.h"
#include "metadata/marshal.h"
#include "metadata/method-builder.h"
#include "metadata/abi-details.h"
* Allocation
*/
-static gboolean alloc_events = FALSE;
-
-void
-mono_gc_enable_alloc_events (void)
-{
- alloc_events = TRUE;
-}
-
void*
mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
{
MonoObject *obj = sgen_alloc_obj (vtable, size);
- if (G_UNLIKELY (alloc_events)) {
+ if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS)) {
if (obj)
mono_profiler_allocation (obj);
}
{
MonoObject *obj = sgen_alloc_obj_pinned (vtable, size);
- if (G_UNLIKELY (alloc_events)) {
+ if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS)) {
if (obj)
mono_profiler_allocation (obj);
}
{
MonoObject *obj = sgen_alloc_obj_mature (vtable, size);
- if (G_UNLIKELY (alloc_events)) {
+ if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS)) {
if (obj)
mono_profiler_allocation (obj);
}
UNLOCK_GC;
done:
- if (G_UNLIKELY (alloc_events))
+ if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
mono_profiler_allocation (&arr->obj);
SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size (vtable, (GCObject*)arr)), "Vector has incorrect size.");
UNLOCK_GC;
done:
- if (G_UNLIKELY (alloc_events))
+ if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
mono_profiler_allocation (&arr->obj);
SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size (vtable, (GCObject*)arr)), "Array has incorrect size.");
UNLOCK_GC;
done:
- if (G_UNLIKELY (alloc_events))
+ if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
mono_profiler_allocation (&str->object);
return str;
static void *moved_objects [MOVED_OBJECTS_NUM];
static int moved_objects_idx = 0;
+static SgenPointerQueue moved_objects_queue = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_MOVED_OBJECT);
+
void
mono_sgen_register_moved_object (void *obj, void *destination)
{
- g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
+ /*
+ * This function can be called from SGen's worker threads. We want to try
+ * and avoid exposing those threads to the profiler API, so queue up move
+ * events and send them later when the main GC thread calls
+ * mono_sgen_gc_event_moves ().
+ *
+ * TODO: Once SGen has multiple worker threads, we need to switch to a
+ * lock-free data structure for the queue as multiple threads will be
+ * adding to it at the same time.
+ */
+ if (sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ())) {
+ sgen_pointer_queue_add (&moved_objects_queue, obj);
+ sgen_pointer_queue_add (&moved_objects_queue, destination);
+ } else {
+ if (moved_objects_idx == MOVED_OBJECTS_NUM) {
+ mono_profiler_gc_moves (moved_objects, moved_objects_idx);
+ moved_objects_idx = 0;
+ }
- if (moved_objects_idx == MOVED_OBJECTS_NUM) {
- mono_profiler_gc_moves (moved_objects, moved_objects_idx);
- moved_objects_idx = 0;
+ moved_objects [moved_objects_idx++] = obj;
+ moved_objects [moved_objects_idx++] = destination;
}
- moved_objects [moved_objects_idx++] = obj;
- moved_objects [moved_objects_idx++] = destination;
}
void
mono_sgen_gc_event_moves (void)
{
+ while (!sgen_pointer_queue_is_empty (&moved_objects_queue)) {
+ void *dst = sgen_pointer_queue_pop (&moved_objects_queue);
+ void *src = sgen_pointer_queue_pop (&moved_objects_queue);
+
+ mono_sgen_register_moved_object (src, dst);
+ }
+
if (moved_objects_idx) {
mono_profiler_gc_moves (moved_objects, moved_objects_idx);
moved_objects_idx = 0;
return 1;
}
-void
-mono_gc_enable_events (void)
-{
-}
-
const char *
mono_gc_get_gc_name (void)
{
{
switch (type) {
case INTERNAL_MEM_EPHEMERON_LINK: return "ephemeron-link";
+ case INTERNAL_MEM_MOVED_OBJECT: return "moved-object";
default:
return NULL;
}
mono_gc_base_cleanup (void)
{
sgen_thread_pool_shutdown ();
+
+ // We should have consumed any outstanding moves.
+ g_assert (sgen_pointer_queue_is_empty (&moved_objects_queue));
}
gboolean
return idx;
}
-
guint32
mono_dynimage_encode_field_signature (MonoDynamicImage *assembly, MonoReflectionFieldBuilder *fb, MonoError *error)
{
MonoType *type;
MonoClass *klass;
- mono_reflection_init_type_builder_generics (fb->type, error);
- return_val_if_nok (error, 0);
-
type = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, error);
return_val_if_nok (error, 0);
klass = mono_class_from_mono_type (type);
return mono_dynimage_encode_typedef_or_ref_full (assembly, type, TRUE);
}
-guint32
-mono_dynimage_encode_generic_method_definition_sig (MonoDynamicImage *assembly, MonoReflectionMethodBuilder *mb)
-{
- SigBuffer buf;
- int i;
- guint32 nparams = mono_array_length (mb->generic_params);
- guint32 idx;
-
- if (!assembly->save)
- return 0;
-
- sigbuffer_init (&buf, 32);
-
- sigbuffer_add_value (&buf, 0xa);
- sigbuffer_add_value (&buf, nparams);
-
- for (i = 0; i < nparams; i++) {
- sigbuffer_add_value (&buf, MONO_TYPE_MVAR);
- sigbuffer_add_value (&buf, i);
- }
-
- idx = sigbuffer_add_to_blob_cached (assembly, &buf);
- sigbuffer_free (&buf);
- return idx;
-}
-
guint32
mono_dynimage_encode_generic_method_sig (MonoDynamicImage *assembly, MonoGenericContext *context)
{
return idx;
}
-#ifndef DISABLE_REFLECTION_EMIT
-guint32
-mono_dynimage_encode_generic_typespec (MonoDynamicImage *assembly, MonoReflectionTypeBuilder *tb, MonoError *error)
-{
- MonoDynamicTable *table;
- MonoClass *klass;
- MonoType *type;
- guint32 *values;
- guint32 token;
- SigBuffer buf;
- int count, i;
-
- /*
- * We're creating a TypeSpec for the TypeBuilder of a generic type declaration,
- * ie. what we'd normally use as the generic type in a TypeSpec signature.
- * Because of this, we must not insert it into the `typeref' hash table.
- */
- type = mono_reflection_type_get_handle ((MonoReflectionType*)tb, error);
- return_val_if_nok (error, 0);
- token = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->typespec, type));
- if (token)
- return token;
-
- sigbuffer_init (&buf, 32);
-
- g_assert (tb->generic_params);
- klass = mono_class_from_mono_type (type);
-
- if (tb->generic_container) {
- if (!mono_reflection_create_generic_class (tb, error))
- goto fail;
- }
-
- sigbuffer_add_value (&buf, MONO_TYPE_GENERICINST);
- g_assert (klass->generic_container);
- sigbuffer_add_value (&buf, klass->byval_arg.type);
- sigbuffer_add_value (&buf, mono_dynimage_encode_typedef_or_ref_full (assembly, &klass->byval_arg, FALSE));
-
- count = mono_array_length (tb->generic_params);
- sigbuffer_add_value (&buf, count);
- for (i = 0; i < count; i++) {
- MonoReflectionGenericParam *gparam;
-
- gparam = mono_array_get (tb->generic_params, MonoReflectionGenericParam *, i);
- MonoType *gparam_type = mono_reflection_type_get_handle ((MonoReflectionType*)gparam, error);
- if (!is_ok (error))
- goto fail;
-
- encode_type (assembly, gparam_type, &buf);
- }
-
- table = &assembly->tables [MONO_TABLE_TYPESPEC];
-
- if (assembly->save) {
- token = sigbuffer_add_to_blob_cached (assembly, &buf);
- alloc_table (table, table->rows + 1);
- values = table->values + table->next_idx * MONO_TYPESPEC_SIZE;
- values [MONO_TYPESPEC_SIGNATURE] = token;
- }
- sigbuffer_free (&buf);
-
- token = MONO_TYPEDEFORREF_TYPESPEC | (table->next_idx << MONO_TYPEDEFORREF_BITS);
- g_hash_table_insert (assembly->typespec, type, GUINT_TO_POINTER(token));
- table->next_idx ++;
- return token;
-fail:
- sigbuffer_free (&buf);
- return 0;
-}
-#else /*DISABLE_REFLECTION_EMIT*/
-guint32
-mono_dynimage_encode_generic_typespec (MonoDynamicImage *assembly, MonoReflectionTypeBuilder *tb, MonoError *error)
-{
- g_assert_not_reached ();
- return 0;
-}
-#endif /*DISABLE_REFLECTION_EMIT*/
-
#ifndef DISABLE_REFLECTION_EMIT
guint32
mono_dynimage_encode_reflection_sighelper (MonoDynamicImage *assembly, MonoReflectionSigHelper *helper, MonoError *error)
gboolean
mono_is_sr_mono_property (MonoClass *klass);
-gboolean
-mono_reflection_create_generic_class (MonoReflectionTypeBuilder *tb, MonoError *error);
-
-MonoMethod*
-mono_reflection_method_builder_to_mono_method (MonoReflectionMethodBuilder *mb, MonoError *error);
-
-MonoMethod*
-mono_reflection_method_on_tb_inst_get_handle (MonoReflectionMethodOnTypeBuilderInst *m, MonoError *error);
-
gpointer
mono_reflection_resolve_object (MonoImage *image, MonoObject *obj, MonoClass **handle_class, MonoGenericContext *context, MonoError *error);
gboolean
mono_reflection_methodbuilder_from_ctor_builder (ReflectionMethodBuilder *rmb, MonoReflectionCtorBuilder *mb,
MonoError *error);
-
-void
-mono_reflection_init_type_builder_generics (MonoObject *type, MonoError *error);
guint32
mono_reflection_resolution_scope_from_image (MonoDynamicImage *assembly, MonoImage *image);
mono_dynimage_encode_method_builder_signature (MonoDynamicImage *assembly, ReflectionMethodBuilder *mb,
MonoError *error);
-guint32
-mono_dynimage_encode_generic_method_definition_sig (MonoDynamicImage *assembly, MonoReflectionMethodBuilder *mb);
-
guint32
mono_dynimage_encode_generic_method_sig (MonoDynamicImage *assembly, MonoGenericContext *context);
-guint32
-mono_dynimage_encode_generic_typespec (MonoDynamicImage *assembly, MonoReflectionTypeBuilder *tb, MonoError *error);
-
guint32
mono_dynimage_encode_typedef_or_ref_full (MonoDynamicImage *assembly, MonoType *type, gboolean try_typespec);
values [MONO_FIELD_SIGNATURE] = mono_dynimage_encode_field_signature (assembly, fb, error);
return_if_nok (error);
-
if (fb->offset != -1) {
table = &assembly->tables [MONO_TABLE_FIELDLAYOUT];
table->rows ++;
{
guint32 code_idx = GPOINTER_TO_UINT (value);
MonoReflectionILTokenInfo *iltoken;
- MonoReflectionFieldBuilder *field;
- MonoReflectionCtorBuilder *ctor;
- MonoReflectionMethodBuilder *method;
MonoReflectionTypeBuilder *tb;
MonoReflectionArrayMethod *am;
guint32 i, idx = 0;
switch (target [3]) {
case MONO_TABLE_FIELD:
if (!strcmp (iltoken->member->vtable->klass->name, "FieldBuilder")) {
- field = (MonoReflectionFieldBuilder *)iltoken->member;
- idx = field->table_idx;
+ g_assert_not_reached ();
} else if (!strcmp (iltoken->member->vtable->klass->name, "MonoField")) {
MonoClassField *f = ((MonoReflectionField*)iltoken->member)->field;
idx = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->field_to_table_idx, f));
break;
case MONO_TABLE_METHOD:
if (!strcmp (iltoken->member->vtable->klass->name, "MethodBuilder")) {
- method = (MonoReflectionMethodBuilder *)iltoken->member;
- idx = method->table_idx;
+ g_assert_not_reached ();
} else if (!strcmp (iltoken->member->vtable->klass->name, "ConstructorBuilder")) {
- ctor = (MonoReflectionCtorBuilder *)iltoken->member;
- idx = ctor->table_idx;
+ g_assert_not_reached ();
} else if (!strcmp (iltoken->member->vtable->klass->name, "MonoMethod") ||
!strcmp (iltoken->member->vtable->klass->name, "MonoCMethod")) {
MonoMethod *m = ((MonoReflectionMethod*)iltoken->member)->method;
}
break;
case MONO_TABLE_TYPEDEF:
- if (strcmp (iltoken->member->vtable->klass->name, "TypeBuilder"))
+ if (!strcmp (iltoken->member->vtable->klass->name, "TypeBuilder")) {
+ g_assert_not_reached ();
+ } else if (!strcmp (iltoken->member->vtable->klass->name, "RuntimeType")) {
+ MonoClass *k = mono_class_from_mono_type (((MonoReflectionType*)iltoken->member)->type);
+ MonoObject *obj = mono_class_get_ref_info (k);
+ g_assert (obj);
+ g_assert (!strcmp (obj->vtable->klass->name, "TypeBuilder"));
+ tb = (MonoReflectionTypeBuilder*)obj;
+ idx = tb->table_idx;
+ } else {
g_assert_not_reached ();
- tb = (MonoReflectionTypeBuilder *)iltoken->member;
- idx = tb->table_idx;
+ }
break;
case MONO_TABLE_MEMBERREF:
if (!strcmp (iltoken->member->vtable->klass->name, "MonoArrayMethod")) {
am = (MonoReflectionArrayMethod*)iltoken->member;
idx = am->table_idx;
} else if (!strcmp (iltoken->member->vtable->klass->name, "MonoMethod") ||
- !strcmp (iltoken->member->vtable->klass->name, "MonoCMethod") ||
- !strcmp (iltoken->member->vtable->klass->name, "MonoGenericMethod") ||
- !strcmp (iltoken->member->vtable->klass->name, "MonoGenericCMethod")) {
+ !strcmp (iltoken->member->vtable->klass->name, "MonoCMethod")) {
MonoMethod *m = ((MonoReflectionMethod*)iltoken->member)->method;
g_assert (m->klass->generic_class || m->klass->generic_container);
continue;
} else if (!strcmp (iltoken->member->vtable->klass->name, "FieldBuilder")) {
+ g_assert_not_reached ();
continue;
} else if (!strcmp (iltoken->member->vtable->klass->name, "MonoField")) {
continue;
} else if (!strcmp (iltoken->member->vtable->klass->name, "MethodBuilder") ||
!strcmp (iltoken->member->vtable->klass->name, "ConstructorBuilder")) {
+ g_assert_not_reached ();
continue;
} else if (!strcmp (iltoken->member->vtable->klass->name, "FieldOnTypeBuilderInst")) {
+ g_assert_not_reached ();
continue;
} else if (!strcmp (iltoken->member->vtable->klass->name, "MethodOnTypeBuilderInst")) {
+ g_assert_not_reached ();
continue;
} else if (!strcmp (iltoken->member->vtable->klass->name, "ConstructorOnTypeBuilderInst")) {
+ g_assert_not_reached ();
continue;
} else {
g_assert_not_reached ();
}
break;
case MONO_TABLE_METHODSPEC:
- if (!strcmp (iltoken->member->vtable->klass->name, "MonoGenericMethod")) {
+ if (!strcmp (iltoken->member->vtable->klass->name, "MonoMethod")) {
MonoMethod *m = ((MonoReflectionMethod*)iltoken->member)->method;
g_assert (mono_method_signature (m)->generic_param_count);
continue;
} else if (!strcmp (iltoken->member->vtable->klass->name, "MethodBuilder")) {
+ g_assert_not_reached ();
continue;
} else if (!strcmp (iltoken->member->vtable->klass->name, "MethodOnTypeBuilderInst")) {
+ g_assert_not_reached ();
+ continue;
+ } else {
+ g_assert_not_reached ();
+ }
+ break;
+ case MONO_TABLE_TYPESPEC:
+ if (!strcmp (iltoken->member->vtable->klass->name, "RuntimeType")) {
continue;
} else {
g_assert_not_reached ();
#include "mono/utils/checked-build.h"
#include "mono/utils/mono-digest.h"
-void
-mono_sre_generic_param_table_entry_free (GenericParamTableEntry *entry)
-{
- mono_gc_deregister_root ((char*) &entry->gparam);
- g_free (entry);
-}
-
static GENERATE_GET_CLASS_WITH_CACHE (marshal_as_attribute, System.Runtime.InteropServices, MarshalAsAttribute);
+static GENERATE_GET_CLASS_WITH_CACHE (module_builder, System.Reflection.Emit, ModuleBuilder);
#ifndef DISABLE_REFLECTION_EMIT
static guint32 mono_image_get_methodref_token (MonoDynamicImage *assembly, MonoMethod *method, gboolean create_typespec);
-static guint32 mono_image_get_methodbuilder_token (MonoDynamicImage *assembly, MonoReflectionMethodBuilder *mb, gboolean create_open_instance, MonoError *error);
-static guint32 mono_image_get_ctorbuilder_token (MonoDynamicImage *assembly, MonoReflectionCtorBuilder *cb, MonoError *error);
static guint32 mono_image_get_sighelper_token (MonoDynamicImage *assembly, MonoReflectionSigHelper *helper, MonoError *error);
static gboolean ensure_runtime_vtable (MonoClass *klass, MonoError *error);
-static guint32 mono_image_get_methodref_token_for_methodbuilder (MonoDynamicImage *assembly, MonoReflectionMethodBuilder *method, MonoError *error);
static void reflection_methodbuilder_from_dynamic_method (ReflectionMethodBuilder *rmb, MonoReflectionDynamicMethod *mb);
+static gboolean reflection_setup_internal_class (MonoReflectionTypeBuilder *tb, MonoError *error);
static gpointer register_assembly (MonoDomain *domain, MonoReflectionAssembly *res, MonoAssembly *assembly);
#endif
static gboolean is_sre_array (MonoClass *klass);
static gboolean is_sre_byref (MonoClass *klass);
static gboolean is_sre_pointer (MonoClass *klass);
+static gboolean is_sre_generic_instance (MonoClass *klass);
static gboolean is_sre_type_builder (MonoClass *klass);
static gboolean is_sre_method_builder (MonoClass *klass);
static gboolean is_sre_field_builder (MonoClass *klass);
+static gboolean is_sre_gparam_builder (MonoClass *klass);
+static gboolean is_sre_enum_builder (MonoClass *klass);
static gboolean is_sr_mono_method (MonoClass *klass);
-static gboolean is_sr_mono_generic_method (MonoClass *klass);
-static gboolean is_sr_mono_generic_cmethod (MonoClass *klass);
static gboolean is_sr_mono_field (MonoClass *klass);
static guint32 mono_image_get_methodspec_token (MonoDynamicImage *assembly, MonoMethod *method);
static guint32 mono_image_get_inflated_method_token (MonoDynamicImage *assembly, MonoMethod *m);
-static MonoMethod * inflate_method (MonoReflectionType *type, MonoObject *obj, MonoError *error);
#define mono_type_array_get_and_resolve(array, index, error) mono_reflection_type_get_handle ((MonoReflectionType*)mono_array_get (array, gpointer, index), error)
}
#endif
-
guint32
mono_reflection_resolution_scope_from_image (MonoDynamicImage *assembly, MonoImage *image)
{
return token;
}
-static guint32
-mono_image_get_methodref_token_for_methodbuilder (MonoDynamicImage *assembly, MonoReflectionMethodBuilder *method, MonoError *error)
-{
- guint32 token, parent, sig;
- ReflectionMethodBuilder rmb;
- MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)method->type;
-
- mono_error_init (error);
- token = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->handleref, method));
- if (token)
- return token;
-
- if (!mono_reflection_methodbuilder_from_method_builder (&rmb, method, error))
- return 0;
-
- /*
- * A methodref signature can't contain an unmanaged calling convention.
- * Since some flags are encoded as part of call_conv, we need to check against it.
- */
- if ((rmb.call_conv & ~0x60) != MONO_CALL_DEFAULT && (rmb.call_conv & ~0x60) != MONO_CALL_VARARG)
- rmb.call_conv = (rmb.call_conv & 0x60) | MONO_CALL_DEFAULT;
-
- sig = mono_dynimage_encode_method_builder_signature (assembly, &rmb, error);
- return_val_if_nok (error, 0);
-
- if (tb->generic_params) {
- parent = mono_dynimage_encode_generic_typespec (assembly, tb, error);
- return_val_if_nok (error, 0);
- } else {
- MonoType *t = mono_reflection_type_get_handle ((MonoReflectionType*)rmb.type, error);
- return_val_if_nok (error, 0);
-
- parent = mono_image_typedef_or_ref (assembly, t);
- }
-
- char *name = mono_string_to_utf8_checked (method->name, error);
- return_val_if_nok (error, 0);
-
- token = mono_image_add_memberef_row (assembly, parent, name, sig);
- g_free (name);
-
- g_hash_table_insert (assembly->handleref, method, GUINT_TO_POINTER(token));
-
- return token;
-}
-
static guint32
mono_image_get_varargs_method_token (MonoDynamicImage *assembly, guint32 original,
const gchar *name, guint32 sig)
return token;
}
-static guint32
-mono_image_get_methodspec_token_for_generic_method_definition (MonoDynamicImage *assembly, MonoReflectionMethodBuilder *mb, MonoError *error)
-{
- MonoDynamicTable *table;
- guint32 *values;
- guint32 token, mtoken = 0;
-
- mono_error_init (error);
- token = GPOINTER_TO_UINT (mono_g_hash_table_lookup (assembly->methodspec, mb));
- if (token)
- return token;
-
- table = &assembly->tables [MONO_TABLE_METHODSPEC];
-
- mtoken = mono_image_get_methodref_token_for_methodbuilder (assembly, mb, error);
- if (!mono_error_ok (error))
- return 0;
-
- switch (mono_metadata_token_table (mtoken)) {
- case MONO_TABLE_MEMBERREF:
- mtoken = (mono_metadata_token_index (mtoken) << MONO_METHODDEFORREF_BITS) | MONO_METHODDEFORREF_METHODREF;
- break;
- case MONO_TABLE_METHOD:
- mtoken = (mono_metadata_token_index (mtoken) << MONO_METHODDEFORREF_BITS) | MONO_METHODDEFORREF_METHODDEF;
- break;
- default:
- g_assert_not_reached ();
- }
-
- if (assembly->save) {
- alloc_table (table, table->rows + 1);
- values = table->values + table->next_idx * MONO_METHODSPEC_SIZE;
- values [MONO_METHODSPEC_METHOD] = mtoken;
- values [MONO_METHODSPEC_SIGNATURE] = mono_dynimage_encode_generic_method_definition_sig (assembly, mb);
- }
-
- token = MONO_TOKEN_METHOD_SPEC | table->next_idx;
- table->next_idx ++;
-
- mono_g_hash_table_insert (assembly->methodspec, mb, GUINT_TO_POINTER(token));
- return token;
-}
-
-static guint32
-mono_image_get_methodbuilder_token (MonoDynamicImage *assembly, MonoReflectionMethodBuilder *mb, gboolean create_methodspec, MonoError *error)
-{
- guint32 token;
-
- mono_error_init (error);
-
- if (mb->generic_params && create_methodspec)
- return mono_image_get_methodspec_token_for_generic_method_definition (assembly, mb, error);
-
- token = GPOINTER_TO_UINT (mono_g_hash_table_lookup (assembly->handleref_managed, mb));
- if (token)
- return token;
-
- token = mono_image_get_methodref_token_for_methodbuilder (assembly, mb, error);
- if (!mono_error_ok (error))
- return 0;
- mono_g_hash_table_insert (assembly->handleref_managed, mb, GUINT_TO_POINTER(token));
- return token;
-}
-
-static guint32
-mono_image_get_ctorbuilder_token (MonoDynamicImage *assembly, MonoReflectionCtorBuilder *mb, MonoError *error)
-{
- guint32 token, parent, sig;
- ReflectionMethodBuilder rmb;
- char *name;
- MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mb->type;
-
- mono_error_init (error);
-
- token = GPOINTER_TO_UINT (mono_g_hash_table_lookup (assembly->handleref_managed, mb));
- if (token)
- return token;
-
- if (!mono_reflection_methodbuilder_from_ctor_builder (&rmb, mb, error))
- return 0;
-
- if (tb->generic_params) {
- parent = mono_dynimage_encode_generic_typespec (assembly, tb, error);
- return_val_if_nok (error, 0);
- } else {
- MonoType * type = mono_reflection_type_get_handle ((MonoReflectionType*)tb, error);
- return_val_if_nok (error, 0);
- parent = mono_image_typedef_or_ref (assembly, type);
- }
-
- name = mono_string_to_utf8_checked (rmb.name, error);
- return_val_if_nok (error, 0);
- sig = mono_dynimage_encode_method_builder_signature (assembly, &rmb, error);
- return_val_if_nok (error, 0);
-
- token = mono_image_add_memberef_row (assembly, parent, name, sig);
-
- g_free (name);
- mono_g_hash_table_insert (assembly->handleref_managed, mb, GUINT_TO_POINTER(token));
- return token;
-}
#endif
static gboolean
return token;
}
-static guint32
-mono_image_get_field_on_inst_token (MonoDynamicImage *assembly, MonoReflectionFieldOnTypeBuilderInst *f, MonoError *error)
-{
- guint32 token;
- MonoClass *klass;
- MonoGenericClass *gclass;
- MonoType *type;
- char *name;
-
- token = GPOINTER_TO_UINT (mono_g_hash_table_lookup (assembly->handleref_managed, f));
- if (token)
- return token;
- if (is_sre_field_builder (mono_object_class (f->fb))) {
- MonoReflectionFieldBuilder *fb = (MonoReflectionFieldBuilder *)f->fb;
- type = mono_reflection_type_get_handle ((MonoReflectionType*)f->inst, error);
- return_val_if_nok (error, 0);
- klass = mono_class_from_mono_type (type);
- gclass = type->data.generic_class;
- g_assert (gclass->is_dynamic);
-
- guint32 sig_token = mono_dynimage_encode_field_signature (assembly, fb, error);
- return_val_if_nok (error, 0);
- name = mono_string_to_utf8_checked (fb->name, error);
- return_val_if_nok (error, 0);
- token = mono_image_get_memberref_token (assembly, &klass->byval_arg, name, sig_token);
- g_free (name);
- } else if (is_sr_mono_field (mono_object_class (f->fb))) {
- guint32 sig;
- MonoClassField *field = ((MonoReflectionField *)f->fb)->field;
-
- type = mono_reflection_type_get_handle ((MonoReflectionType*)f->inst, error);
- return_val_if_nok (error, 0);
- klass = mono_class_from_mono_type (type);
-
- sig = mono_dynimage_encode_fieldref_signature (assembly, field->parent->image, field->type);
- token = mono_image_get_memberref_token (assembly, &klass->byval_arg, field->name, sig);
- } else {
- char *name = mono_type_get_full_name (mono_object_class (f->fb));
- g_error ("mono_image_get_field_on_inst_token: don't know how to handle %s", name);
- }
-
- mono_g_hash_table_insert (assembly->handleref_managed, f, GUINT_TO_POINTER (token));
- return token;
-}
-
-static guint32
-mono_image_get_ctor_on_inst_token (MonoDynamicImage *assembly, MonoReflectionCtorOnTypeBuilderInst *c, gboolean create_methodspec, MonoError *error)
-{
- guint32 sig, token;
- MonoClass *klass;
- MonoGenericClass *gclass;
- MonoType *type;
-
- mono_error_init (error);
-
- /* A ctor cannot be a generic method, so we can ignore create_methodspec */
-
- token = GPOINTER_TO_UINT (mono_g_hash_table_lookup (assembly->handleref_managed, c));
- if (token)
- return token;
-
- if (mono_is_sre_ctor_builder (mono_object_class (c->cb))) {
- MonoReflectionCtorBuilder *cb = (MonoReflectionCtorBuilder *)c->cb;
- ReflectionMethodBuilder rmb;
- char *name;
-
- type = mono_reflection_type_get_handle ((MonoReflectionType*)c->inst, error);
- return_val_if_nok (error, 0);
- klass = mono_class_from_mono_type (type);
-
- gclass = type->data.generic_class;
- g_assert (gclass->is_dynamic);
-
- if (!mono_reflection_methodbuilder_from_ctor_builder (&rmb, cb, error))
- return 0;
-
- sig = mono_dynimage_encode_method_builder_signature (assembly, &rmb, error);
- return_val_if_nok (error, 0);
-
- name = mono_string_to_utf8_checked (rmb.name, error);
- return_val_if_nok (error, 0);
-
- token = mono_image_get_memberref_token (assembly, &klass->byval_arg, name, sig);
- g_free (name);
- } else if (mono_is_sr_mono_cmethod (mono_object_class (c->cb))) {
- MonoMethod *mm = ((MonoReflectionMethod *)c->cb)->method;
-
- type = mono_reflection_type_get_handle ((MonoReflectionType*)c->inst, error);
- return_val_if_nok (error, 0);
- klass = mono_class_from_mono_type (type);
-
- sig = mono_dynimage_encode_method_signature (assembly, mono_method_signature (mm));
- token = mono_image_get_memberref_token (assembly, &klass->byval_arg, mm->name, sig);
- } else {
- char *name = mono_type_get_full_name (mono_object_class (c->cb));
- g_error ("mono_image_get_method_on_inst_token: don't know how to handle %s", name);
- }
-
-
- mono_g_hash_table_insert (assembly->handleref_managed, c, GUINT_TO_POINTER (token));
- return token;
-}
-
-MonoMethod*
-mono_reflection_method_on_tb_inst_get_handle (MonoReflectionMethodOnTypeBuilderInst *m, MonoError *error)
-{
- MonoClass *klass;
- MonoGenericContext tmp_context;
- MonoType **type_argv;
- MonoGenericInst *ginst;
- MonoMethod *method, *inflated;
- int count, i;
-
- mono_error_init (error);
-
- mono_reflection_init_type_builder_generics ((MonoObject*)m->inst, error);
- return_val_if_nok (error, NULL);
-
- method = inflate_method (m->inst, (MonoObject*)m->mb, error);
- return_val_if_nok (error, NULL);
-
- klass = method->klass;
-
- if (m->method_args == NULL)
- return method;
-
- if (method->is_inflated)
- method = ((MonoMethodInflated *) method)->declaring;
-
- count = mono_array_length (m->method_args);
-
- type_argv = g_new0 (MonoType *, count);
- for (i = 0; i < count; i++) {
- MonoReflectionType *garg = (MonoReflectionType *)mono_array_get (m->method_args, gpointer, i);
- type_argv [i] = mono_reflection_type_get_handle (garg, error);
- return_val_if_nok (error, NULL);
- }
- ginst = mono_metadata_get_generic_inst (count, type_argv);
- g_free (type_argv);
-
- tmp_context.class_inst = klass->generic_class ? klass->generic_class->context.class_inst : NULL;
- tmp_context.method_inst = ginst;
-
- inflated = mono_class_inflate_generic_method_checked (method, &tmp_context, error);
- mono_error_assert_ok (error);
- return inflated;
-}
-
-static guint32
-mono_image_get_method_on_inst_token (MonoDynamicImage *assembly, MonoReflectionMethodOnTypeBuilderInst *m, gboolean create_methodspec, MonoError *error)
-{
- guint32 sig, token = 0;
- MonoType *type;
- MonoClass *klass;
-
- mono_error_init (error);
-
- if (m->method_args) {
- MonoMethod *inflated;
-
- inflated = mono_reflection_method_on_tb_inst_get_handle (m, error);
- return_val_if_nok (error, 0);
-
- if (create_methodspec)
- token = mono_image_get_methodspec_token (assembly, inflated);
- else
- token = mono_image_get_inflated_method_token (assembly, inflated);
- return token;
- }
-
- token = GPOINTER_TO_UINT (mono_g_hash_table_lookup (assembly->handleref_managed, m));
- if (token)
- return token;
-
- if (is_sre_method_builder (mono_object_class (m->mb))) {
- MonoReflectionMethodBuilder *mb = (MonoReflectionMethodBuilder *)m->mb;
- MonoGenericClass *gclass;
- ReflectionMethodBuilder rmb;
- char *name;
-
- type = mono_reflection_type_get_handle ((MonoReflectionType*)m->inst, error);
- return_val_if_nok (error, 0);
- klass = mono_class_from_mono_type (type);
- gclass = type->data.generic_class;
- g_assert (gclass->is_dynamic);
-
- if (!mono_reflection_methodbuilder_from_method_builder (&rmb, mb, error))
- return 0;
-
- sig = mono_dynimage_encode_method_builder_signature (assembly, &rmb, error);
- return_val_if_nok (error, 0);
-
- name = mono_string_to_utf8_checked (rmb.name, error);
- return_val_if_nok (error, 0);
-
- token = mono_image_get_memberref_token (assembly, &klass->byval_arg, name, sig);
- g_free (name);
- } else if (is_sr_mono_method (mono_object_class (m->mb))) {
- MonoMethod *mm = ((MonoReflectionMethod *)m->mb)->method;
-
- type = mono_reflection_type_get_handle ((MonoReflectionType*)m->inst, error);
- return_val_if_nok (error, 0);
- klass = mono_class_from_mono_type (type);
-
- sig = mono_dynimage_encode_method_signature (assembly, mono_method_signature (mm));
- token = mono_image_get_memberref_token (assembly, &klass->byval_arg, mm->name, sig);
- } else {
- char *name = mono_type_get_full_name (mono_object_class (m->mb));
- g_error ("mono_image_get_method_on_inst_token: don't know how to handle %s", name);
- }
-
- mono_g_hash_table_insert (assembly->handleref_managed, m, GUINT_TO_POINTER (token));
- return token;
-}
-
static guint32
method_encode_methodspec (MonoDynamicImage *assembly, MonoMethod *method)
{
return token;
}
-/*
- * Return a copy of TYPE, adding the custom modifiers in MODREQ and MODOPT.
- */
-static MonoType*
-add_custom_modifiers (MonoDynamicImage *assembly, MonoType *type, MonoArray *modreq, MonoArray *modopt, MonoError *error)
-{
- int i, count, len, pos;
- MonoType *t;
-
- mono_error_init (error);
-
- count = 0;
- if (modreq)
- count += mono_array_length (modreq);
- if (modopt)
- count += mono_array_length (modopt);
-
- if (count == 0)
- return mono_metadata_type_dup (NULL, type);
-
- len = MONO_SIZEOF_TYPE + ((gint32)count) * sizeof (MonoCustomMod);
- t = (MonoType *)g_malloc (len);
- memcpy (t, type, MONO_SIZEOF_TYPE);
-
- t->num_mods = count;
- pos = 0;
- if (modreq) {
- for (i = 0; i < mono_array_length (modreq); ++i) {
- MonoType *mod = mono_type_array_get_and_resolve (modreq, i, error);
- if (!is_ok (error))
- goto fail;
- t->modifiers [pos].required = 1;
- t->modifiers [pos].token = mono_image_typedef_or_ref (assembly, mod);
- pos ++;
- }
- }
- if (modopt) {
- for (i = 0; i < mono_array_length (modopt); ++i) {
- MonoType *mod = mono_type_array_get_and_resolve (modopt, i, error);
- if (!is_ok (error))
- goto fail;
- t->modifiers [pos].required = 0;
- t->modifiers [pos].token = mono_image_typedef_or_ref (assembly, mod);
- pos ++;
- }
- }
-
- return t;
-fail:
- g_free (t);
- return NULL;
-}
-
-void
-mono_reflection_init_type_builder_generics (MonoObject *type, MonoError *error)
-{
- MonoReflectionTypeBuilder *tb;
-
- mono_error_init (error);
-
- if (!is_sre_type_builder(mono_object_class (type)))
- return;
- tb = (MonoReflectionTypeBuilder *)type;
-
- if (tb && tb->generic_container)
- mono_reflection_create_generic_class (tb, error);
-}
-
-static guint32
-mono_image_get_generic_field_token (MonoDynamicImage *assembly, MonoReflectionFieldBuilder *fb, MonoError *error)
-{
- MonoDynamicTable *table;
- MonoType *custom = NULL, *type;
- guint32 *values;
- guint32 token, pclass, parent, sig;
- gchar *name;
-
- mono_error_init (error);
-
- token = GPOINTER_TO_UINT (mono_g_hash_table_lookup (assembly->handleref_managed, fb));
- if (token)
- return token;
-
- MonoType *typeb = mono_reflection_type_get_handle (fb->typeb, error);
- return_val_if_nok (error, 0);
- /* FIXME: is this call necessary? */
- mono_class_from_mono_type (typeb);
-
- /*FIXME this is one more layer of ugliness due how types are created.*/
- mono_reflection_init_type_builder_generics (fb->type, error);
- return_val_if_nok (error, 0);
-
- /* fb->type does not include the custom modifiers */
- /* FIXME: We should do this in one place when a fieldbuilder is created */
- type = mono_reflection_type_get_handle ((MonoReflectionType*)fb->type, error);
- return_val_if_nok (error, 0);
-
- if (fb->modreq || fb->modopt) {
- type = custom = add_custom_modifiers (assembly, type, fb->modreq, fb->modopt, error);
- return_val_if_nok (error, 0);
- }
-
- sig = mono_dynimage_encode_fieldref_signature (assembly, NULL, type);
- g_free (custom);
-
- parent = mono_dynimage_encode_generic_typespec (assembly, (MonoReflectionTypeBuilder *) fb->typeb, error);
- return_val_if_nok (error, 0);
- g_assert ((parent & MONO_TYPEDEFORREF_MASK) == MONO_TYPEDEFORREF_TYPESPEC);
-
- pclass = MONO_MEMBERREF_PARENT_TYPESPEC;
- parent >>= MONO_TYPEDEFORREF_BITS;
-
- table = &assembly->tables [MONO_TABLE_MEMBERREF];
-
- name = mono_string_to_utf8_checked (fb->name, error);
- return_val_if_nok (error, 0);
-
- if (assembly->save) {
- alloc_table (table, table->rows + 1);
- values = table->values + table->next_idx * MONO_MEMBERREF_SIZE;
- values [MONO_MEMBERREF_CLASS] = pclass | (parent << MONO_MEMBERREF_PARENT_BITS);
- values [MONO_MEMBERREF_NAME] = string_heap_insert (&assembly->sheap, name);
- values [MONO_MEMBERREF_SIGNATURE] = sig;
- }
-
- token = MONO_TOKEN_MEMBER_REF | table->next_idx;
- table->next_idx ++;
- mono_g_hash_table_insert (assembly->handleref_managed, fb, GUINT_TO_POINTER(token));
- g_free (name);
- return token;
-}
-
static guint32
mono_image_get_sighelper_token (MonoDynamicImage *assembly, MonoReflectionSigHelper *helper, MonoError *error)
{
if (!is_ok (error)) goto fail;
}
+ // FIXME: This doesn't work, we don't use 'sig' for anything
+ // The token fixup doesn't work either
+ g_assert_not_reached ();
+
sig_token = mono_dynimage_encode_method_builder_signature (assembly, &rmb, error);
if (!is_ok (error))
goto fail;
return 0;
}
- if (strcmp (klass->name, "MethodBuilder") == 0) {
- MonoReflectionMethodBuilder *mb = (MonoReflectionMethodBuilder *)obj;
- MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder*)mb->type;
-
- if (tb->module->dynamic_image == assembly && !tb->generic_params && !mb->generic_params)
- token = mb->table_idx | MONO_TOKEN_METHOD_DEF;
- else {
- token = mono_image_get_methodbuilder_token (assembly, mb, create_open_instance, error);
- if (!mono_error_ok (error))
- return 0;
- }
- /*g_print ("got token 0x%08x for %s\n", token, mono_string_to_utf8 (mb->name));*/
- } else if (strcmp (klass->name, "ConstructorBuilder") == 0) {
- MonoReflectionCtorBuilder *mb = (MonoReflectionCtorBuilder *)obj;
- MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder*)mb->type;
-
- if (tb->module->dynamic_image == assembly && !tb->generic_params)
- token = mb->table_idx | MONO_TOKEN_METHOD_DEF;
- else {
- token = mono_image_get_ctorbuilder_token (assembly, mb, error);
- if (!mono_error_ok (error))
- return 0;
- }
- /*g_print ("got token 0x%08x for %s\n", token, mono_string_to_utf8 (mb->name));*/
- } else if (strcmp (klass->name, "FieldBuilder") == 0) {
- MonoReflectionFieldBuilder *fb = (MonoReflectionFieldBuilder *)obj;
- MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)fb->typeb;
- if (tb->generic_params) {
- token = mono_image_get_generic_field_token (assembly, fb, error);
- return_val_if_nok (error, 0);
- } else {
- if (tb->module->dynamic_image == assembly) {
- token = fb->table_idx | MONO_TOKEN_FIELD_DEF;
- } else {
- token = mono_image_get_fieldref_token (assembly, (MonoObject*)fb, fb->handle);
- }
- }
- } else if (strcmp (klass->name, "TypeBuilder") == 0) {
- MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)obj;
- if (create_open_instance && tb->generic_params) {
- MonoType *type;
- mono_reflection_init_type_builder_generics (obj, error);
- return_val_if_nok (error, 0);
- type = mono_reflection_type_get_handle ((MonoReflectionType *)obj, error);
- return_val_if_nok (error, 0);
- token = mono_dynimage_encode_typedef_or_ref_full (assembly, type, TRUE);
- token = mono_metadata_token_from_dor (token);
- } else if (tb->module->dynamic_image == assembly) {
- token = tb->table_idx | MONO_TOKEN_TYPE_DEF;
- } else {
- MonoType *type;
- type = mono_reflection_type_get_handle ((MonoReflectionType *)obj, error);
- return_val_if_nok (error, 0);
- token = mono_metadata_token_from_dor (mono_image_typedef_or_ref (assembly, type));
- }
- } else if (strcmp (klass->name, "RuntimeType") == 0) {
+ if (strcmp (klass->name, "RuntimeType") == 0) {
MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType *)obj, error);
return_val_if_nok (error, 0);
MonoClass *mc = mono_class_from_mono_type (type);
token = mono_metadata_token_from_dor (
mono_dynimage_encode_typedef_or_ref_full (assembly, type, mc->generic_container == NULL || create_open_instance));
- } else if (strcmp (klass->name, "GenericTypeParameterBuilder") == 0) {
- MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType *)obj, error);
- return_val_if_nok (error, 0);
- token = mono_metadata_token_from_dor (
- mono_image_typedef_or_ref (assembly, type));
- } else if (strcmp (klass->name, "MonoGenericClass") == 0) {
- MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType *)obj, error);
- return_val_if_nok (error, 0);
- token = mono_metadata_token_from_dor (
- mono_image_typedef_or_ref (assembly, type));
} else if (strcmp (klass->name, "MonoCMethod") == 0 ||
- strcmp (klass->name, "MonoMethod") == 0 ||
- strcmp (klass->name, "MonoGenericMethod") == 0 ||
- strcmp (klass->name, "MonoGenericCMethod") == 0) {
+ strcmp (klass->name, "MonoMethod") == 0) {
MonoReflectionMethod *m = (MonoReflectionMethod *)obj;
if (m->method->is_inflated) {
if (create_open_instance)
return_val_if_nok (error, 0);
token = mono_metadata_token_from_dor (
mono_image_typedef_or_ref (assembly, type));
- } else if (strcmp (klass->name, "FieldOnTypeBuilderInst") == 0) {
- MonoReflectionFieldOnTypeBuilderInst *f = (MonoReflectionFieldOnTypeBuilderInst*)obj;
- token = mono_image_get_field_on_inst_token (assembly, f, error);
- return_val_if_nok (error, 0);
- } else if (strcmp (klass->name, "ConstructorOnTypeBuilderInst") == 0) {
- MonoReflectionCtorOnTypeBuilderInst *c = (MonoReflectionCtorOnTypeBuilderInst*)obj;
- token = mono_image_get_ctor_on_inst_token (assembly, c, create_open_instance, error);
- if (!mono_error_ok (error))
- return 0;
- } else if (strcmp (klass->name, "MethodOnTypeBuilderInst") == 0) {
- MonoReflectionMethodOnTypeBuilderInst *m = (MonoReflectionMethodOnTypeBuilderInst*)obj;
- token = mono_image_get_method_on_inst_token (assembly, m, create_open_instance, error);
- if (!mono_error_ok (error))
- return 0;
- } else if (is_sre_array (klass) || is_sre_byref (klass) || is_sre_pointer (klass)) {
- MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType *)obj, error);
- return_val_if_nok (error, 0);
- token = mono_metadata_token_from_dor (
- mono_image_typedef_or_ref (assembly, type));
} else {
g_error ("requested token for %s\n", klass->name);
}
static gboolean
is_sre_generic_instance (MonoClass *klass)
{
- check_corlib_type_cached (klass, "System.Reflection", "MonoGenericClass");
+ check_corlib_type_cached (klass, "System.Reflection.Emit", "TypeBuilderInstantiation");
}
static gboolean
check_corlib_type_cached (klass, "System.Reflection.Emit", "FieldBuilder");
}
+static gboolean
+is_sre_gparam_builder (MonoClass *klass)
+{
+ check_corlib_type_cached (klass, "System.Reflection.Emit", "GenericTypeParameterBuilder");
+}
+
+static gboolean
+is_sre_enum_builder (MonoClass *klass)
+{
+ check_corlib_type_cached (klass, "System.Reflection.Emit", "EnumBuilder");
+}
+
gboolean
mono_is_sre_method_on_tb_inst (MonoClass *klass)
{
g_assert (res);
gclass->type.type = res;
return res;
- }
+ } else if (is_sre_gparam_builder (klass)) {
+ MonoReflectionGenericParam *gparam = (MonoReflectionGenericParam *)ref;
+ MonoGenericParamFull *param;
+ MonoImage *image;
+ MonoClass *pklass;
- g_error ("Cannot handle corlib user type %s", mono_type_full_name (&mono_object_class(ref)->byval_arg));
- return NULL;
-}
+ image = &gparam->tbuilder->module->dynamic_image->image;
-void
-ves_icall_SymbolType_create_unmanaged_type (MonoReflectionType *type)
-{
- MonoError error;
- mono_reflection_type_get_handle (type, &error);
- mono_error_set_pending_exception (&error);
-}
+ param = mono_image_new0 (image, MonoGenericParamFull, 1);
-static gboolean
-reflection_register_with_runtime (MonoReflectionType *type, MonoError *error)
-{
- MonoDomain *domain = mono_object_domain ((MonoObject*)type);
- MonoClass *klass;
+ param->info.name = mono_string_to_utf8_image (image, gparam->name, error);
+ mono_error_assert_ok (error);
+ param->param.num = gparam->index;
- mono_error_init (error);
+ if (gparam->mbuilder) {
+ if (!gparam->mbuilder->generic_container) {
+ gparam->mbuilder->generic_container = (MonoGenericContainer *)mono_image_alloc0 (image, sizeof (MonoGenericContainer));
+ gparam->mbuilder->generic_container->is_method = TRUE;
+ /*
+ * Cannot set owner.method, since the MonoMethod is not created yet.
+ * Set the image field instead, so type_in_image () works.
+ */
+ gparam->mbuilder->generic_container->is_anonymous = TRUE;
+ gparam->mbuilder->generic_container->owner.image = image;
+ }
+ param->param.owner = gparam->mbuilder->generic_container;
+ } else if (gparam->tbuilder) {
+ MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)(gparam->tbuilder), error);
+ mono_error_assert_ok (error);
+ MonoClass *owner = mono_class_from_mono_type (type);
+ g_assert (owner->generic_container);
+ param->param.owner = owner->generic_container;
+ }
- MonoType *res = mono_reflection_type_get_handle (type, error);
+ pklass = mono_class_from_generic_parameter_internal ((MonoGenericParam *) param);
- if (!res && is_ok (error)) {
- mono_error_set_argument (error, NULL, "Invalid generic instantiation, one or more arguments are not proper user types");
- }
- return_val_if_nok (error, FALSE);
+ gparam->type.type = &pklass->byval_arg;
- klass = mono_class_from_mono_type (res);
+ mono_class_set_ref_info (pklass, gparam);
+ mono_image_append_class_to_reflection_info_set (pklass);
- mono_loader_lock (); /*same locking as mono_type_get_object_checked */
- mono_domain_lock (domain);
+ return &pklass->byval_arg;
+ } else if (is_sre_enum_builder (klass)) {
+ MonoReflectionEnumBuilder *ebuilder = (MonoReflectionEnumBuilder *)ref;
- if (!image_is_dynamic (klass->image)) {
- mono_class_setup_supertypes (klass);
- } else {
- if (!domain->type_hash)
- domain->type_hash = mono_g_hash_table_new_type ((GHashFunc)mono_metadata_type_hash,
- (GCompareFunc)mono_metadata_type_equal, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DOMAIN, "domain reflection types table");
- mono_g_hash_table_insert (domain->type_hash, res, type);
- }
- mono_domain_unlock (domain);
- mono_loader_unlock ();
+ return mono_reflection_type_get_handle ((MonoReflectionType*)ebuilder->tb, error);
+ } else if (is_sre_type_builder (klass)) {
+ MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder*)ref;
- return TRUE;
-}
+ /* This happens when a finished type references an unfinished one. Have to create the minimal type */
+ reflection_setup_internal_class (tb, error);
+ mono_error_assert_ok (error);
+ g_assert (ref->type);
+ return ref->type;
+ }
-void
-mono_reflection_register_with_runtime (MonoReflectionType *type)
-{
- MonoError error;
- (void) reflection_register_with_runtime (type, &error);
- mono_error_set_pending_exception (&error);
+ g_error ("Cannot handle corlib user type %s", mono_type_full_name (&mono_object_class(ref)->byval_arg));
+ return NULL;
}
/**
#else /* DISABLE_REFLECTION_EMIT */
-void
-mono_reflection_register_with_runtime (MonoReflectionType *type)
-{
- /* This is empty */
-}
-
static gboolean
is_sre_type_builder (MonoClass *klass)
{
return FALSE;
}
-void
-mono_reflection_init_type_builder_generics (MonoObject *type, MonoError *error)
-{
- mono_error_init (error);
-}
-
#endif /* !DISABLE_REFLECTION_EMIT */
check_corlib_type_cached (klass, "System.Reflection", "MonoCMethod");
}
-static gboolean
-is_sr_mono_generic_method (MonoClass *klass)
-{
- check_corlib_type_cached (klass, "System.Reflection", "MonoGenericMethod");
-}
-
-static gboolean
-is_sr_mono_generic_cmethod (MonoClass *klass)
-{
- check_corlib_type_cached (klass, "System.Reflection", "MonoGenericCMethod");
-}
-
gboolean
mono_class_is_reflection_method_or_constructor (MonoClass *klass)
{
- return is_sr_mono_method (klass) || mono_is_sr_mono_cmethod (klass) || is_sr_mono_generic_method (klass) || is_sr_mono_generic_cmethod (klass);
+ return is_sr_mono_method (klass) || mono_is_sr_mono_cmethod (klass);
}
gboolean
tb->type.type = &klass->byval_arg;
if (tb->nesting_type) {
+ reflection_setup_internal_class ((MonoReflectionTypeBuilder*)tb->nesting_type, error);
g_assert (tb->nesting_type->type);
MonoType *nesting_type = mono_reflection_type_get_handle (tb->nesting_type, error);
if (!is_ok (error)) goto failure;
}
/**
- * ves_icall_TypeBuilder_setup_internal_class:
- * @tb: a TypeBuilder object
- *
- * (icall)
- * Creates a MonoClass that represents the TypeBuilder.
- * This is a trick that lets us simplify a lot of reflection code
- * (and will allow us to support Build and Run assemblies easier).
- *
- */
-void
-ves_icall_TypeBuilder_setup_internal_class (MonoReflectionTypeBuilder *tb)
-{
- MonoError error;
- (void) reflection_setup_internal_class (tb, &error);
- mono_error_set_pending_exception (&error);
-}
-
-/**
- * mono_reflection_create_generic_class:
+ * reflection_create_generic_class:
* @tb: a TypeBuilder object
* @error: set on error
*
* Creates the generic class after all generic parameters have been added.
* On success returns TRUE, on failure returns FALSE and sets @error.
- *
*/
-gboolean
-mono_reflection_create_generic_class (MonoReflectionTypeBuilder *tb, MonoError *error)
+static gboolean
+reflection_create_generic_class (MonoReflectionTypeBuilder *tb, MonoError *error)
{
-
MonoClass *klass;
int count, i;
mono_error_init (error);
+ reflection_setup_internal_class (tb, error);
+
klass = mono_class_from_mono_type (tb->type.type);
count = tb->generic_params ? mono_array_length (tb->generic_params) : 0;
- if (klass->generic_container || (count == 0))
+ if (count == 0)
return TRUE;
- g_assert (tb->generic_container && (tb->generic_container->owner.klass == klass));
-
klass->generic_container = (MonoGenericContainer *)mono_image_alloc0 (klass->image, sizeof (MonoGenericContainer));
klass->generic_container->owner.klass = klass;
if (rmb->generic_params) {
int count = mono_array_length (rmb->generic_params);
- MonoGenericContainer *container = rmb->generic_container;
-
- g_assert (container);
+ MonoGenericContainer *container;
+ container = (MonoGenericContainer *)mono_image_alloc0 (klass->image, sizeof (MonoGenericContainer));
+ container->is_method = TRUE;
+ container->is_anonymous = FALSE;
container->type_argc = count;
container->type_params = image_g_new0 (image, MonoGenericParamFull, count);
container->owner.method = m;
- container->is_anonymous = FALSE; // Method is now known, container is no longer anonymous
m->is_generic = TRUE;
mono_method_set_generic_container (m, container);
mono_error_assert_ok (error);
MonoGenericParamFull *param = (MonoGenericParamFull *) gp_type->data.generic_param;
container->type_params [i] = *param;
+ container->type_params [i].param.owner = container;
+
+ gp->type.type->data.generic_param = (MonoGenericParam*)&container->type_params [i];
}
/*
MonoMethodSignature *sig;
mono_loader_lock ();
- g_assert (klass->image != NULL);
- sig = ctor_builder_to_signature (klass->image, mb, error);
- mono_loader_unlock ();
- return_val_if_nok (error, NULL);
if (!mono_reflection_methodbuilder_from_ctor_builder (&rmb, mb, error))
return NULL;
- mb->mhandle = reflection_methodbuilder_to_mono_method (klass, &rmb, sig, error);
- return_val_if_nok (error, NULL);
- mono_save_custom_attrs (klass->image, mb->mhandle, mb->cattrs);
-
- /* If we are in a generic class, we might be called multiple times from inflate_method */
- if (!((MonoDynamicImage*)(MonoDynamicImage*)klass->image)->save && !klass->generic_container) {
- /* ilgen is no longer needed */
- mb->ilgen = NULL;
- }
-
- return mb->mhandle;
-}
-
-static MonoMethod*
-methodbuilder_to_mono_method (MonoClass *klass, MonoReflectionMethodBuilder* mb, MonoError *error)
-{
- ReflectionMethodBuilder rmb;
- MonoMethodSignature *sig;
-
- mono_error_init (error);
-
- mono_loader_lock ();
g_assert (klass->image != NULL);
- sig = method_builder_to_signature (klass->image, mb, error);
+ sig = ctor_builder_to_signature (klass->image, mb, error);
mono_loader_unlock ();
return_val_if_nok (error, NULL);
- if (!mono_reflection_methodbuilder_from_method_builder (&rmb, mb, error))
- return NULL;
-
mb->mhandle = reflection_methodbuilder_to_mono_method (klass, &rmb, sig, error);
return_val_if_nok (error, NULL);
mono_save_custom_attrs (klass->image, mb->mhandle, mb->cattrs);
- /* If we are in a generic class, we might be called multiple times from inflate_method */
- if (!((MonoDynamicImage*)(MonoDynamicImage*)klass->image)->save && !klass->generic_container) {
+ if (!((MonoDynamicImage*)(MonoDynamicImage*)klass->image)->save) {
/* ilgen is no longer needed */
- mb->ilgen = NULL;
- }
- return mb->mhandle;
-}
-#endif
-
-#ifndef DISABLE_REFLECTION_EMIT
-
-static MonoMethod *
-inflate_mono_method (MonoClass *klass, MonoMethod *method, MonoObject *obj)
-{
- MonoMethodInflated *imethod;
- MonoGenericContext *context;
- int i;
-
- /*
- * With generic code sharing the klass might not be inflated.
- * This can happen because classes inflated with their own
- * type arguments are "normalized" to the uninflated class.
- */
- if (!klass->generic_class)
- return method;
-
- context = mono_class_get_context (klass);
-
- if (klass->method.count && klass->methods) {
- /* Find the already created inflated method */
- for (i = 0; i < klass->method.count; ++i) {
- g_assert (klass->methods [i]->is_inflated);
- if (((MonoMethodInflated*)klass->methods [i])->declaring == method)
- break;
- }
- g_assert (i < klass->method.count);
- imethod = (MonoMethodInflated*)klass->methods [i];
- } else {
- MonoError error;
- imethod = (MonoMethodInflated *) mono_class_inflate_generic_method_full_checked (method, klass, context, &error);
- mono_error_assert_ok (&error);
- }
-
- if (method->is_generic && image_is_dynamic (method->klass->image)) {
- MonoDynamicImage *image = (MonoDynamicImage*)method->klass->image;
-
- mono_image_lock ((MonoImage*)image);
- mono_g_hash_table_insert (image->generic_def_objects, imethod, obj);
- mono_image_unlock ((MonoImage*)image);
- }
- return (MonoMethod *) imethod;
-}
-
-static MonoMethod *
-inflate_method (MonoReflectionType *type, MonoObject *obj, MonoError *error)
-{
- MonoMethod *method;
- MonoClass *gklass;
-
- mono_error_init (error);
-
- MonoClass *type_class = mono_object_class (type);
-
- if (is_sre_generic_instance (type_class)) {
- MonoReflectionGenericClass *mgc = (MonoReflectionGenericClass*)type;
- MonoType *generic_type = mono_reflection_type_get_handle ((MonoReflectionType*)mgc->generic_type, error);
- return_val_if_nok (error, NULL);
- gklass = mono_class_from_mono_type (generic_type);
- } else if (is_sre_type_builder (type_class)) {
- MonoType *t = mono_reflection_type_get_handle (type, error);
- return_val_if_nok (error, NULL);
- gklass = mono_class_from_mono_type (t);
- } else if (type->type) {
- gklass = mono_class_from_mono_type (type->type);
- gklass = mono_class_get_generic_type_definition (gklass);
- } else {
- g_error ("Can't handle type %s", mono_type_get_full_name (mono_object_class (type)));
- }
-
- if (!strcmp (obj->vtable->klass->name, "MethodBuilder"))
- if (((MonoReflectionMethodBuilder*)obj)->mhandle)
- method = ((MonoReflectionMethodBuilder*)obj)->mhandle;
- else {
- method = methodbuilder_to_mono_method (gklass, (MonoReflectionMethodBuilder *) obj, error);
- if (!method)
- return NULL;
- }
- else if (!strcmp (obj->vtable->klass->name, "ConstructorBuilder")) {
- method = ctorbuilder_to_mono_method (gklass, (MonoReflectionCtorBuilder *) obj, error);
- if (!method)
- return NULL;
- } else if (!strcmp (obj->vtable->klass->name, "MonoMethod") || !strcmp (obj->vtable->klass->name, "MonoCMethod"))
- method = ((MonoReflectionMethod *) obj)->method;
- else {
- method = NULL; /* prevent compiler warning */
- g_error ("can't handle type %s", obj->vtable->klass->name);
+ mb->ilgen = NULL;
}
- MonoType *t = mono_reflection_type_get_handle (type, error);
- return_val_if_nok (error, NULL);
- return inflate_mono_method (mono_class_from_mono_type (t), method, obj);
+ return mb->mhandle;
}
-static void
-reflection_generic_class_initialize (MonoReflectionGenericClass *type, MonoError *error)
+static MonoMethod*
+methodbuilder_to_mono_method (MonoClass *klass, MonoReflectionMethodBuilder* mb, MonoError *error)
{
- MonoGenericClass *gclass;
- MonoClass *klass, *gklass;
- MonoType *gtype;
+ ReflectionMethodBuilder rmb;
+ MonoMethodSignature *sig;
mono_error_init (error);
- gtype = mono_reflection_type_get_handle ((MonoReflectionType*)type, error);
- return_if_nok (error);
- klass = mono_class_from_mono_type (gtype);
- g_assert (gtype->type == MONO_TYPE_GENERICINST);
- gclass = gtype->data.generic_class;
+ mono_loader_lock ();
- if (!gclass->is_dynamic)
- return;
+ if (!mono_reflection_methodbuilder_from_method_builder (&rmb, mb, error))
+ return NULL;
+
+ g_assert (klass->image != NULL);
+ sig = method_builder_to_signature (klass->image, mb, error);
+ mono_loader_unlock ();
+ return_val_if_nok (error, NULL);
- gklass = gclass->container_class;
- mono_class_init (gklass);
+ mb->mhandle = reflection_methodbuilder_to_mono_method (klass, &rmb, sig, error);
+ return_val_if_nok (error, NULL);
+ mono_save_custom_attrs (klass->image, mb->mhandle, mb->cattrs);
- /* Mark this as needing synchronization with its generic container */
- gclass->need_sync = TRUE;
+ if (!((MonoDynamicImage*)(MonoDynamicImage*)klass->image)->save)
+ /* ilgen is no longer needed */
+ mb->ilgen = NULL;
+ return mb->mhandle;
}
+#endif
-void
-mono_reflection_generic_class_initialize (MonoReflectionGenericClass *type, MonoArray *fields)
-{
- MonoError error;
- reflection_generic_class_initialize (type, &error);
- mono_error_set_pending_exception (&error);
-}
+#ifndef DISABLE_REFLECTION_EMIT
/**
* fix_partial_generic_class:
{
mono_error_init (error);
MonoClass *klass = mono_object_class (method);
- if (is_sr_mono_method (klass) || is_sr_mono_generic_method (klass)) {
+ if (is_sr_mono_method (klass)) {
MonoReflectionMethod *sr_method = (MonoReflectionMethod*)method;
return sr_method->method;
}
return mb->mhandle;
}
if (mono_is_sre_method_on_tb_inst (klass)) {
- MonoReflectionMethodOnTypeBuilderInst *m = (MonoReflectionMethodOnTypeBuilderInst*)method;
- MonoMethod *result;
- /*FIXME move this to a proper method and unify with resolve_object*/
- if (m->method_args) {
- result = mono_reflection_method_on_tb_inst_get_handle (m, error);
- } else {
- MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)m->inst, error);
- return_val_if_nok (error, NULL);
- MonoClass *inflated_klass = mono_class_from_mono_type (type);
- MonoMethod *mono_method;
+ MonoClass *handle_class;
- if (is_sre_method_builder (mono_object_class (m->mb)))
- mono_method = ((MonoReflectionMethodBuilder *)m->mb)->mhandle;
- else if (is_sr_mono_method (mono_object_class (m->mb)))
- mono_method = ((MonoReflectionMethod *)m->mb)->method;
- else
- g_error ("resolve_object:: can't handle a MTBI with base_method of type %s", mono_type_get_full_name (mono_object_class (m->mb)));
+ MonoMethod *result = mono_reflection_resolve_object (NULL, method, &handle_class, NULL, error);
+ return_val_if_nok (error, NULL);
- result = inflate_mono_method (inflated_klass, mono_method, (MonoObject*)m->mb);
- }
return result;
}
}
}
-static MonoReflectionEvent *
-reflection_event_builder_get_event_info (MonoReflectionTypeBuilder *tb, MonoReflectionEventBuilder *eb, MonoError *error)
-{
- mono_error_init (error);
-
- MonoEvent *event = g_new0 (MonoEvent, 1);
- MonoClass *klass;
-
- MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)tb, error);
- if (!is_ok (error)) {
- g_free (event);
- return NULL;
- }
- klass = mono_class_from_mono_type (type);
-
- event->parent = klass;
- event->attrs = eb->attrs;
- event->name = mono_string_to_utf8_checked (eb->name, error);
- if (!is_ok (error)) {
- g_free (event);
- return NULL;
- }
- if (eb->add_method)
- event->add = eb->add_method->mhandle;
- if (eb->remove_method)
- event->remove = eb->remove_method->mhandle;
- if (eb->raise_method)
- event->raise = eb->raise_method->mhandle;
-
-#ifndef MONO_SMALL_CONFIG
- if (eb->other_methods) {
- int j;
- event->other = g_new0 (MonoMethod*, mono_array_length (eb->other_methods) + 1);
- for (j = 0; j < mono_array_length (eb->other_methods); ++j) {
- MonoReflectionMethodBuilder *mb =
- mono_array_get (eb->other_methods,
- MonoReflectionMethodBuilder*, j);
- event->other [j] = mb->mhandle;
- }
- }
-#endif
-
- MonoReflectionEvent *ev_obj = mono_event_get_object_checked (mono_object_domain (tb), klass, event, error);
- if (!is_ok (error)) {
-#ifndef MONO_SMALL_CONFIG
- g_free (event->other);
-#endif
- g_free (event);
- return NULL;
- }
- return ev_obj;
-}
-
-MonoReflectionEvent *
-ves_icall_TypeBuilder_get_event_info (MonoReflectionTypeBuilder *tb, MonoReflectionEventBuilder *eb)
-{
- MonoError error;
- MonoReflectionEvent *result = reflection_event_builder_get_event_info (tb, eb, &error);
- mono_error_set_pending_exception (&error);
- return result;
-}
-
static void
typebuilder_setup_events (MonoClass *klass, MonoError *error)
{
mono_error_init (&error);
+ reflection_create_generic_class (tb, &error);
+ mono_error_assert_ok (&error);
+
domain = mono_object_domain (tb);
klass = mono_class_from_mono_type (tb->type.type);
mono_class_setup_supertypes (klass);
mono_class_setup_mono_type (klass);
-#if 0
- if (!((MonoDynamicImage*)klass->image)->run) {
- if (klass->generic_container) {
- /* FIXME: The code below can't handle generic classes */
- klass->wastypebuilder = TRUE;
- mono_loader_unlock ();
- mono_domain_unlock (domain);
-
- res = mono_type_get_object_checked (mono_object_domain (tb), &klass->byval_arg, &error);
- mono_error_set_pending_exception (&error);
-
- return res;
- }
- }
-#endif
-
/* enums are done right away */
if (!klass->enumtype)
if (!ensure_runtime_vtable (klass, &error))
for (i = 0; i < mono_array_length (tb->subtypes); ++i) {
MonoReflectionTypeBuilder *subtb = mono_array_get (tb->subtypes, MonoReflectionTypeBuilder*, i);
mono_class_alloc_ext (klass);
+
+ if (!subtb->type.type) {
+ reflection_setup_internal_class (subtb, &error);
+ mono_error_assert_ok (&error);
+ }
+
MonoType *subtype = mono_reflection_type_get_handle ((MonoReflectionType*)subtb, &error);
if (!is_ok (&error)) goto failure;
klass->ext->nested_classes = g_list_prepend_image (klass->image, klass->ext->nested_classes, mono_class_from_mono_type (subtype));
return NULL;
}
-static gboolean
-reflection_initialize_generic_parameter (MonoReflectionGenericParam *gparam, MonoError *error)
-{
- MonoGenericParamFull *param;
- MonoImage *image;
- MonoClass *pklass;
-
- mono_error_init (error);
-
- image = &gparam->tbuilder->module->dynamic_image->image;
-
- param = mono_image_new0 (image, MonoGenericParamFull, 1);
-
- param->info.name = mono_string_to_utf8_image (image, gparam->name, error);
- mono_error_assert_ok (error);
- param->param.num = gparam->index;
-
- if (gparam->mbuilder) {
- if (!gparam->mbuilder->generic_container) {
- MonoType *tb = mono_reflection_type_get_handle ((MonoReflectionType*)gparam->mbuilder->type, error);
- return_val_if_nok (error, FALSE);
-
- MonoClass *klass = mono_class_from_mono_type (tb);
- gparam->mbuilder->generic_container = (MonoGenericContainer *)mono_image_alloc0 (klass->image, sizeof (MonoGenericContainer));
- gparam->mbuilder->generic_container->is_method = TRUE;
- /*
- * Cannot set owner.method, since the MonoMethod is not created yet.
- * Set the image field instead, so type_in_image () works.
- */
- gparam->mbuilder->generic_container->is_anonymous = TRUE;
- gparam->mbuilder->generic_container->owner.image = klass->image;
- }
- param->param.owner = gparam->mbuilder->generic_container;
- } else if (gparam->tbuilder) {
- if (!gparam->tbuilder->generic_container) {
- MonoType *tb = mono_reflection_type_get_handle ((MonoReflectionType*)gparam->tbuilder, error);
- return_val_if_nok (error, FALSE);
- MonoClass *klass = mono_class_from_mono_type (tb);
- gparam->tbuilder->generic_container = (MonoGenericContainer *)mono_image_alloc0 (klass->image, sizeof (MonoGenericContainer));
- gparam->tbuilder->generic_container->owner.klass = klass;
- }
- param->param.owner = gparam->tbuilder->generic_container;
- }
-
- pklass = mono_class_from_generic_parameter_internal ((MonoGenericParam *) param);
-
- gparam->type.type = &pklass->byval_arg;
-
- mono_class_set_ref_info (pklass, gparam);
- mono_image_append_class_to_reflection_info_set (pklass);
-
- return TRUE;
-}
-
-void
-ves_icall_GenericTypeParameterBuilder_initialize_generic_parameter (MonoReflectionGenericParam *gparam)
-{
- MonoError error;
- (void) reflection_initialize_generic_parameter (gparam, &error);
- mono_error_set_pending_exception (&error);
-}
-
-
typedef struct {
MonoMethod *handle;
MonoDomain *domain;
gpointer
mono_reflection_resolve_object (MonoImage *image, MonoObject *obj, MonoClass **handle_class, MonoGenericContext *context, MonoError *error)
{
+ MonoClass *oklass = obj->vtable->klass;
gpointer result = NULL;
mono_error_init (error);
- if (strcmp (obj->vtable->klass->name, "String") == 0) {
+ if (strcmp (oklass->name, "String") == 0) {
result = mono_string_intern_checked ((MonoString*)obj, error);
return_val_if_nok (error, NULL);
*handle_class = mono_defaults.string_class;
g_assert (result);
- } else if (strcmp (obj->vtable->klass->name, "RuntimeType") == 0) {
+ } else if (strcmp (oklass->name, "RuntimeType") == 0) {
MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)obj, error);
return_val_if_nok (error, NULL);
MonoClass *mc = mono_class_from_mono_type (type);
}
*handle_class = mono_defaults.typehandle_class;
g_assert (result);
- } else if (strcmp (obj->vtable->klass->name, "MonoMethod") == 0 ||
- strcmp (obj->vtable->klass->name, "MonoCMethod") == 0 ||
- strcmp (obj->vtable->klass->name, "MonoGenericCMethod") == 0 ||
- strcmp (obj->vtable->klass->name, "MonoGenericMethod") == 0) {
+ } else if (strcmp (oklass->name, "MonoMethod") == 0 ||
+ strcmp (oklass->name, "MonoCMethod") == 0) {
result = ((MonoReflectionMethod*)obj)->method;
if (context) {
result = mono_class_inflate_generic_method_checked ((MonoMethod *)result, context, error);
}
*handle_class = mono_defaults.methodhandle_class;
g_assert (result);
- } else if (strcmp (obj->vtable->klass->name, "MethodBuilder") == 0) {
- MonoReflectionMethodBuilder *mb = (MonoReflectionMethodBuilder*)obj;
- result = mb->mhandle;
- if (!result) {
- /* Type is not yet created */
- MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder*)mb->type;
-
- mono_domain_try_type_resolve_checked (mono_domain_get (), NULL, (MonoObject*)tb, error);
- return_val_if_nok (error, NULL);
-
- /*
- * Hopefully this has been filled in by calling CreateType() on the
- * TypeBuilder.
- */
- /*
- * TODO: This won't work if the application finishes another
- * TypeBuilder instance instead of this one.
- */
- result = mb->mhandle;
- }
- if (context) {
- result = mono_class_inflate_generic_method_checked ((MonoMethod *)result, context, error);
- mono_error_assert_ok (error);
- }
- *handle_class = mono_defaults.methodhandle_class;
- } else if (strcmp (obj->vtable->klass->name, "ConstructorBuilder") == 0) {
- MonoReflectionCtorBuilder *cb = (MonoReflectionCtorBuilder*)obj;
-
- result = cb->mhandle;
- if (!result) {
- MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder*)cb->type;
-
- mono_domain_try_type_resolve_checked (mono_domain_get (), NULL, (MonoObject*)tb, error);
- return_val_if_nok (error, NULL);
- result = cb->mhandle;
- }
- if (context) {
- result = mono_class_inflate_generic_method_checked ((MonoMethod *)result, context, error);
- mono_error_assert_ok (error);
- }
- *handle_class = mono_defaults.methodhandle_class;
- } else if (strcmp (obj->vtable->klass->name, "MonoField") == 0) {
+ } else if (strcmp (oklass->name, "MonoField") == 0) {
MonoClassField *field = ((MonoReflectionField*)obj)->field;
ensure_complete_type (field->parent, error);
}
*handle_class = mono_defaults.fieldhandle_class;
g_assert (result);
- } else if (strcmp (obj->vtable->klass->name, "FieldBuilder") == 0) {
- MonoReflectionFieldBuilder *fb = (MonoReflectionFieldBuilder*)obj;
- result = fb->handle;
-
- if (!result) {
- MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder*)fb->typeb;
-
- mono_domain_try_type_resolve_checked (mono_domain_get (), NULL, (MonoObject*)tb, error);
- return_val_if_nok (error, NULL);
- result = fb->handle;
- }
-
- if (fb->handle && fb->handle->parent->generic_container) {
- MonoClass *klass = fb->handle->parent;
- MonoType *type = mono_class_inflate_generic_type_checked (&klass->byval_arg, context, error);
- return_val_if_nok (error, NULL);
-
- MonoClass *inflated = mono_class_from_mono_type (type);
-
- result = mono_class_get_field_from_name (inflated, mono_field_get_name (fb->handle));
- g_assert (result);
- mono_metadata_free_type (type);
- }
- *handle_class = mono_defaults.fieldhandle_class;
- } else if (strcmp (obj->vtable->klass->name, "TypeBuilder") == 0) {
+ } else if (strcmp (oklass->name, "TypeBuilder") == 0) {
MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder*)obj;
MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)tb, error);
return_val_if_nok (error, NULL);
g_assert (result);
}
*handle_class = mono_defaults.typehandle_class;
- } else if (strcmp (obj->vtable->klass->name, "SignatureHelper") == 0) {
+ } else if (strcmp (oklass->name, "SignatureHelper") == 0) {
MonoReflectionSigHelper *helper = (MonoReflectionSigHelper*)obj;
MonoMethodSignature *sig;
int nargs, i;
result = sig;
*handle_class = NULL;
- } else if (strcmp (obj->vtable->klass->name, "DynamicMethod") == 0) {
+ } else if (strcmp (oklass->name, "DynamicMethod") == 0) {
MonoReflectionDynamicMethod *method = (MonoReflectionDynamicMethod*)obj;
/* Already created by the managed code */
g_assert (method->mhandle);
result = method->mhandle;
*handle_class = mono_defaults.methodhandle_class;
- } else if (strcmp (obj->vtable->klass->name, "GenericTypeParameterBuilder") == 0) {
- MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)obj, error);
- return_val_if_nok (error, NULL);
- type = mono_class_inflate_generic_type_checked (type, context, error);
- return_val_if_nok (error, NULL);
-
- result = mono_class_from_mono_type (type);
- *handle_class = mono_defaults.typehandle_class;
- g_assert (result);
- mono_metadata_free_type (type);
- } else if (strcmp (obj->vtable->klass->name, "MonoGenericClass") == 0) {
- MonoType *type = mono_reflection_type_get_handle ((MonoReflectionType*)obj, error);
- return_val_if_nok (error, NULL);
- type = mono_class_inflate_generic_type_checked (type, context, error);
- return_val_if_nok (error, NULL);
-
- result = mono_class_from_mono_type (type);
- *handle_class = mono_defaults.typehandle_class;
- g_assert (result);
- mono_metadata_free_type (type);
- } else if (strcmp (obj->vtable->klass->name, "FieldOnTypeBuilderInst") == 0) {
- MonoReflectionFieldOnTypeBuilderInst *f = (MonoReflectionFieldOnTypeBuilderInst*)obj;
- MonoClass *inflated;
- MonoType *type;
- MonoClassField *field;
-
- if (is_sre_field_builder (mono_object_class (f->fb)))
- field = ((MonoReflectionFieldBuilder*)f->fb)->handle;
- else if (is_sr_mono_field (mono_object_class (f->fb)))
- field = ((MonoReflectionField*)f->fb)->field;
- else
- g_error ("resolve_object:: can't handle a FTBI with base_method of type %s", mono_type_get_full_name (mono_object_class (f->fb)));
-
- MonoType *finst = mono_reflection_type_get_handle ((MonoReflectionType*)f->inst, error);
- return_val_if_nok (error, NULL);
- type = mono_class_inflate_generic_type_checked (finst, context, error);
- return_val_if_nok (error, NULL);
-
- inflated = mono_class_from_mono_type (type);
-
- result = field = mono_class_get_field_from_name (inflated, mono_field_get_name (field));
- ensure_complete_type (field->parent, error);
- if (!is_ok (error)) {
- mono_metadata_free_type (type);
- return NULL;
- }
-
- g_assert (result);
- mono_metadata_free_type (type);
- *handle_class = mono_defaults.fieldhandle_class;
- } else if (strcmp (obj->vtable->klass->name, "ConstructorOnTypeBuilderInst") == 0) {
- MonoReflectionCtorOnTypeBuilderInst *c = (MonoReflectionCtorOnTypeBuilderInst*)obj;
- MonoType *cinst = mono_reflection_type_get_handle ((MonoReflectionType*)c->inst, error);
- return_val_if_nok (error, NULL);
- MonoType *type = mono_class_inflate_generic_type_checked (cinst, context, error);
- return_val_if_nok (error, NULL);
-
- MonoClass *inflated_klass = mono_class_from_mono_type (type);
- MonoMethod *method;
-
- if (mono_is_sre_ctor_builder (mono_object_class (c->cb)))
- method = ((MonoReflectionCtorBuilder *)c->cb)->mhandle;
- else if (mono_is_sr_mono_cmethod (mono_object_class (c->cb)))
- method = ((MonoReflectionMethod *)c->cb)->method;
- else
- g_error ("resolve_object:: can't handle a CTBI with base_method of type %s", mono_type_get_full_name (mono_object_class (c->cb)));
-
- result = inflate_mono_method (inflated_klass, method, (MonoObject*)c->cb);
- *handle_class = mono_defaults.methodhandle_class;
- mono_metadata_free_type (type);
- } else if (strcmp (obj->vtable->klass->name, "MethodOnTypeBuilderInst") == 0) {
- MonoReflectionMethodOnTypeBuilderInst *m = (MonoReflectionMethodOnTypeBuilderInst*)obj;
- if (m->method_args) {
- result = mono_reflection_method_on_tb_inst_get_handle (m, error);
- return_val_if_nok (error, NULL);
- if (context) {
- result = mono_class_inflate_generic_method_checked ((MonoMethod *)result, context, error);
- mono_error_assert_ok (error);
- }
- } else {
- MonoType *minst = mono_reflection_type_get_handle ((MonoReflectionType*)m->inst, error);
- return_val_if_nok (error, NULL);
- MonoType *type = mono_class_inflate_generic_type_checked (minst, context, error);
- return_val_if_nok (error, NULL);
-
- MonoClass *inflated_klass = mono_class_from_mono_type (type);
- MonoMethod *method;
-
- if (is_sre_method_builder (mono_object_class (m->mb)))
- method = ((MonoReflectionMethodBuilder *)m->mb)->mhandle;
- else if (is_sr_mono_method (mono_object_class (m->mb)))
- method = ((MonoReflectionMethod *)m->mb)->method;
- else
- g_error ("resolve_object:: can't handle a MTBI with base_method of type %s", mono_type_get_full_name (mono_object_class (m->mb)));
-
- result = inflate_mono_method (inflated_klass, method, (MonoObject*)m->mb);
- mono_metadata_free_type (type);
- }
- *handle_class = mono_defaults.methodhandle_class;
- } else if (strcmp (obj->vtable->klass->name, "MonoArrayMethod") == 0) {
+ } else if (strcmp (oklass->name, "MonoArrayMethod") == 0) {
MonoReflectionArrayMethod *m = (MonoReflectionArrayMethod*)obj;
MonoType *mtype;
MonoClass *klass;
result = method;
*handle_class = mono_defaults.methodhandle_class;
- } else if (is_sre_array (mono_object_get_class(obj)) ||
- is_sre_byref (mono_object_get_class(obj)) ||
- is_sre_pointer (mono_object_get_class(obj))) {
- MonoReflectionType *ref_type = (MonoReflectionType *)obj;
- MonoType *type = mono_reflection_type_get_handle (ref_type, error);
- return_val_if_nok (error, NULL);
-
- if (context) {
- MonoType *inflated = mono_class_inflate_generic_type_checked (type, context, error);
- return_val_if_nok (error, NULL);
-
- result = mono_class_from_mono_type (inflated);
- mono_metadata_free_type (inflated);
- } else {
- result = mono_class_from_mono_type (type);
+ } else if (is_sre_method_builder (oklass) ||
+ mono_is_sre_ctor_builder (oklass) ||
+ is_sre_field_builder (oklass) ||
+ is_sre_gparam_builder (oklass) ||
+ is_sre_generic_instance (oklass) ||
+ is_sre_array (oklass) ||
+ is_sre_byref (oklass) ||
+ is_sre_pointer (oklass) ||
+ !strcmp (oklass->name, "FieldOnTypeBuilderInst") ||
+ !strcmp (oklass->name, "MethodOnTypeBuilderInst") ||
+ !strcmp (oklass->name, "ConstructorOnTypeBuilderInst")) {
+ static MonoMethod *resolve_method;
+ if (!resolve_method) {
+ MonoMethod *m = mono_class_get_method_from_name_flags (mono_class_get_module_builder_class (), "RuntimeResolve", 1, 0);
+ g_assert (m);
+ mono_memory_barrier ();
+ resolve_method = m;
}
- *handle_class = mono_defaults.typehandle_class;
+ void *args [16];
+ args [0] = obj;
+ obj = mono_runtime_invoke_checked (resolve_method, NULL, args, error);
+ mono_error_assert_ok (error);
+ g_assert (obj);
+ return mono_reflection_resolve_object (image, obj, handle_class, context, error);
} else {
g_print ("%s\n", obj->vtable->klass->name);
g_assert_not_reached ();
return NULL;
}
-void
-ves_icall_TypeBuilder_setup_internal_class (MonoReflectionTypeBuilder *tb)
-{
- g_assert_not_reached ();
-}
-
-gboolean
-mono_reflection_create_generic_class (MonoReflectionTypeBuilder *tb, MonoError *error)
-{
- g_assert_not_reached ();
- return FALSE;
-}
-
void
mono_reflection_dynimage_basic_init (MonoReflectionAssemblyBuilder *assemblyb)
{
return 0;
}
-void
-mono_reflection_generic_class_initialize (MonoReflectionGenericClass *type, MonoArray *fields)
-{
- g_assert_not_reached ();
-}
-
void
mono_reflection_get_dynamic_overrides (MonoClass *klass, MonoMethod ***overrides, int *num_overrides, MonoError *error)
{
*num_overrides = 0;
}
-MonoReflectionEvent *
-ves_icall_TypeBuilder_get_event_info (MonoReflectionTypeBuilder *tb, MonoReflectionEventBuilder *eb)
-{
- g_assert_not_reached ();
- return NULL;
-}
-
MonoReflectionType*
ves_icall_TypeBuilder_create_runtime_class (MonoReflectionTypeBuilder *tb)
{
return NULL;
}
-void
-ves_icall_GenericTypeParameterBuilder_initialize_generic_parameter (MonoReflectionGenericParam *gparam)
-{
- g_assert_not_reached ();
-}
-
void
ves_icall_DynamicMethod_create_dynamic_method (MonoReflectionDynamicMethod *mb)
{
#endif /* DISABLE_REFLECTION_EMIT */
-#ifndef DISABLE_REFLECTION_EMIT
-MonoMethod*
-mono_reflection_method_builder_to_mono_method (MonoReflectionMethodBuilder *mb, MonoError *error)
-{
- MonoType *tb;
- MonoClass *klass;
-
- tb = mono_reflection_type_get_handle ((MonoReflectionType*)mb->type, error);
- return_val_if_nok (error, NULL);
- klass = mono_class_from_mono_type (tb);
-
- return methodbuilder_to_mono_method (klass, mb, error);
-}
-#else /* DISABLE_REFLECTION_EMIT */
-MonoMethod*
-mono_reflection_method_builder_to_mono_method (MonoReflectionMethodBuilder *mb, MonoError *error)
+void
+mono_sre_generic_param_table_entry_free (GenericParamTableEntry *entry)
{
- g_assert_not_reached ();
- return NULL;
+ mono_gc_deregister_root ((char*) &entry->gparam);
+ g_free (entry);
}
-#endif /* DISABLE_REFLECTION_EMIT */
gint32
ves_icall_ModuleBuilder_getToken (MonoReflectionModuleBuilder *mb, MonoObject *obj, gboolean create_open_instance)
return obj;
}
-/**
- * ves_icall_TypeBuilder_create_generic_class:
- * @tb: a TypeBuilder object
- *
- * (icall)
- * Creates the generic class after all generic parameters have been added.
- */
-void
-ves_icall_TypeBuilder_create_generic_class (MonoReflectionTypeBuilder *tb)
-{
- MonoError error;
- (void) mono_reflection_create_generic_class (tb, &error);
- mono_error_set_pending_exception (&error);
-}
-
#ifndef DISABLE_REFLECTION_EMIT
MonoArray*
ves_icall_CustomAttributeBuilder_GetBlob (MonoReflectionAssembly *assembly, MonoObject *ctor, MonoArray *ctorArgs, MonoArray *properties, MonoArray *propValues, MonoArray *fields, MonoArray* fieldValues)
mono_reflection_dynimage_basic_init (assemblyb);
}
-MonoBoolean
-ves_icall_TypeBuilder_get_IsGenericParameter (MonoReflectionTypeBuilder *tb)
-{
- return mono_type_is_generic_parameter (tb->type.type);
-}
-
void
ves_icall_EnumBuilder_setup_enum_type (MonoReflectionType *enumtype,
MonoReflectionType *t)
#include <signal.h>
#endif
+#if defined(HOST_WIN32)
+#include <objbase.h>
+#endif
+
#if defined(PLATFORM_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
#define USE_TKILL_ON_ANDROID 1
#endif
if (thread != pthread_self ()) {
MONO_ENTER_GC_SAFE;
/* This shouldn't block */
- pthread_join (thread, NULL);
+ mono_native_thread_join (thread);
MONO_EXIT_GC_SAFE;
}
} else {
return;
thread = (pthread_t)tid;
MONO_ENTER_GC_SAFE;
- pthread_join (thread, NULL);
+ mono_native_thread_join (thread);
MONO_EXIT_GC_SAFE;
#endif
}
return mono_thread_resume_interruption ();
}
+#if 0
/* Returns TRUE if the current thread is ready to be interrupted. */
gboolean
mono_threads_is_ready_to_be_interrupted (void)
UNLOCK_THREAD (thread);
return TRUE;
}
+#endif
void
mono_thread_internal_describe (MonoInternalThread *internal, GString *text)
bundle_save_library_initialize ()
{
bundle_save_library_initialized = 1;
- char *path = g_build_filename (g_get_tmp_dir (), "mono-bundle-XXXXXX");
+ char *path = g_build_filename (g_get_tmp_dir (), "mono-bundle-XXXXXX", NULL);
bundled_dylibrary_directory = g_mkdtemp (path);
g_free (path);
if (bundled_dylibrary_directory == NULL)
save_library (int fd, uint64_t offset, uint64_t size, const char *destfname)
{
MonoDl *lib;
- char *file, *buffer, *err;
+ char *file, *buffer, *err, *internal_path;
if (!bundle_save_library_initialized)
bundle_save_library_initialize ();
- file = g_build_filename (bundled_dylibrary_directory, destfname);
+ file = g_build_filename (bundled_dylibrary_directory, destfname, NULL);
buffer = load_from_region (fd, offset, size);
g_file_set_contents (file, buffer, size, NULL);
+
lib = mono_dl_open (file, MONO_DL_LAZY, &err);
- if (err != NULL){
- fprintf (stderr, "Error loading shared library: %s\n", file);
+ if (lib == NULL){
+ fprintf (stderr, "Error loading shared library: %s %s\n", file, err);
exit (1);
}
- mono_loader_register_module (destfname, lib);
+ // Register the name with "." as this is how it will be found when embedded
+ internal_path = g_build_filename (".", destfname, NULL);
+ mono_loader_register_module (internal_path, lib);
+ g_free (internal_path);
bundle_library_paths = g_slist_append (bundle_library_paths, file);
g_free (buffer);
/* See the comment in mono_runtime_shutdown_stat_profiler (). */
if (mono_native_thread_id_get () == sampling_thread) {
-#ifdef HAVE_CLOCK_NANOSLEEP
- if (mono_profiler_get_sampling_mode () == MONO_PROFILER_STAT_MODE_PROCESS) {
- InterlockedIncrement (&profiler_interrupt_signals_received);
- return;
- }
-#endif
-
- g_error ("%s: Unexpected profiler signal received by the sampler thread", __func__);
+ InterlockedIncrement (&profiler_interrupt_signals_received);
+ return;
}
InterlockedIncrement (&profiler_signals_received);
{
InterlockedWrite (&sampling_thread_running, 0);
-#ifdef HAVE_CLOCK_NANOSLEEP
+#ifndef PLATFORM_MACOSX
/*
* There is a slight problem when we're using CLOCK_PROCESS_CPUTIME_ID: If
* we're shutting down and there's largely no activity in the process other
* sampling_thread_running upon an interrupt and return immediately if it's
* zero. profiler_signal_handler () has a special case to ignore the signal
* for the sampler thread.
- *
- * We do not need to do this on platforms where we use a regular sleep
- * based on a monotonic clock. The sleep will return in a reasonable amount
- * of time in those cases.
*/
- if (mono_profiler_get_sampling_mode () == MONO_PROFILER_STAT_MODE_PROCESS) {
- MonoThreadInfo *info;
-
- // Did it shut down already?
- if ((info = mono_thread_info_lookup (sampling_thread))) {
- while (!InterlockedRead (&sampling_thread_exiting)) {
- mono_threads_pthread_kill (info, profiler_signal);
- mono_thread_info_usleep (10 * 1000 /* 10ms */);
- }
+ MonoThreadInfo *info;
- // Make sure info can be freed.
- mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
+ // Did it shut down already?
+ if ((info = mono_thread_info_lookup (sampling_thread))) {
+ while (!InterlockedRead (&sampling_thread_exiting)) {
+ mono_threads_pthread_kill (info, profiler_signal);
+ mono_thread_info_usleep (10 * 1000 /* 10ms */);
}
+
+ // Make sure info can be freed.
+ mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
}
#endif
- pthread_join (sampling_thread, NULL);
+ mono_native_thread_join (sampling_thread);
/*
* We can't safely remove the signal handler because we have no guarantee
libmono_profiler_iomap_static_la_SOURCES = mono-profiler-iomap.c
libmono_profiler_iomap_static_la_LDFLAGS = -static
-libmono_profiler_log_la_SOURCES = proflog.c
+libmono_profiler_log_la_SOURCES = mono-profiler-log.c
libmono_profiler_log_la_LIBADD = $(monodir)/mono/mini/$(LIBMONO_LA) $(GLIB_LIBS) $(Z_LIBS)
libmono_profiler_log_la_LDFLAGS = $(prof_ldflags)
-libmono_profiler_log_static_la_SOURCES = proflog.c
+libmono_profiler_log_static_la_SOURCES = mono-profiler-log.c
libmono_profiler_log_static_la_LDFLAGS = -static
if HAVE_VTUNE
libmono_profiler_vtune_static_la_LDFLAGS = -static
endif
-mprof_report_SOURCES = decode.c
+mprof_report_SOURCES = mprof-report.c
mprof_report_LDADD = $(Z_LIBS) $(GLIB_LIBS) $(LIBICONV)
PLOG_TESTS_SRC=test-alloc.cs test-busy.cs test-monitor.cs test-excleave.cs \
check-local: $(check_targets)
-EXTRA_DIST=utils.c utils.h proflog.h \
- $(PLOG_TESTS_SRC) ptestrunner.pl \
+EXTRA_DIST=mono-profiler-log.h \
+ $(PLOG_TESTS_SRC) \
+ ptestrunner.pl \
$(suppression_DATA)
+++ /dev/null
-/*
- * decode.c: mprof-report program source: decode and analyze the log profiler data
- *
- * Authors:
- * Paolo Molaro (lupus@ximian.com)
- * Alex Rønne Petersen (alexrp@xamarin.com)
- *
- * Copyright 2010 Novell, Inc (http://www.novell.com)
- * Licensed under the MIT license. See LICENSE file in the project root for full license information.
- */
-
-/*
- * The Coverage XML output schema
- * <coverage>
- * <assembly/>
- * <class/>
- * <method>
- * <statement/>
- * </method>
- * </coverage>
- *
- * Elements:
- * <coverage> - The root element of the documentation. It can contain any number of
- * <assembly>, <class> or <method> elements.
- * Attributes:
- * - version: The version number for the file format - (eg: "0.3")
- * <assembly> - Contains data about assemblies. Has no child elements
- * Attributes:
- * - name: The name of the assembly - (eg: "System.Xml")
- * - guid: The GUID of the assembly
- * - filename: The filename of the assembly
- * - method-count: The number of methods in the assembly
- * - full: The number of fully covered methods
- * - partial: The number of partially covered methods
- * <class> - Contains data about classes. Has no child elements
- * Attributes:
- * - name: The name of the class
- * - method-count: The number of methods in the class
- * - full: The number of fully covered methods
- * - partial: The number of partially covered methods
- * <method> - Contains data about methods. Can contain any number of <statement> elements
- * Attributes:
- * - assembly: The name of the parent assembly
- * - class: The name of the parent class
- * - name: The name of the method, with all it's parameters
- * - filename: The name of the source file containing this method
- * - token
- * <statement> - Contains data about IL statements. Has no child elements
- * Attributes:
- * - offset: The offset of the statement in the IL code after the previous
- * statement's offset
- * - counter: 1 if the line was covered, 0 if it was not
- * - line: The line number in the parent method's file
- * - column: The column on the line
- */
-#include <config.h>
-#include "utils.c"
-#include "proflog.h"
-#include <string.h>
-#include <assert.h>
-#include <stdio.h>
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
-#include <malloc.h>
-#endif
-#include <unistd.h>
-#include <stdlib.h>
-#if defined (HAVE_SYS_ZLIB)
-#include <zlib.h>
-#endif
-#include <glib.h>
-#include <mono/metadata/profiler.h>
-#include <mono/metadata/object.h>
-#include <mono/metadata/debug-helpers.h>
-#include <mono/utils/mono-counters.h>
-
-#define HASH_SIZE 9371
-#define SMALL_HASH_SIZE 31
-
-#if defined(__native_client__) || defined(__native_client_codegen__)
-volatile int __nacl_thread_suspension_needed = 0;
-void __nacl_suspend_thread_if_needed() {}
-#endif
-
-static int debug = 0;
-static int collect_traces = 0;
-static int show_traces = 0;
-static int trace_max = 6;
-static int verbose = 0;
-static uintptr_t *tracked_objects = 0;
-static int num_tracked_objects = 0;
-static uintptr_t thread_filter = 0;
-static uint64_t find_size = 0;
-static const char* find_name = NULL;
-static uint64_t time_from = 0;
-static uint64_t time_to = 0xffffffffffffffffULL;
-static int use_time_filter = 0;
-static uint64_t startup_time = 0;
-static FILE* outfile = NULL;
-static FILE* coverage_outfile = NULL;
-
-static int32_t
-read_int16 (unsigned char *p)
-{
- int32_t value = *p++;
- value |= (*p++) << 8;
- return value;
-}
-
-static int32_t
-read_int32 (unsigned char *p)
-{
- int32_t value = *p++;
- value |= (*p++) << 8;
- value |= (*p++) << 16;
- value |= (uint32_t)(*p++) << 24;
- return value;
-}
-
-static int64_t
-read_int64 (unsigned char *p)
-{
- uint64_t value = *p++;
- value |= (*p++) << 8;
- value |= (*p++) << 16;
- value |= (uint64_t)(*p++) << 24;
- value |= (uint64_t)(*p++) << 32;
- value |= (uint64_t)(*p++) << 40;
- value |= (uint64_t)(*p++) << 48;
- value |= (uint64_t)(*p++) << 54;
- return value;
-}
-
-static char*
-pstrdup (const char *s)
-{
- int len = strlen (s) + 1;
- char *p = (char *)malloc (len);
- memcpy (p, s, len);
- return p;
-}
-
-typedef struct _CounterValue CounterValue;
-struct _CounterValue {
- uint64_t timestamp;
- unsigned char *buffer;
- CounterValue *next;
-};
-
-typedef struct _Counter Counter;
-struct _Counter {
- int index;
- const char *section;
- const char *name;
- int type;
- int unit;
- int variance;
- CounterValue *values;
- CounterValue *values_last;
-};
-
-typedef struct _CounterList CounterList;
-struct _CounterList {
- Counter *counter;
- CounterList *next;
-};
-
-typedef struct _CounterSection CounterSection;
-struct _CounterSection {
- const char *value;
- CounterList *counters;
- CounterList *counters_last;
- CounterSection *next;
-};
-
-typedef struct _CounterTimestamp CounterTimestamp;
-struct _CounterTimestamp {
- uint64_t value;
- CounterSection *sections;
- CounterSection *sections_last;
- CounterTimestamp *next;
-};
-
-static CounterList *counters = NULL;
-static CounterSection *counters_sections = NULL;
-static CounterTimestamp *counters_timestamps = NULL;
-
-enum {
- COUNTERS_SORT_TIME,
- COUNTERS_SORT_CATEGORY
-};
-
-static int counters_sort_mode = COUNTERS_SORT_TIME;
-
-static void
-add_counter_to_section (Counter *counter)
-{
- CounterSection *csection, *s;
- CounterList *clist;
-
- clist = (CounterList *)calloc (1, sizeof (CounterList));
- clist->counter = counter;
-
- for (csection = counters_sections; csection; csection = csection->next) {
- if (strcmp (csection->value, counter->section) == 0) {
- /* If section exist */
- if (!csection->counters)
- csection->counters = clist;
- else
- csection->counters_last->next = clist;
- csection->counters_last = clist;
- return;
- }
- }
-
- /* If section does not exist */
- csection = (CounterSection *)calloc (1, sizeof (CounterSection));
- csection->value = counter->section;
- csection->counters = clist;
- csection->counters_last = clist;
-
- if (!counters_sections) {
- counters_sections = csection;
- } else {
- s = counters_sections;
- while (s->next)
- s = s->next;
- s->next = csection;
- }
-}
-
-static void
-add_counter (const char *section, const char *name, int type, int unit, int variance, int index)
-{
- CounterList *list, *l;
- Counter *counter;
-
- for (list = counters; list; list = list->next)
- if (list->counter->index == index)
- return;
-
- counter = (Counter *)calloc (1, sizeof (Counter));
- counter->section = section;
- counter->name = name;
- counter->type = type;
- counter->unit = unit;
- counter->variance = variance;
- counter->index = index;
-
- list = (CounterList *)calloc (1, sizeof (CounterList));
- list->counter = counter;
-
- if (!counters) {
- counters = list;
- } else {
- l = counters;
- while (l->next)
- l = l->next;
- l->next = list;
- }
-
- if (counters_sort_mode == COUNTERS_SORT_CATEGORY || !verbose)
- add_counter_to_section (counter);
-}
-
-static void
-add_counter_to_timestamp (uint64_t timestamp, Counter *counter)
-{
- CounterTimestamp *ctimestamp, *t;
- CounterSection *csection;
- CounterList *clist;
-
- clist = (CounterList *)calloc (1, sizeof (CounterList));
- clist->counter = counter;
-
- for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
- if (ctimestamp->value == timestamp) {
- for (csection = ctimestamp->sections; csection; csection = csection->next) {
- if (strcmp (csection->value, counter->section) == 0) {
- /* if timestamp exist and section exist */
- if (!csection->counters)
- csection->counters = clist;
- else
- csection->counters_last->next = clist;
- csection->counters_last = clist;
- return;
- }
- }
-
- /* if timestamp exist and section does not exist */
- csection = (CounterSection *)calloc (1, sizeof (CounterSection));
- csection->value = counter->section;
- csection->counters = clist;
- csection->counters_last = clist;
-
- if (!ctimestamp->sections)
- ctimestamp->sections = csection;
- else
- ctimestamp->sections_last->next = csection;
- ctimestamp->sections_last = csection;
- return;
- }
- }
-
- /* If timestamp do not exist and section does not exist */
- csection = (CounterSection *)calloc (1, sizeof (CounterSection));
- csection->value = counter->section;
- csection->counters = clist;
- csection->counters_last = clist;
-
- ctimestamp = (CounterTimestamp *)calloc (1, sizeof (CounterTimestamp));
- ctimestamp->value = timestamp;
- ctimestamp->sections = csection;
- ctimestamp->sections_last = csection;
-
- if (!counters_timestamps) {
- counters_timestamps = ctimestamp;
- } else {
- t = counters_timestamps;
- while (t->next)
- t = t->next;
- t->next = ctimestamp;
- }
-}
-
-static void
-add_counter_value (int index, CounterValue *value)
-{
- CounterList *list;
-
- for (list = counters; list; list = list->next) {
- if (list->counter->index == index) {
- if (!list->counter->values)
- list->counter->values = value;
- else
- list->counter->values_last->next = value;
- list->counter->values_last = value;
-
- if (counters_sort_mode == COUNTERS_SORT_TIME)
- add_counter_to_timestamp (value->timestamp, list->counter);
-
- return;
- }
- }
-}
-
-static const char*
-section_name (int section)
-{
- switch (section) {
- case MONO_COUNTER_JIT: return "Mono JIT";
- case MONO_COUNTER_GC: return "Mono GC";
- case MONO_COUNTER_METADATA: return "Mono Metadata";
- case MONO_COUNTER_GENERICS: return "Mono Generics";
- case MONO_COUNTER_SECURITY: return "Mono Security";
- case MONO_COUNTER_RUNTIME: return "Mono Runtime";
- case MONO_COUNTER_SYSTEM: return "Mono System";
- case MONO_COUNTER_PROFILER: return "Mono Profiler";
- default: return "<unknown>";
- }
-}
-
-static const char*
-type_name (int type)
-{
- switch (type) {
- case MONO_COUNTER_INT: return "Int";
- case MONO_COUNTER_UINT: return "UInt";
- case MONO_COUNTER_WORD: return "Word";
- case MONO_COUNTER_LONG: return "Long";
- case MONO_COUNTER_ULONG: return "ULong";
- case MONO_COUNTER_DOUBLE: return "Double";
- case MONO_COUNTER_STRING: return "String";
- case MONO_COUNTER_TIME_INTERVAL: return "Time Interval";
- default: return "<unknown>";
- }
-}
-
-static const char*
-unit_name (int unit)
-{
- switch (unit) {
- case MONO_COUNTER_RAW: return "Raw";
- case MONO_COUNTER_BYTES: return "Bytes";
- case MONO_COUNTER_TIME: return "Time";
- case MONO_COUNTER_COUNT: return "Count";
- case MONO_COUNTER_PERCENTAGE: return "Percentage";
- default: return "<unknown>";
- }
-}
-
-static const char*
-variance_name (int variance)
-{
- switch (variance) {
- case MONO_COUNTER_MONOTONIC: return "Monotonic";
- case MONO_COUNTER_CONSTANT: return "Constant";
- case MONO_COUNTER_VARIABLE: return "Variable";
- default: return "<unknown>";
- }
-}
-
-static void
-dump_counters_value (Counter *counter, const char *key_format, const char *key, void *value)
-{
- char format[32];
-
- if (value == NULL) {
- snprintf (format, sizeof (format), "%s : %%s\n", key_format);
- fprintf (outfile, format, key, "<null>");
- } else {
- switch (counter->type) {
- case MONO_COUNTER_INT:
-#if SIZEOF_VOID_P == 4
- case MONO_COUNTER_WORD:
-#endif
- snprintf (format, sizeof (format), "%s : %%d\n", key_format);
- fprintf (outfile, format, key, *(int32_t*)value);
- break;
- case MONO_COUNTER_UINT:
- snprintf (format, sizeof (format), "%s : %%u\n", key_format);
- fprintf (outfile, format, key, *(uint32_t*)value);
- break;
- case MONO_COUNTER_LONG:
-#if SIZEOF_VOID_P == 8
- case MONO_COUNTER_WORD:
-#endif
- case MONO_COUNTER_TIME_INTERVAL:
- if (counter->type == MONO_COUNTER_LONG && counter->unit == MONO_COUNTER_TIME) {
- snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
- fprintf (outfile, format, key, (double)*(int64_t*)value / 10000.0);
- } else if (counter->type == MONO_COUNTER_TIME_INTERVAL) {
- snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
- fprintf (outfile, format, key, (double)*(int64_t*)value / 1000.0);
- } else {
- snprintf (format, sizeof (format), "%s : %%u\n", key_format);
- fprintf (outfile, format, key, *(int64_t*)value);
- }
- break;
- case MONO_COUNTER_ULONG:
- snprintf (format, sizeof (format), "%s : %%llu\n", key_format);
- fprintf (outfile, format, key, *(uint64_t*)value);
- break;
- case MONO_COUNTER_DOUBLE:
- snprintf (format, sizeof (format), "%s : %%f\n", key_format);
- fprintf (outfile, format, key, *(double*)value);
- break;
- case MONO_COUNTER_STRING:
- snprintf (format, sizeof (format), "%s : %%s\n", key_format);
- fprintf (outfile, format, key, *(char*)value);
- break;
- }
- }
-}
-
-static void
-dump_counters (void)
-{
- Counter *counter;
- CounterValue *cvalue;
- CounterTimestamp *ctimestamp;
- CounterSection *csection;
- CounterList *clist;
- char strtimestamp[17];
- int i, section_printed;
-
- fprintf (outfile, "\nCounters:\n");
-
- if (!verbose) {
- char counters_to_print[][64] = {
- "Methods from AOT",
- "Methods JITted using mono JIT",
- "Methods JITted using LLVM",
- "Total time spent JITting (sec)",
- "User Time",
- "System Time",
- "Total Time",
- "Working Set",
- "Private Bytes",
- "Virtual Bytes",
- "Page Faults",
- "CPU Load Average - 1min",
- "CPU Load Average - 5min",
- "CPU Load Average - 15min",
- ""
- };
-
- for (csection = counters_sections; csection; csection = csection->next) {
- section_printed = 0;
-
- for (clist = csection->counters; clist; clist = clist->next) {
- counter = clist->counter;
- if (!counter->values_last)
- continue;
-
- for (i = 0; counters_to_print [i][0] != 0; i++) {
- if (strcmp (counters_to_print [i], counter->name) == 0) {
- if (!section_printed) {
- fprintf (outfile, "\t%s:\n", csection->value);
- section_printed = 1;
- }
-
- dump_counters_value (counter, "\t\t%-30s", counter->name, counter->values_last->buffer);
- break;
- }
- }
- }
- }
- } else if (counters_sort_mode == COUNTERS_SORT_TIME) {
- for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
- fprintf (outfile, "\t%llu:%02llu:%02llu:%02llu.%03llu:\n",
- (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 / 24 % 1000),
- (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 % 24),
- (unsigned long long) (ctimestamp->value / 1000 / 60 % 60),
- (unsigned long long) (ctimestamp->value / 1000 % 60),
- (unsigned long long) (ctimestamp->value % 1000));
-
- for (csection = ctimestamp->sections; csection; csection = csection->next) {
- fprintf (outfile, "\t\t%s:\n", csection->value);
-
- for (clist = csection->counters; clist; clist = clist->next) {
- counter = clist->counter;
- for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
- if (cvalue->timestamp != ctimestamp->value)
- continue;
-
- dump_counters_value (counter, "\t\t\t%-30s", counter->name, cvalue->buffer);
- }
- }
- }
- }
- } else if (counters_sort_mode == COUNTERS_SORT_CATEGORY) {
- for (csection = counters_sections; csection; csection = csection->next) {
- fprintf (outfile, "\t%s:\n", csection->value);
-
- for (clist = csection->counters; clist; clist = clist->next) {
- counter = clist->counter;
- fprintf (outfile, "\t\t%s: [type: %s, unit: %s, variance: %s]\n",
- counter->name, type_name (counter->type), unit_name (counter->unit), variance_name (counter->variance));
-
- for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
- snprintf (strtimestamp, sizeof (strtimestamp), "%llu:%02llu:%02llu:%02llu.%03llu",
- (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 / 24 % 1000),
- (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 % 24),
- (unsigned long long) (cvalue->timestamp / 1000 / 60 % 60),
- (unsigned long long) (cvalue->timestamp / 1000 % 60),
- (unsigned long long) (cvalue->timestamp % 1000));
-
- dump_counters_value (counter, "\t\t\t%s", strtimestamp, cvalue->buffer);
- }
- }
- }
- }
-}
-
-static int num_images;
-typedef struct _ImageDesc ImageDesc;
-struct _ImageDesc {
- ImageDesc *next;
- intptr_t image;
- char *filename;
-};
-
-static ImageDesc* image_hash [SMALL_HASH_SIZE] = {0};
-
-static void
-add_image (intptr_t image, char *name)
-{
- int slot = ((image >> 2) & 0xffff) % SMALL_HASH_SIZE;
- ImageDesc *cd = (ImageDesc *)malloc (sizeof (ImageDesc));
- cd->image = image;
- cd->filename = pstrdup (name);
- cd->next = image_hash [slot];
- image_hash [slot] = cd;
- num_images++;
-}
-
-static int num_assemblies;
-
-typedef struct _AssemblyDesc AssemblyDesc;
-struct _AssemblyDesc {
- AssemblyDesc *next;
- intptr_t assembly;
- char *asmname;
-};
-
-static AssemblyDesc* assembly_hash [SMALL_HASH_SIZE] = {0};
-
-static void
-add_assembly (intptr_t assembly, char *name)
-{
- int slot = ((assembly >> 2) & 0xffff) % SMALL_HASH_SIZE;
- AssemblyDesc *cd = (AssemblyDesc *)malloc (sizeof (AssemblyDesc));
- cd->assembly = assembly;
- cd->asmname = pstrdup (name);
- cd->next = assembly_hash [slot];
- assembly_hash [slot] = cd;
- num_assemblies++;
-}
-
-typedef struct _BackTrace BackTrace;
-typedef struct {
- uint64_t count;
- BackTrace *bt;
-} CallContext;
-
-typedef struct {
- int count;
- int size;
- CallContext *traces;
-} TraceDesc;
-
-typedef struct _ClassDesc ClassDesc;
-struct _ClassDesc {
- ClassDesc *next;
- intptr_t klass;
- char *name;
- intptr_t allocs;
- uint64_t alloc_size;
- TraceDesc traces;
-};
-
-static ClassDesc* class_hash [HASH_SIZE] = {0};
-static int num_classes = 0;
-
-static ClassDesc*
-add_class (intptr_t klass, const char *name)
-{
- int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
- ClassDesc *cd;
- cd = class_hash [slot];
- while (cd && cd->klass != klass)
- cd = cd->next;
- /* we resolved an unknown class (unless we had the code unloaded) */
- if (cd) {
- /*printf ("resolved unknown: %s\n", name);*/
- g_free (cd->name);
- cd->name = pstrdup (name);
- return cd;
- }
- cd = (ClassDesc *)calloc (sizeof (ClassDesc), 1);
- cd->klass = klass;
- cd->name = pstrdup (name);
- cd->next = class_hash [slot];
- cd->allocs = 0;
- cd->alloc_size = 0;
- cd->traces.count = 0;
- cd->traces.size = 0;
- cd->traces.traces = NULL;
- class_hash [slot] = cd;
- num_classes++;
- return cd;
-}
-
-static ClassDesc *
-lookup_class (intptr_t klass)
-{
- int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
- ClassDesc *cd = class_hash [slot];
- while (cd && cd->klass != klass)
- cd = cd->next;
- if (!cd) {
- char buf [128];
- snprintf (buf, sizeof (buf), "unresolved class %p", (void*)klass);
- return add_class (klass, buf);
- }
- return cd;
-}
-
-typedef struct _MethodDesc MethodDesc;
-struct _MethodDesc {
- MethodDesc *next;
- intptr_t method;
- char *name;
- intptr_t code;
- int len;
- int recurse_count;
- int sample_hits;
- int ignore_jit; /* when this is set, we collect the metadata but don't count this method fot jit time and code size, when filtering events */
- uint64_t calls;
- uint64_t total_time;
- uint64_t callee_time;
- uint64_t self_time;
- TraceDesc traces;
-};
-
-static MethodDesc* method_hash [HASH_SIZE] = {0};
-static int num_methods = 0;
-
-static MethodDesc*
-add_method (intptr_t method, const char *name, intptr_t code, int len)
-{
- int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
- MethodDesc *cd;
- cd = method_hash [slot];
- while (cd && cd->method != method)
- cd = cd->next;
- /* we resolved an unknown method (unless we had the code unloaded) */
- if (cd) {
- cd->code = code;
- cd->len = len;
- /*printf ("resolved unknown: %s\n", name);*/
- g_free (cd->name);
- cd->name = pstrdup (name);
- return cd;
- }
- cd = (MethodDesc *)calloc (sizeof (MethodDesc), 1);
- cd->method = method;
- cd->name = pstrdup (name);
- cd->code = code;
- cd->len = len;
- cd->calls = 0;
- cd->total_time = 0;
- cd->traces.count = 0;
- cd->traces.size = 0;
- cd->traces.traces = NULL;
- cd->next = method_hash [slot];
- method_hash [slot] = cd;
- num_methods++;
- return cd;
-}
-
-static MethodDesc *
-lookup_method (intptr_t method)
-{
- int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
- MethodDesc *cd = method_hash [slot];
- while (cd && cd->method != method)
- cd = cd->next;
- if (!cd) {
- char buf [128];
- snprintf (buf, sizeof (buf), "unknown method %p", (void*)method);
- return add_method (method, buf, 0, 0);
- }
- return cd;
-}
-
-static int num_stat_samples = 0;
-static int size_stat_samples = 0;
-uintptr_t *stat_samples = NULL;
-int *stat_sample_desc = NULL;
-
-static void
-add_stat_sample (int type, uintptr_t ip) {
- if (num_stat_samples == size_stat_samples) {
- size_stat_samples *= 2;
- if (!size_stat_samples)
- size_stat_samples = 32;
- stat_samples = (uintptr_t *)realloc (stat_samples, size_stat_samples * sizeof (uintptr_t));
- stat_sample_desc = (int *)realloc (stat_sample_desc, size_stat_samples * sizeof (int));
- }
- stat_samples [num_stat_samples] = ip;
- stat_sample_desc [num_stat_samples++] = type;
-}
-
-static MethodDesc*
-lookup_method_by_ip (uintptr_t ip)
-{
- int i;
- MethodDesc* m;
- /* dumb */
- for (i = 0; i < HASH_SIZE; ++i) {
- m = method_hash [i];
- while (m) {
- //printf ("checking %p against %p-%p\n", (void*)ip, (void*)(m->code), (void*)(m->code + m->len));
- if (ip >= (uintptr_t)m->code && ip < (uintptr_t)m->code + m->len) {
- return m;
- }
- m = m->next;
- }
- }
- return NULL;
-}
-
-static int
-compare_method_samples (const void *a, const void *b)
-{
- MethodDesc *const *A = (MethodDesc *const *)a;
- MethodDesc *const *B = (MethodDesc *const *)b;
- if ((*A)->sample_hits == (*B)->sample_hits)
- return 0;
- if ((*B)->sample_hits < (*A)->sample_hits)
- return -1;
- return 1;
-}
-
-typedef struct _UnmanagedSymbol UnmanagedSymbol;
-struct _UnmanagedSymbol {
- UnmanagedSymbol *parent;
- char *name;
- int is_binary;
- uintptr_t addr;
- uintptr_t size;
- uintptr_t sample_hits;
-};
-
-static UnmanagedSymbol **usymbols = NULL;
-static int usymbols_size = 0;
-static int usymbols_num = 0;
-
-static int
-compare_usymbol_addr (const void *a, const void *b)
-{
- UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
- UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
- if ((*B)->addr == (*A)->addr)
- return 0;
- if ((*B)->addr > (*A)->addr)
- return -1;
- return 1;
-}
-
-static int
-compare_usymbol_samples (const void *a, const void *b)
-{
- UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
- UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
- if ((*B)->sample_hits == (*A)->sample_hits)
- return 0;
- if ((*B)->sample_hits < (*A)->sample_hits)
- return -1;
- return 1;
-}
-
-static void
-add_unmanaged_symbol (uintptr_t addr, char *name, uintptr_t size)
-{
- UnmanagedSymbol *sym;
- if (usymbols_num == usymbols_size) {
- int new_size = usymbols_size * 2;
- if (!new_size)
- new_size = 16;
- usymbols = (UnmanagedSymbol **)realloc (usymbols, sizeof (void*) * new_size);
- usymbols_size = new_size;
- }
- sym = (UnmanagedSymbol *)calloc (sizeof (UnmanagedSymbol), 1);
- sym->addr = addr;
- sym->name = name;
- sym->size = size;
- usymbols [usymbols_num++] = sym;
-}
-
-/* only valid after the symbols are sorted */
-static UnmanagedSymbol*
-lookup_unmanaged_symbol (uintptr_t addr)
-{
- int r = usymbols_num - 1;
- int l = 0;
- UnmanagedSymbol *sym;
- int last_best = -1;
- while (r >= l) {
- int m = (l + r) / 2;
- sym = usymbols [m];
- if (addr == sym->addr)
- return sym;
- if (addr < sym->addr) {
- r = m - 1;
- } else if (addr > sym->addr) {
- l = m + 1;
- last_best = m;
- }
- }
- if (last_best >= 0 && (addr - usymbols [last_best]->addr) < 4096)
- return usymbols [last_best];
- return NULL;
-}
-
-/* we use the same structure for binaries */
-static UnmanagedSymbol **ubinaries = NULL;
-static int ubinaries_size = 0;
-static int ubinaries_num = 0;
-
-static void
-add_unmanaged_binary (uintptr_t addr, char *name, uintptr_t size)
-{
- UnmanagedSymbol *sym;
- if (ubinaries_num == ubinaries_size) {
- int new_size = ubinaries_size * 2;
- if (!new_size)
- new_size = 16;
- ubinaries = (UnmanagedSymbol **)realloc (ubinaries, sizeof (void*) * new_size);
- ubinaries_size = new_size;
- }
- sym = (UnmanagedSymbol *)calloc (sizeof (UnmanagedSymbol), 1);
- sym->addr = addr;
- sym->name = name;
- sym->size = size;
- sym->is_binary = 1;
- ubinaries [ubinaries_num++] = sym;
-}
-
-static UnmanagedSymbol*
-lookup_unmanaged_binary (uintptr_t addr)
-{
- int i;
- for (i = 0; i < ubinaries_num; ++i) {
- UnmanagedSymbol *ubin = ubinaries [i];
- if (addr >= ubin->addr && addr < ubin->addr + ubin->size) {
- return ubin;
- }
- }
- return NULL;
-}
-
-static const char*
-sample_type_name (int type)
-{
- switch (type) {
- case SAMPLE_CYCLES: return "cycles";
- case SAMPLE_INSTRUCTIONS: return "instructions retired";
- case SAMPLE_CACHE_MISSES: return "cache misses";
- case SAMPLE_CACHE_REFS: return "cache references";
- case SAMPLE_BRANCHES: return "executed branches";
- case SAMPLE_BRANCH_MISSES: return "unpredicted branches";
- }
- return "unknown";
-}
-
-static void
-set_usym_parent (UnmanagedSymbol** cachedus, int count)
-{
- int i;
- for (i = 0; i < count; ++i) {
- UnmanagedSymbol *ubin = lookup_unmanaged_binary (cachedus [i]->addr);
- if (ubin == cachedus [i])
- continue;
- cachedus [i]->parent = ubin;
- }
-}
-
-static void
-print_usym (UnmanagedSymbol* um)
-{
- if (um->parent)
- fprintf (outfile, "\t%6zd %6.2f %-36s in %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name, um->parent->name);
- else
- fprintf (outfile, "\t%6zd %6.2f %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name);
-}
-
-static int
-sym_percent (uintptr_t sample_hits)
-{
- double pc;
- if (verbose)
- return 1;
- pc = sample_hits*100.0/num_stat_samples;
- return pc >= 0.1;
-}
-
-static void
-dump_samples (void)
-{
- int i, u;
- int count = 0, msize = 0;
- int unmanaged_hits = 0;
- int unresolved_hits = 0;
- MethodDesc** cachedm = NULL;
- int ucount = 0, usize = 0;
- UnmanagedSymbol** cachedus = NULL;
- if (!num_stat_samples)
- return;
- qsort (usymbols, usymbols_num, sizeof (UnmanagedSymbol*), compare_usymbol_addr);
- for (i = 0; i < num_stat_samples; ++i) {
- MethodDesc *m = lookup_method_by_ip (stat_samples [i]);
- if (m) {
- if (!m->sample_hits) {
- if (count == msize) {
- msize *= 2;
- if (!msize)
- msize = 4;
- cachedm = (MethodDesc **)realloc (cachedm, sizeof (void*) * msize);
- }
- cachedm [count++] = m;
- }
- m->sample_hits++;
- } else {
- UnmanagedSymbol *usym = lookup_unmanaged_symbol (stat_samples [i]);
- if (!usym) {
- unresolved_hits++;
- //printf ("unmanaged hit at %p\n", (void*)stat_samples [i]);
- usym = lookup_unmanaged_binary (stat_samples [i]);
- }
- if (usym) {
- if (!usym->sample_hits) {
- if (ucount == usize) {
- usize *= 2;
- if (!usize)
- usize = 4;
- cachedus = (UnmanagedSymbol **)realloc (cachedus, sizeof (void*) * usize);
- }
- cachedus [ucount++] = usym;
- }
- usym->sample_hits++;
- }
- unmanaged_hits++;
- }
- }
- qsort (cachedm, count, sizeof (MethodDesc*), compare_method_samples);
- qsort (cachedus, ucount, sizeof (UnmanagedSymbol*), compare_usymbol_samples);
- set_usym_parent (cachedus, ucount);
- fprintf (outfile, "\nStatistical samples summary\n");
- fprintf (outfile, "\tSample type: %s\n", sample_type_name (stat_sample_desc [0]));
- fprintf (outfile, "\tUnmanaged hits: %6d (%4.1f%%)\n", unmanaged_hits, (100.0*unmanaged_hits)/num_stat_samples);
- fprintf (outfile, "\tManaged hits: %6d (%4.1f%%)\n", num_stat_samples - unmanaged_hits, (100.0*(num_stat_samples-unmanaged_hits))/num_stat_samples);
- fprintf (outfile, "\tUnresolved hits: %6d (%4.1f%%)\n", unresolved_hits, (100.0*unresolved_hits)/num_stat_samples);
- fprintf (outfile, "\t%6s %6s %s\n", "Hits", "%", "Method name");
- i = 0;
- u = 0;
- while (i < count || u < ucount) {
- if (i < count) {
- MethodDesc *m = cachedm [i];
- if (u < ucount) {
- UnmanagedSymbol *um = cachedus [u];
- if (um->sample_hits > m->sample_hits) {
- if (!sym_percent (um->sample_hits))
- break;
- print_usym (um);
- u++;
- continue;
- }
- }
- if (!sym_percent (m->sample_hits))
- break;
- fprintf (outfile, "\t%6d %6.2f %s\n", m->sample_hits, m->sample_hits*100.0/num_stat_samples, m->name);
- i++;
- continue;
- }
- if (u < ucount) {
- UnmanagedSymbol *um = cachedus [u];
- if (!sym_percent (um->sample_hits))
- break;
- print_usym (um);
- u++;
- continue;
- }
- }
-}
-
-typedef struct _HeapClassDesc HeapClassDesc;
-typedef struct {
- HeapClassDesc *klass;
- uint64_t count;
-} HeapClassRevRef;
-
-struct _HeapClassDesc {
- ClassDesc *klass;
- int64_t count;
- int64_t total_size;
- HeapClassRevRef *rev_hash;
- int rev_hash_size;
- int rev_count;
- uintptr_t pinned_references;
- uintptr_t root_references;
-};
-
-static int
-add_rev_class_hashed (HeapClassRevRef *rev_hash, uintptr_t size, HeapClassDesc *hklass, uint64_t value)
-{
- uintptr_t i;
- uintptr_t start_pos;
- start_pos = (hklass->klass->klass >> 2) % size;
- assert (start_pos < size);
- i = start_pos;
- do {
- if (rev_hash [i].klass == hklass) {
- rev_hash [i].count += value;
- return 0;
- } else if (!rev_hash [i].klass) {
- rev_hash [i].klass = hklass;
- rev_hash [i].count += value;
- start_pos = 0;
- for (i = 0; i < size; ++i)
- if (rev_hash [i].klass && rev_hash [i].klass->klass == hklass->klass)
- start_pos ++;
- assert (start_pos == 1);
- return 1;
- }
- /* wrap around */
- if (++i == size)
- i = 0;
- } while (i != start_pos);
- /* should not happen */
- printf ("failed revref store\n");
- return 0;
-}
-
-static void
-add_heap_class_rev (HeapClassDesc *from, HeapClassDesc *to)
-{
- uintptr_t i;
- if (to->rev_count * 2 >= to->rev_hash_size) {
- HeapClassRevRef *n;
- uintptr_t old_size = to->rev_hash_size;
- to->rev_hash_size *= 2;
- if (to->rev_hash_size == 0)
- to->rev_hash_size = 4;
- n = (HeapClassRevRef *)calloc (sizeof (HeapClassRevRef) * to->rev_hash_size, 1);
- for (i = 0; i < old_size; ++i) {
- if (to->rev_hash [i].klass)
- add_rev_class_hashed (n, to->rev_hash_size, to->rev_hash [i].klass, to->rev_hash [i].count);
- }
- if (to->rev_hash)
- g_free (to->rev_hash);
- to->rev_hash = n;
- }
- to->rev_count += add_rev_class_hashed (to->rev_hash, to->rev_hash_size, from, 1);
-}
-
-typedef struct {
- uintptr_t objaddr;
- HeapClassDesc *hklass;
- uintptr_t num_refs;
- uintptr_t refs [0];
-} HeapObjectDesc;
-
-typedef struct _HeapShot HeapShot;
-struct _HeapShot {
- HeapShot *next;
- uint64_t timestamp;
- int class_count;
- int hash_size;
- HeapClassDesc **class_hash;
- HeapClassDesc **sorted;
- HeapObjectDesc **objects_hash;
- uintptr_t objects_count;
- uintptr_t objects_hash_size;
- uintptr_t num_roots;
- uintptr_t *roots;
- uintptr_t *roots_extra;
- int *roots_types;
-};
-
-static HeapShot *heap_shots = NULL;
-static int num_heap_shots = 0;
-
-static HeapShot*
-new_heap_shot (uint64_t timestamp)
-{
- HeapShot *hs = (HeapShot *)calloc (sizeof (HeapShot), 1);
- hs->hash_size = 4;
- hs->class_hash = (HeapClassDesc **)calloc (sizeof (void*), hs->hash_size);
- hs->timestamp = timestamp;
- num_heap_shots++;
- hs->next = heap_shots;
- heap_shots = hs;
- return hs;
-}
-
-static HeapClassDesc*
-heap_class_lookup (HeapShot *hs, ClassDesc *klass)
-{
- int i;
- unsigned int start_pos;
- start_pos = ((uintptr_t)klass->klass >> 2) % hs->hash_size;
- i = start_pos;
- do {
- HeapClassDesc* cd = hs->class_hash [i];
- if (!cd)
- return NULL;
- if (cd->klass == klass)
- return cd;
- /* wrap around */
- if (++i == hs->hash_size)
- i = 0;
- } while (i != start_pos);
- return NULL;
-}
-
-static int
-add_heap_hashed (HeapClassDesc **hash, HeapClassDesc **retv, uintptr_t hsize, ClassDesc *klass, uint64_t size, uint64_t count)
-{
- uintptr_t i;
- uintptr_t start_pos;
- start_pos = ((uintptr_t)klass->klass >> 2) % hsize;
- i = start_pos;
- do {
- if (hash [i] && hash [i]->klass == klass) {
- hash [i]->total_size += size;
- hash [i]->count += count;
- *retv = hash [i];
- return 0;
- } else if (!hash [i]) {
- if (*retv) {
- hash [i] = *retv;
- return 1;
- }
- hash [i] = (HeapClassDesc *)calloc (sizeof (HeapClassDesc), 1);
- hash [i]->klass = klass;
- hash [i]->total_size += size;
- hash [i]->count += count;
- *retv = hash [i];
- return 1;
- }
- /* wrap around */
- if (++i == hsize)
- i = 0;
- } while (i != start_pos);
- /* should not happen */
- printf ("failed heap class store\n");
- return 0;
-}
-
-static HeapClassDesc*
-add_heap_shot_class (HeapShot *hs, ClassDesc *klass, uint64_t size)
-{
- HeapClassDesc *res;
- int i;
- if (hs->class_count * 2 >= hs->hash_size) {
- HeapClassDesc **n;
- int old_size = hs->hash_size;
- hs->hash_size *= 2;
- if (hs->hash_size == 0)
- hs->hash_size = 4;
- n = (HeapClassDesc **)calloc (sizeof (void*) * hs->hash_size, 1);
- for (i = 0; i < old_size; ++i) {
- res = hs->class_hash [i];
- if (hs->class_hash [i])
- add_heap_hashed (n, &res, hs->hash_size, hs->class_hash [i]->klass, hs->class_hash [i]->total_size, hs->class_hash [i]->count);
- }
- if (hs->class_hash)
- g_free (hs->class_hash);
- hs->class_hash = n;
- }
- res = NULL;
- hs->class_count += add_heap_hashed (hs->class_hash, &res, hs->hash_size, klass, size, 1);
- //if (res->count == 1)
- // printf ("added heap class: %s\n", res->klass->name);
- return res;
-}
-
-static HeapObjectDesc*
-alloc_heap_obj (uintptr_t objaddr, HeapClassDesc *hklass, uintptr_t num_refs)
-{
- HeapObjectDesc* ho = (HeapObjectDesc *)calloc (sizeof (HeapObjectDesc) + num_refs * sizeof (uintptr_t), 1);
- ho->objaddr = objaddr;
- ho->hklass = hklass;
- ho->num_refs = num_refs;
- return ho;
-}
-
-static uintptr_t
-heap_shot_find_obj_slot (HeapShot *hs, uintptr_t objaddr)
-{
- uintptr_t i;
- uintptr_t start_pos;
- HeapObjectDesc **hash = hs->objects_hash;
- start_pos = ((uintptr_t)objaddr >> 3) % hs->objects_hash_size;
- i = start_pos;
- do {
- if (hash [i] && hash [i]->objaddr == objaddr) {
- return i;
- } else if (!hash [i]) {
- break; /* fail */
- }
- /* wrap around */
- if (++i == hs->objects_hash_size)
- i = 0;
- } while (i != start_pos);
- /* should not happen */
- //printf ("failed heap obj slot\n");
- return -1;
-}
-
-static HeapObjectDesc*
-heap_shot_obj_add_refs (HeapShot *hs, uintptr_t objaddr, uintptr_t num, uintptr_t *ref_offset)
-{
- HeapObjectDesc **hash = hs->objects_hash;
- uintptr_t i = heap_shot_find_obj_slot (hs, objaddr);
- if (i >= 0) {
- HeapObjectDesc* ho = alloc_heap_obj (objaddr, hash [i]->hklass, hash [i]->num_refs + num);
- *ref_offset = hash [i]->num_refs;
- memcpy (ho->refs, hash [i]->refs, hash [i]->num_refs * sizeof (uintptr_t));
- g_free (hash [i]);
- hash [i] = ho;
- return ho;
- }
- /* should not happen */
- printf ("failed heap obj update\n");
- return NULL;
-
-}
-
-static uintptr_t
-add_heap_hashed_obj (HeapObjectDesc **hash, uintptr_t hsize, HeapObjectDesc *obj)
-{
- uintptr_t i;
- uintptr_t start_pos;
- start_pos = ((uintptr_t)obj->objaddr >> 3) % hsize;
- i = start_pos;
- do {
- if (hash [i] && hash [i]->objaddr == obj->objaddr) {
- printf ("duplicate object!\n");
- return 0;
- } else if (!hash [i]) {
- hash [i] = obj;
- return 1;
- }
- /* wrap around */
- if (++i == hsize)
- i = 0;
- } while (i != start_pos);
- /* should not happen */
- printf ("failed heap obj store\n");
- return 0;
-}
-
-static void
-add_heap_shot_obj (HeapShot *hs, HeapObjectDesc *obj)
-{
- uintptr_t i;
- if (hs->objects_count * 2 >= hs->objects_hash_size) {
- HeapObjectDesc **n;
- uintptr_t old_size = hs->objects_hash_size;
- hs->objects_hash_size *= 2;
- if (hs->objects_hash_size == 0)
- hs->objects_hash_size = 4;
- n = (HeapObjectDesc **)calloc (sizeof (void*) * hs->objects_hash_size, 1);
- for (i = 0; i < old_size; ++i) {
- if (hs->objects_hash [i])
- add_heap_hashed_obj (n, hs->objects_hash_size, hs->objects_hash [i]);
- }
- if (hs->objects_hash)
- g_free (hs->objects_hash);
- hs->objects_hash = n;
- }
- hs->objects_count += add_heap_hashed_obj (hs->objects_hash, hs->objects_hash_size, obj);
-}
-
-static void
-heap_shot_resolve_reverse_refs (HeapShot *hs)
-{
- uintptr_t i;
- for (i = 0; i < hs->objects_hash_size; ++i) {
- uintptr_t r;
- HeapObjectDesc *ho = hs->objects_hash [i];
- if (!ho)
- continue;
- for (r = 0; r < ho->num_refs; ++r) {
- uintptr_t oi = heap_shot_find_obj_slot (hs, ho->refs [r]);
- add_heap_class_rev (ho->hklass, hs->objects_hash [oi]->hklass);
- }
- }
-}
-
-#define MARK_GRAY 1
-#define MARK_BLACK 2
-
-static void
-heap_shot_mark_objects (HeapShot *hs)
-{
- uintptr_t i, oi, r;
- unsigned char *marks;
- HeapObjectDesc *obj, *ref;
- int marked_some;
- uintptr_t num_marked = 0, num_unmarked;
- for (i = 0; i < hs->num_roots; ++i) {
- HeapClassDesc *cd;
- oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
- if (oi == -1) {
- continue;
- }
- obj = hs->objects_hash [oi];
- cd = obj->hklass;
- if (hs->roots_types [i] & MONO_PROFILE_GC_ROOT_PINNING)
- cd->pinned_references++;
- cd->root_references++;
- }
- if (!debug)
- return;
- /* consistency checks: it seems not all the objects are walked in the heap in some cases */
- marks = (unsigned char *)calloc (hs->objects_hash_size, 1);
- if (!marks)
- return;
- for (i = 0; i < hs->num_roots; ++i) {
- oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
- if (oi == -1) {
- fprintf (outfile, "root type 0x%x for obj %p (%s) not found in heap\n", hs->roots_types [i], (void*)hs->roots [i], lookup_class (hs->roots_extra [i])->name);
- continue;
- }
- obj = hs->objects_hash [oi];
- if (!marks [oi]) {
- marks [oi] = obj->num_refs? MARK_GRAY: MARK_BLACK;
- num_marked++;
- }
- }
- marked_some = 1;
- while (marked_some) {
- marked_some = 0;
- for (i = 0; i < hs->objects_hash_size; ++i) {
- if (marks [i] != MARK_GRAY)
- continue;
- marks [i] = MARK_BLACK;
- obj = hs->objects_hash [i];
- for (r = 0; r < obj->num_refs; ++r) {
- oi = heap_shot_find_obj_slot (hs, obj->refs [r]);
- if (oi == -1) {
- fprintf (outfile, "referenced obj %p not found in heap\n", (void*)obj->refs [r]);
- continue;
- }
- ref = hs->objects_hash [oi];
- if (!marks [oi]) {
- marks [oi] = ref->num_refs? MARK_GRAY: MARK_BLACK;
- }
- }
- marked_some++;
- }
- }
-
- num_unmarked = 0;
- for (i = 0; i < hs->objects_hash_size; ++i) {
- if (hs->objects_hash [i] && !marks [i]) {
- num_unmarked++;
- fprintf (outfile, "object %p (%s) unmarked\n", (void*)hs->objects_hash [i], hs->objects_hash [i]->hklass->klass->name);
- }
- }
- fprintf (outfile, "Total unmarked: %zd/%zd\n", num_unmarked, hs->objects_count);
- g_free (marks);
-}
-
-static void
-heap_shot_free_objects (HeapShot *hs)
-{
- uintptr_t i;
- for (i = 0; i < hs->objects_hash_size; ++i) {
- HeapObjectDesc *ho = hs->objects_hash [i];
- if (ho)
- g_free (ho);
- }
- if (hs->objects_hash)
- g_free (hs->objects_hash);
- hs->objects_hash = NULL;
- hs->objects_hash_size = 0;
- hs->objects_count = 0;
-}
-
-
-struct _BackTrace {
- BackTrace *next;
- unsigned int hash;
- int count;
- int id;
- MethodDesc *methods [1];
-};
-
-static BackTrace *backtrace_hash [HASH_SIZE];
-static BackTrace **backtraces = NULL;
-static int num_backtraces = 0;
-static int next_backtrace = 0;
-
-static int
-hash_backtrace (int count, MethodDesc **methods)
-{
- int hash = count;
- int i;
- for (i = 0; i < count; ++i) {
- hash = (hash << 5) - hash + methods [i]->method;
- }
- return hash;
-}
-
-static int
-compare_backtrace (BackTrace *bt, int count, MethodDesc **methods)
-{
- int i;
- if (bt->count != count)
- return 0;
- for (i = 0; i < count; ++i)
- if (methods [i] != bt->methods [i])
- return 0;
- return 1;
-}
-
-static BackTrace*
-add_backtrace (int count, MethodDesc **methods)
-{
- int hash = hash_backtrace (count, methods);
- int slot = (hash & 0xffff) % HASH_SIZE;
- BackTrace *bt = backtrace_hash [slot];
- while (bt) {
- if (bt->hash == hash && compare_backtrace (bt, count, methods))
- return bt;
- bt = bt->next;
- }
- bt = (BackTrace *)malloc (sizeof (BackTrace) + ((count - 1) * sizeof (void*)));
- bt->next = backtrace_hash [slot];
- backtrace_hash [slot] = bt;
- if (next_backtrace == num_backtraces) {
- num_backtraces *= 2;
- if (!num_backtraces)
- num_backtraces = 16;
- backtraces = (BackTrace **)realloc (backtraces, sizeof (void*) * num_backtraces);
- }
- bt->id = next_backtrace++;
- backtraces [bt->id] = bt;
- bt->count = count;
- bt->hash = hash;
- for (slot = 0; slot < count; ++slot)
- bt->methods [slot] = methods [slot];
-
- return bt;
-}
-
-typedef struct _MonitorDesc MonitorDesc;
-typedef struct _ThreadContext ThreadContext;
-typedef struct _DomainContext DomainContext;
-typedef struct _RemCtxContext RemCtxContext;
-
-typedef struct {
- FILE *file;
-#if defined (HAVE_SYS_ZLIB)
- gzFile gzfile;
-#endif
- unsigned char *buf;
- int size;
- int data_version;
- int version_major;
- int version_minor;
- int timer_overhead;
- int pid;
- int port;
- char *args;
- char *arch;
- char *os;
- uint64_t startup_time;
- ThreadContext *threads;
- ThreadContext *current_thread;
- DomainContext *domains;
- DomainContext *current_domain;
- RemCtxContext *remctxs;
- RemCtxContext *current_remctx;
-} ProfContext;
-
-struct _ThreadContext {
- ThreadContext *next;
- intptr_t thread_id;
- char *name;
- /* emulated stack */
- MethodDesc **stack;
- uint64_t *time_stack;
- uint64_t *callee_time_stack;
- uint64_t last_time;
- uint64_t contention_start;
- MonitorDesc *monitor;
- int stack_size;
- int stack_id;
- HeapShot *current_heap_shot;
- uintptr_t num_roots;
- uintptr_t size_roots;
- uintptr_t *roots;
- uintptr_t *roots_extra;
- int *roots_types;
- uint64_t gc_start_times [3];
-};
-
-struct _DomainContext {
- DomainContext *next;
- intptr_t domain_id;
- const char *friendly_name;
-};
-
-struct _RemCtxContext {
- RemCtxContext *next;
- intptr_t remctx_id;
- intptr_t domain_id;
-};
-
-static void
-ensure_buffer (ProfContext *ctx, int size)
-{
- if (ctx->size < size) {
- ctx->buf = (unsigned char *)realloc (ctx->buf, size);
- ctx->size = size;
- }
-}
-
-static int
-load_data (ProfContext *ctx, int size)
-{
- ensure_buffer (ctx, size);
-#if defined (HAVE_SYS_ZLIB)
- if (ctx->gzfile) {
- int r = gzread (ctx->gzfile, ctx->buf, size);
- if (r == 0)
- return size == 0? 1: 0;
- return r == size;
- } else
-#endif
- {
- int r = fread (ctx->buf, size, 1, ctx->file);
- if (r == 0)
- return size == 0? 1: 0;
- return r;
- }
-}
-
-static ThreadContext*
-get_thread (ProfContext *ctx, intptr_t thread_id)
-{
- ThreadContext *thread;
- if (ctx->current_thread && ctx->current_thread->thread_id == thread_id)
- return ctx->current_thread;
- thread = ctx->threads;
- while (thread) {
- if (thread->thread_id == thread_id) {
- return thread;
- }
- thread = thread->next;
- }
- thread = (ThreadContext *)calloc (sizeof (ThreadContext), 1);
- thread->next = ctx->threads;
- ctx->threads = thread;
- thread->thread_id = thread_id;
- thread->last_time = 0;
- thread->stack_id = 0;
- thread->stack_size = 32;
- thread->stack = (MethodDesc **)malloc (thread->stack_size * sizeof (void*));
- thread->time_stack = (uint64_t *)malloc (thread->stack_size * sizeof (uint64_t));
- thread->callee_time_stack = (uint64_t *)malloc (thread->stack_size * sizeof (uint64_t));
- return thread;
-}
-
-static DomainContext *
-get_domain (ProfContext *ctx, intptr_t domain_id)
-{
- if (ctx->current_domain && ctx->current_domain->domain_id == domain_id)
- return ctx->current_domain;
-
- DomainContext *domain = ctx->domains;
-
- while (domain) {
- if (domain->domain_id == domain_id)
- return domain;
-
- domain = domain->next;
- }
-
- domain = (DomainContext *)calloc (sizeof (DomainContext), 1);
- domain->next = ctx->domains;
- ctx->domains = domain;
- domain->domain_id = domain_id;
-
- return domain;
-}
-
-static RemCtxContext *
-get_remctx (ProfContext *ctx, intptr_t remctx_id)
-{
- if (ctx->current_remctx && ctx->current_remctx->remctx_id == remctx_id)
- return ctx->current_remctx;
-
- RemCtxContext *remctx = ctx->remctxs;
-
- while (remctx) {
- if (remctx->remctx_id == remctx_id)
- return remctx;
-
- remctx = remctx->next;
- }
-
- remctx = (RemCtxContext *)calloc (sizeof (RemCtxContext), 1);
- remctx->next = ctx->remctxs;
- ctx->remctxs = remctx;
- remctx->remctx_id = remctx_id;
-
- return remctx;
-}
-
-static ThreadContext*
-load_thread (ProfContext *ctx, intptr_t thread_id)
-{
- ThreadContext *thread = get_thread (ctx, thread_id);
- ctx->current_thread = thread;
- return thread;
-}
-
-static void
-ensure_thread_stack (ThreadContext *thread)
-{
- if (thread->stack_id == thread->stack_size) {
- thread->stack_size *= 2;
- thread->stack = (MethodDesc **)realloc (thread->stack, thread->stack_size * sizeof (void*));
- thread->time_stack = (uint64_t *)realloc (thread->time_stack, thread->stack_size * sizeof (uint64_t));
- thread->callee_time_stack = (uint64_t *)realloc (thread->callee_time_stack, thread->stack_size * sizeof (uint64_t));
- }
-}
-
-static int
-add_trace_hashed (CallContext *traces, int size, BackTrace *bt, uint64_t value)
-{
- int i;
- unsigned int start_pos;
- start_pos = bt->hash % size;
- i = start_pos;
- do {
- if (traces [i].bt == bt) {
- traces [i].count += value;
- return 0;
- } else if (!traces [i].bt) {
- traces [i].bt = bt;
- traces [i].count += value;
- return 1;
- }
- /* wrap around */
- if (++i == size)
- i = 0;
- } while (i != start_pos);
- /* should not happen */
- printf ("failed trace store\n");
- return 0;
-}
-
-static void
-add_trace_bt (BackTrace *bt, TraceDesc *trace, uint64_t value)
-{
- int i;
- if (!collect_traces)
- return;
- if (trace->count * 2 >= trace->size) {
- CallContext *n;
- int old_size = trace->size;
- trace->size *= 2;
- if (trace->size == 0)
- trace->size = 4;
- n = (CallContext *)calloc (sizeof (CallContext) * trace->size, 1);
- for (i = 0; i < old_size; ++i) {
- if (trace->traces [i].bt)
- add_trace_hashed (n, trace->size, trace->traces [i].bt, trace->traces [i].count);
- }
- if (trace->traces)
- g_free (trace->traces);
- trace->traces = n;
- }
- trace->count += add_trace_hashed (trace->traces, trace->size, bt, value);
-}
-
-static BackTrace*
-add_trace_thread (ThreadContext *thread, TraceDesc *trace, uint64_t value)
-{
- BackTrace *bt;
- int count = thread->stack_id;
- if (!collect_traces)
- return NULL;
- if (count > trace_max)
- count = trace_max;
- bt = add_backtrace (count, thread->stack + thread->stack_id - count);
- add_trace_bt (bt, trace, value);
- return bt;
-}
-
-static BackTrace*
-add_trace_methods (MethodDesc **methods, int count, TraceDesc *trace, uint64_t value)
-{
- BackTrace *bt;
- if (!collect_traces)
- return NULL;
- if (count > trace_max)
- count = trace_max;
- bt = add_backtrace (count, methods);
- add_trace_bt (bt, trace, value);
- return bt;
-}
-
-static void
-thread_add_root (ThreadContext *ctx, uintptr_t obj, int root_type, uintptr_t extra_info)
-{
- if (ctx->num_roots == ctx->size_roots) {
- int new_size = ctx->size_roots * 2;
- if (!new_size)
- new_size = 4;
- ctx->roots = (uintptr_t *)realloc (ctx->roots, new_size * sizeof (uintptr_t));
- ctx->roots_extra = (uintptr_t *)realloc (ctx->roots_extra, new_size * sizeof (uintptr_t));
- ctx->roots_types = (int *)realloc (ctx->roots_types, new_size * sizeof (int));
- ctx->size_roots = new_size;
- }
- ctx->roots_types [ctx->num_roots] = root_type;
- ctx->roots_extra [ctx->num_roots] = extra_info;
- ctx->roots [ctx->num_roots++] = obj;
-}
-
-static int
-compare_callc (const void *a, const void *b)
-{
- const CallContext *A = (const CallContext *)a;
- const CallContext *B = (const CallContext *)b;
- if (B->count == A->count)
- return 0;
- if (B->count < A->count)
- return -1;
- return 1;
-}
-
-static void
-sort_context_array (TraceDesc* traces)
-{
- int i, j;
- for (i = 0, j = 0; i < traces->size; ++i) {
- if (traces->traces [i].bt) {
- traces->traces [j].bt = traces->traces [i].bt;
- traces->traces [j].count = traces->traces [i].count;
- j++;
- }
- }
- qsort (traces->traces, traces->count, sizeof (CallContext), compare_callc);
-}
-
-static void
-push_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
-{
- ensure_thread_stack (thread);
- thread->time_stack [thread->stack_id] = timestamp;
- thread->callee_time_stack [thread->stack_id] = 0;
- thread->stack [thread->stack_id++] = method;
- method->recurse_count++;
-}
-
-static void
-pop_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
-{
- method->recurse_count--;
- if (thread->stack_id > 0 && thread->stack [thread->stack_id - 1] == method) {
- uint64_t tdiff;
- thread->stack_id--;
- method->calls++;
- if (timestamp < thread->time_stack [thread->stack_id])
- fprintf (outfile, "time went backwards for %s\n", method->name);
- tdiff = timestamp - thread->time_stack [thread->stack_id];
- if (thread->callee_time_stack [thread->stack_id] > tdiff)
- fprintf (outfile, "callee time bigger for %s\n", method->name);
- method->self_time += tdiff - thread->callee_time_stack [thread->stack_id];
- method->callee_time += thread->callee_time_stack [thread->stack_id];
- if (thread->stack_id)
- thread->callee_time_stack [thread->stack_id - 1] += tdiff;
- //fprintf (outfile, "method %s took %d\n", method->name, (int)(tdiff/1000));
- } else {
- fprintf (outfile, "unmatched leave at stack pos: %d for method %s\n", thread->stack_id, method->name);
- }
-}
-
-typedef struct {
- uint64_t total_time;
- uint64_t max_time;
- int count;
-} GCDesc;
-static GCDesc gc_info [3];
-static uint64_t max_heap_size;
-static uint64_t gc_object_moves;
-static int gc_resizes;
-typedef struct {
- uint64_t created;
- uint64_t destroyed;
- uint64_t live;
- uint64_t max_live;
- TraceDesc traces;
- TraceDesc destroy_traces;
-} HandleInfo;
-static HandleInfo handle_info [4];
-
-static const char*
-gc_event_name (int ev)
-{
- switch (ev) {
- case MONO_GC_EVENT_START: return "start";
- case MONO_GC_EVENT_MARK_START: return "mark start";
- case MONO_GC_EVENT_MARK_END: return "mark end";
- case MONO_GC_EVENT_RECLAIM_START: return "reclaim start";
- 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 uint64_t clause_summary [MONO_EXCEPTION_CLAUSE_FAULT + 1];
-static uint64_t throw_count = 0;
-static TraceDesc exc_traces;
-
-static const char*
-clause_name (int type)
-{
- switch (type) {
- case MONO_EXCEPTION_CLAUSE_NONE: return "catch";
- case MONO_EXCEPTION_CLAUSE_FILTER: return "filter";
- case MONO_EXCEPTION_CLAUSE_FINALLY: return "finally";
- case MONO_EXCEPTION_CLAUSE_FAULT: return "fault";
- default: return "invalid";
- }
-}
-
-static uint64_t monitor_contention;
-static uint64_t monitor_failed;
-static uint64_t monitor_acquired;
-
-struct _MonitorDesc {
- MonitorDesc *next;
- uintptr_t objid;
- uintptr_t contentions;
- uint64_t wait_time;
- uint64_t max_wait_time;
- TraceDesc traces;
-};
-
-static MonitorDesc* monitor_hash [SMALL_HASH_SIZE] = {0};
-static int num_monitors = 0;
-
-static MonitorDesc*
-lookup_monitor (uintptr_t objid)
-{
- int slot = ((objid >> 3) & 0xffff) % SMALL_HASH_SIZE;
- MonitorDesc *cd = monitor_hash [slot];
- while (cd && cd->objid != objid)
- cd = cd->next;
- if (!cd) {
- cd = (MonitorDesc *)calloc (sizeof (MonitorDesc), 1);
- cd->objid = objid;
- cd->next = monitor_hash [slot];
- monitor_hash [slot] = cd;
- num_monitors++;
- }
- return cd;
-}
-
-static const char*
-monitor_ev_name (int ev)
-{
- switch (ev) {
- case MONO_PROFILER_MONITOR_CONTENTION: return "contended";
- case MONO_PROFILER_MONITOR_DONE: return "acquired";
- case MONO_PROFILER_MONITOR_FAIL: return "not taken";
- default: return "invalid";
- }
-}
-
-static const char*
-get_handle_name (int htype)
-{
- switch (htype) {
- case 0: return "weak";
- case 1: return "weaktrack";
- case 2: return "normal";
- case 3: return "pinned";
- default: return "unknown";
- }
-}
-
-static const char*
-get_root_name (int rtype)
-{
- switch (rtype & MONO_PROFILE_GC_ROOT_TYPEMASK) {
- case MONO_PROFILE_GC_ROOT_STACK: return "stack";
- case MONO_PROFILE_GC_ROOT_FINALIZER: return "finalizer";
- case MONO_PROFILE_GC_ROOT_HANDLE: return "handle";
- case MONO_PROFILE_GC_ROOT_OTHER: return "other";
- case MONO_PROFILE_GC_ROOT_MISC: return "misc";
- default: return "unknown";
- }
-}
-
-static MethodDesc**
-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;
- if (ctx->data_version < 13)
- decode_uleb128 (p, &p); /* flags */
- int count = decode_uleb128 (p, &p);
- 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);
- 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;
- return frames;
-}
-
-static void
-tracked_creation (uintptr_t obj, ClassDesc *cd, uint64_t size, BackTrace *bt, uint64_t timestamp)
-{
- int i;
- for (i = 0; i < num_tracked_objects; ++i) {
- if (tracked_objects [i] != obj)
- continue;
- fprintf (outfile, "Object %p created (%s, %llu bytes) at %.3f secs.\n", (void*)obj, cd->name, (unsigned long long) size, (timestamp - startup_time)/1000000000.0);
- if (bt && bt->count) {
- int k;
- for (k = 0; k < bt->count; ++k)
- fprintf (outfile, "\t%s\n", bt->methods [k]->name);
- }
- }
-}
-
-static void
-track_handle (uintptr_t obj, int htype, uint32_t handle, BackTrace *bt, uint64_t timestamp)
-{
- int i;
- for (i = 0; i < num_tracked_objects; ++i) {
- if (tracked_objects [i] != obj)
- continue;
- fprintf (outfile, "Object %p referenced from handle %u at %.3f secs.\n", (void*)obj, handle, (timestamp - startup_time) / 1000000000.0);
- if (bt && bt->count) {
- int k;
- for (k = 0; k < bt->count; ++k)
- fprintf (outfile, "\t%s\n", bt->methods [k]->name);
- }
- }
-}
-
-static void
-track_move (uintptr_t src, uintptr_t dst)
-{
- int i;
- for (i = 0; i < num_tracked_objects; ++i) {
- if (tracked_objects [i] == src)
- fprintf (outfile, "Object %p moved to %p\n", (void*)src, (void*)dst);
- else if (tracked_objects [i] == dst)
- fprintf (outfile, "Object %p moved from %p\n", (void*)dst, (void*)src);
- }
-}
-
-static void
-track_obj_reference (uintptr_t obj, uintptr_t parent, ClassDesc *cd)
-{
- int i;
- for (i = 0; i < num_tracked_objects; ++i) {
- if (tracked_objects [i] == obj)
- fprintf (outfile, "Object %p referenced from %p (%s).\n", (void*)obj, (void*)parent, cd->name);
- }
-}
-
-static void
-found_object (uintptr_t obj)
-{
- num_tracked_objects ++;
- tracked_objects = (uintptr_t *)realloc (tracked_objects, num_tracked_objects * sizeof (tracked_objects [0]));
- tracked_objects [num_tracked_objects - 1] = obj;
-}
-
-static int num_jit_helpers = 0;
-static int jit_helpers_code_size = 0;
-
-static const char*
-code_buffer_desc (int type)
-{
- switch (type) {
- case MONO_PROFILER_CODE_BUFFER_METHOD:
- return "method";
- case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE:
- return "method trampoline";
- case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE:
- return "unbox trampoline";
- case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE:
- return "imt trampoline";
- case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE:
- return "generics trampoline";
- case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE:
- return "specific trampoline";
- case MONO_PROFILER_CODE_BUFFER_HELPER:
- return "misc helper";
- case MONO_PROFILER_CODE_BUFFER_MONITOR:
- return "monitor/lock";
- case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE:
- return "delegate invoke";
- case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING:
- return "exception handling";
- default:
- return "unspecified";
- }
-}
-
-typedef struct _CoverageAssembly CoverageAssembly;
-struct _CoverageAssembly {
- char *name;
- char *guid;
- char *filename;
- int number_of_methods;
- int fully_covered;
- int partially_covered;
-};
-
-typedef struct _CoverageClass CoverageClass;
-struct _CoverageClass {
- char *assembly_name;
- char *class_name;
- int number_of_methods;
- int fully_covered;
- int partially_covered;
-};
-
-typedef struct _CoverageCoverage CoverageCoverage;
-struct _CoverageCoverage {
- int method_id;
- int offset;
- int count;
- int line;
- int column;
-};
-
-typedef struct _CoverageMethod CoverageMethod;
-struct _CoverageMethod {
- char *assembly_name;
- char *class_name;
- char *method_name;
- char *method_signature;
- char *filename;
- int token;
- int n_statements;
- int method_id;
- GPtrArray *coverage;
-};
-static GPtrArray *coverage_assemblies = NULL;
-static GPtrArray *coverage_methods = NULL;
-static GPtrArray *coverage_statements = NULL;
-static GHashTable *coverage_methods_hash = NULL;
-static GPtrArray *coverage_classes = NULL;
-static GHashTable *coverage_assembly_classes = NULL;
-
-static void
-gather_coverage_statements (void)
-{
- for (guint i = 0; i < coverage_statements->len; i++) {
- CoverageCoverage *coverage = (CoverageCoverage *)coverage_statements->pdata[i];
- CoverageMethod *method = (CoverageMethod *)g_hash_table_lookup (coverage_methods_hash, GINT_TO_POINTER (coverage->method_id));
- if (method == NULL) {
- fprintf (outfile, "Cannot find method with ID: %d\n", coverage->method_id);
- continue;
- }
-
- g_ptr_array_add (method->coverage, coverage);
- }
-}
-
-static void
-coverage_add_assembly (CoverageAssembly *assembly)
-{
- if (coverage_assemblies == NULL)
- coverage_assemblies = g_ptr_array_new ();
-
- g_ptr_array_add (coverage_assemblies, assembly);
-}
-
-static void
-coverage_add_method (CoverageMethod *method)
-{
- if (coverage_methods == NULL) {
- coverage_methods = g_ptr_array_new ();
- coverage_methods_hash = g_hash_table_new (NULL, NULL);
- }
-
- g_ptr_array_add (coverage_methods, method);
- g_hash_table_insert (coverage_methods_hash, GINT_TO_POINTER (method->method_id), method);
-}
-
-static void
-coverage_add_class (CoverageClass *klass)
-{
- GPtrArray *classes = NULL;
-
- if (coverage_classes == NULL) {
- coverage_classes = g_ptr_array_new ();
- coverage_assembly_classes = g_hash_table_new (g_str_hash, g_str_equal);
- }
-
- g_ptr_array_add (coverage_classes, klass);
- classes = (GPtrArray *)g_hash_table_lookup (coverage_assembly_classes, klass->assembly_name);
- if (classes == NULL) {
- classes = g_ptr_array_new ();
- g_hash_table_insert (coverage_assembly_classes, klass->assembly_name, classes);
- }
- g_ptr_array_add (classes, klass);
-}
-
-static void
-coverage_add_coverage (CoverageCoverage *coverage)
-{
- if (coverage_statements == NULL)
- coverage_statements = g_ptr_array_new ();
-
- g_ptr_array_add (coverage_statements, coverage);
-}
-
-#define OBJ_ADDR(diff) ((obj_base + diff) << 3)
-#define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
-
-
-/* Stats */
-#define BUFFER_HEADER_SIZE 48
-
-typedef struct {
- int count, min_size, max_size, bytes;
-} EventStat;
-
-static int buffer_count;
-static EventStat stats [256];
-
-static void
-record_event_stats (int type, int size)
-{
- ++stats [type].count;
- if (!stats [type].min_size)
- stats [type].min_size = size;
- stats [type].min_size = MIN (stats [type].min_size, size);
- stats [type].max_size = MAX (stats [type].max_size, size);
- stats [type].bytes += size;
-}
-
-static int
-decode_buffer (ProfContext *ctx)
-{
- unsigned char *p;
- unsigned char *end;
- intptr_t thread_id;
- intptr_t ptr_base;
- intptr_t obj_base;
- intptr_t method_base;
- uint64_t time_base;
- uint64_t file_offset;
- int len, i;
- ThreadContext *thread;
-
-#ifdef HAVE_SYS_ZLIB
- if (ctx->gzfile)
- file_offset = gztell (ctx->gzfile);
- else
-#endif
- file_offset = ftell (ctx->file);
- if (!load_data (ctx, 48))
- return 0;
- p = ctx->buf;
- if (read_int32 (p) != BUF_ID) {
- fprintf (outfile, "Incorrect buffer id: 0x%x\n", read_int32 (p));
- for (i = 0; i < 48; ++i) {
- fprintf (outfile, "0x%x%s", p [i], i % 8?" ":"\n");
- }
- return 0;
- }
- len = read_int32 (p + 4);
- time_base = read_int64 (p + 8);
- ptr_base = read_int64 (p + 16);
- obj_base = read_int64 (p + 24);
- thread_id = read_int64 (p + 32);
- method_base = read_int64 (p + 40);
- if (debug)
- fprintf (outfile, "buf: thread:%zx, len: %d, time: %llu, file offset: %llu\n", thread_id, len, (unsigned long long) time_base, (unsigned long long) file_offset);
- thread = load_thread (ctx, thread_id);
- if (!load_data (ctx, len))
- return 0;
-
- ++buffer_count;
-
- if (!startup_time) {
- startup_time = time_base;
- if (use_time_filter) {
- time_from += startup_time;
- time_to += startup_time;
- }
- }
- for (i = 0; i < thread->stack_id; ++i)
- thread->stack [i]->recurse_count++;
- p = ctx->buf;
- end = p + len;
- while (p < end) {
- unsigned char *start = p;
- unsigned char event = *p;
- switch (*p & 0xf) {
- case TYPE_GC: {
- int subtype = *p & 0xf0;
- uint64_t tdiff = decode_uleb128 (p + 1, &p);
- LOG_TIME (time_base, tdiff);
- time_base += tdiff;
- if (subtype == TYPE_GC_RESIZE) {
- uint64_t new_size = decode_uleb128 (p, &p);
- if (debug)
- fprintf (outfile, "gc heap resized to %llu\n", (unsigned long long) new_size);
- gc_resizes++;
- if (new_size > max_heap_size)
- max_heap_size = new_size;
- } else if (subtype == TYPE_GC_EVENT) {
- 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) {
- fprintf (outfile, "incorrect gc gen: %d\n", gen);
- break;
- }
- if (ev == MONO_GC_EVENT_START) {
- thread->gc_start_times [gen] = time_base;
- gc_info [gen].count++;
- } else if (ev == MONO_GC_EVENT_END) {
- tdiff = time_base - thread->gc_start_times [gen];
- gc_info [gen].total_time += tdiff;
- if (tdiff > gc_info [gen].max_time)
- gc_info [gen].max_time = tdiff;
- }
- } else if (subtype == TYPE_GC_MOVE) {
- int j, num = decode_uleb128 (p, &p);
- gc_object_moves += num / 2;
- for (j = 0; j < num; j += 2) {
- intptr_t obj1diff = decode_sleb128 (p, &p);
- intptr_t obj2diff = decode_sleb128 (p, &p);
- if (num_tracked_objects)
- track_move (OBJ_ADDR (obj1diff), OBJ_ADDR (obj2diff));
- if (debug) {
- fprintf (outfile, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff), (void*)OBJ_ADDR (obj2diff));
- }
- }
- } else if (subtype == TYPE_GC_HANDLE_CREATED || subtype == TYPE_GC_HANDLE_CREATED_BT) {
- int has_bt = subtype == TYPE_GC_HANDLE_CREATED_BT;
- int num_bt = 0;
- MethodDesc *sframes [8];
- MethodDesc **frames = sframes;
- int htype = decode_uleb128 (p, &p);
- uint32_t handle = decode_uleb128 (p, &p);
- intptr_t objdiff = decode_sleb128 (p, &p);
- if (has_bt) {
- num_bt = 8;
- frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
- if (!frames) {
- fprintf (outfile, "Cannot load backtrace\n");
- return 0;
- }
- }
- if (htype > 3)
- return 0;
- if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
- handle_info [htype].created++;
- handle_info [htype].live++;
- if (handle_info [htype].live > handle_info [htype].max_live)
- handle_info [htype].max_live = handle_info [htype].live;
- BackTrace *bt;
- if (has_bt)
- bt = add_trace_methods (frames, num_bt, &handle_info [htype].traces, 1);
- else
- bt = add_trace_thread (thread, &handle_info [htype].traces, 1);
- if (num_tracked_objects)
- track_handle (OBJ_ADDR (objdiff), htype, handle, bt, time_base);
- }
- if (debug)
- fprintf (outfile, "handle (%s) %u created for object %p\n", get_handle_name (htype), handle, (void*)OBJ_ADDR (objdiff));
- if (frames != sframes)
- g_free (frames);
- } else if (subtype == TYPE_GC_HANDLE_DESTROYED || subtype == TYPE_GC_HANDLE_DESTROYED_BT) {
- int has_bt = subtype == TYPE_GC_HANDLE_DESTROYED_BT;
- int num_bt = 0;
- MethodDesc *sframes [8];
- MethodDesc **frames = sframes;
- int htype = decode_uleb128 (p, &p);
- uint32_t handle = decode_uleb128 (p, &p);
- if (has_bt) {
- num_bt = 8;
- frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
- if (!frames) {
- fprintf (outfile, "Cannot load backtrace\n");
- return 0;
- }
- }
- if (htype > 3)
- return 0;
- if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
- handle_info [htype].destroyed ++;
- handle_info [htype].live--;
- BackTrace *bt;
- if (has_bt)
- bt = add_trace_methods (frames, num_bt, &handle_info [htype].destroy_traces, 1);
- else
- bt = add_trace_thread (thread, &handle_info [htype].destroy_traces, 1);
- /* TODO: track_handle_free () - would need to record and keep track of the associated object address... */
- }
- if (debug)
- fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
- if (frames != sframes)
- g_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;
- }
- case TYPE_METADATA: {
- int subtype = *p & 0xf0;
- const char *load_str = subtype == TYPE_END_LOAD ? "loaded" : "unloaded";
- uint64_t tdiff = decode_uleb128 (p + 1, &p);
- int mtype = *p++;
- intptr_t ptrdiff = decode_sleb128 (p, &p);
- LOG_TIME (time_base, tdiff);
- time_base += tdiff;
- if (mtype == TYPE_CLASS) {
- intptr_t imptrdiff = decode_sleb128 (p, &p);
- 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)
- add_class (ptr_base + ptrdiff, (char*)p);
- while (*p) p++;
- p++;
- } else if (mtype == TYPE_IMAGE) {
- 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)
- add_image (ptr_base + ptrdiff, (char*)p);
- while (*p) p++;
- p++;
- } else if (mtype == TYPE_ASSEMBLY) {
- 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)
- add_assembly (ptr_base + ptrdiff, (char*)p);
- while (*p) p++;
- p++;
- } else if (mtype == TYPE_DOMAIN) {
- 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)
- nd->friendly_name = pstrdup ((char *) p);
- if (debug) {
- if (subtype == 0)
- fprintf (outfile, "domain %p named at %llu: %s\n", (void *) (ptr_base + ptrdiff), (unsigned long long) time_base, p);
- else
- fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
- }
- if (subtype == 0) {
- while (*p) p++;
- p++;
- }
- } else if (mtype == TYPE_CONTEXT) {
- 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) {
- 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)
- nt->name = pstrdup ((char*)p);
- if (debug) {
- if (subtype == 0)
- fprintf (outfile, "thread %p named at %llu: %s\n", (void*)(ptr_base + ptrdiff), (unsigned long long) time_base, p);
- else
- fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
- }
- if (subtype == 0) {
- while (*p) p++;
- p++;
- }
- }
- break;
- }
- case TYPE_ALLOC: {
- int has_bt = *p & TYPE_ALLOC_BT;
- uint64_t tdiff = decode_uleb128 (p + 1, &p);
- intptr_t ptrdiff = decode_sleb128 (p, &p);
- intptr_t objdiff = decode_sleb128 (p, &p);
- uint64_t len;
- int num_bt = 0;
- MethodDesc* sframes [8];
- MethodDesc** frames = sframes;
- ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
- len = decode_uleb128 (p, &p);
- LOG_TIME (time_base, tdiff);
- time_base += tdiff;
- if (debug)
- 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 (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
- if (!frames) {
- fprintf (outfile, "Cannot load backtrace\n");
- return 0;
- }
- }
- if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
- BackTrace *bt;
- cd->allocs++;
- cd->alloc_size += len;
- if (has_bt)
- bt = add_trace_methods (frames, num_bt, &cd->traces, len);
- else
- bt = add_trace_thread (thread, &cd->traces, len);
- if (find_size && len >= find_size) {
- if (!find_name || strstr (cd->name, find_name))
- found_object (OBJ_ADDR (objdiff));
- } else if (!find_size && find_name && strstr (cd->name, find_name)) {
- found_object (OBJ_ADDR (objdiff));
- }
- if (num_tracked_objects)
- tracked_creation (OBJ_ADDR (objdiff), cd, len, bt, time_base);
- }
- if (frames != sframes)
- g_free (frames);
- break;
- }
- case TYPE_METHOD: {
- int subtype = *p & 0xf0;
- uint64_t tdiff = decode_uleb128 (p + 1, &p);
- int64_t ptrdiff = decode_sleb128 (p, &p);
- LOG_TIME (time_base, tdiff);
- time_base += tdiff;
- method_base += ptrdiff;
- if (subtype == TYPE_JIT) {
- intptr_t codediff = decode_sleb128 (p, &p);
- int codelen = decode_uleb128 (p, &p);
- MethodDesc *jitted_method;
- if (debug)
- fprintf (outfile, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base), p, codelen, (void*)(ptr_base + codediff));
- jitted_method = add_method (method_base, (char*)p, ptr_base + codediff, codelen);
- if (!(time_base >= time_from && time_base < time_to))
- jitted_method->ignore_jit = 1;
- while (*p) p++;
- p++;
- } else {
- MethodDesc *method;
- if ((thread_filter && thread_filter != thread->thread_id))
- break;
- if (!(time_base >= time_from && time_base < time_to))
- break;
- method = lookup_method (method_base);
- if (subtype == TYPE_ENTER) {
- add_trace_thread (thread, &method->traces, 1);
- push_method (thread, method, time_base);
- } else {
- pop_method (thread, method, time_base);
- }
- if (debug)
- fprintf (outfile, "%s method %s\n", subtype == TYPE_ENTER? "enter": subtype == TYPE_EXC_LEAVE? "exleave": "leave", method->name);
- }
- break;
- }
- case TYPE_HEAP: {
- int subtype = *p & 0xf0;
- if (subtype == TYPE_HEAP_OBJECT) {
- HeapObjectDesc *ho = NULL;
- int i;
- 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);
- uintptr_t ref_offset = 0;
- uintptr_t last_obj_offset = 0;
- ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
- if (size) {
- HeapClassDesc *hcd = add_heap_shot_class (thread->current_heap_shot, cd, size);
- if (collect_traces) {
- ho = alloc_heap_obj (OBJ_ADDR (objdiff), hcd, num);
- add_heap_shot_obj (thread->current_heap_shot, ho);
- ref_offset = 0;
- }
- } else {
- if (collect_traces)
- ho = heap_shot_obj_add_refs (thread->current_heap_shot, OBJ_ADDR (objdiff), num, &ref_offset);
- }
- for (i = 0; i < num; ++i) {
- /* FIXME: use object distance to measure how good
- * the GC is at keeping related objects close
- */
- uintptr_t offset = ctx->data_version > 1? last_obj_offset + decode_uleb128 (p, &p): -1;
- intptr_t obj1diff = decode_sleb128 (p, &p);
- last_obj_offset = offset;
- if (collect_traces)
- ho->refs [ref_offset + i] = OBJ_ADDR (obj1diff);
- if (num_tracked_objects)
- track_obj_reference (OBJ_ADDR (obj1diff), OBJ_ADDR (objdiff), cd);
- }
- 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;
- 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;
- 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)
- fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type));
- if (collect_traces)
- thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info);
- }
- } else if (subtype == TYPE_HEAP_END) {
- uint64_t tdiff = decode_uleb128 (p + 1, &p);
- LOG_TIME (time_base, tdiff);
- time_base += tdiff;
- if (debug)
- fprintf (outfile, "heap shot end\n");
- if (collect_traces) {
- HeapShot *hs = thread->current_heap_shot;
- if (hs && thread->num_roots) {
- /* transfer the root ownershipt to the heapshot */
- hs->num_roots = thread->num_roots;
- hs->roots = thread->roots;
- hs->roots_extra = thread->roots_extra;
- hs->roots_types = thread->roots_types;
- } else {
- g_free (thread->roots);
- g_free (thread->roots_extra);
- g_free (thread->roots_types);
- }
- thread->num_roots = 0;
- thread->size_roots = 0;
- thread->roots = NULL;
- thread->roots_extra = NULL;
- thread->roots_types = NULL;
- heap_shot_resolve_reverse_refs (hs);
- heap_shot_mark_objects (hs);
- heap_shot_free_objects (hs);
- }
- thread->current_heap_shot = NULL;
- } else if (subtype == TYPE_HEAP_START) {
- uint64_t tdiff = decode_uleb128 (p + 1, &p);
- LOG_TIME (time_base, tdiff);
- time_base += tdiff;
- if (debug)
- fprintf (outfile, "heap shot start\n");
- thread->current_heap_shot = new_heap_shot (time_base);
- }
- break;
- }
- case TYPE_MONITOR: {
- int event = (*p >> 4) & 0x3;
- int has_bt = *p & TYPE_MONITOR_BT;
- uint64_t tdiff = decode_uleb128 (p + 1, &p);
- intptr_t objdiff = decode_sleb128 (p, &p);
- MethodDesc* sframes [8];
- MethodDesc** frames = sframes;
- int record;
- int num_bt = 0;
- LOG_TIME (time_base, tdiff);
- time_base += tdiff;
- record = (!thread_filter || thread_filter == thread->thread_id);
- if (!(time_base >= time_from && time_base < time_to))
- record = 0;
- if (event == MONO_PROFILER_MONITOR_CONTENTION) {
- MonitorDesc *mdesc = lookup_monitor (OBJ_ADDR (objdiff));
- if (record) {
- monitor_contention++;
- mdesc->contentions++;
- thread->monitor = mdesc;
- thread->contention_start = time_base;
- }
- if (has_bt) {
- num_bt = 8;
- frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
- if (!frames) {
- fprintf (outfile, "Cannot load backtrace\n");
- return 0;
- }
- if (record)
- add_trace_methods (frames, num_bt, &mdesc->traces, 1);
- } else {
- if (record)
- add_trace_thread (thread, &mdesc->traces, 1);
- }
- } else if (event == MONO_PROFILER_MONITOR_FAIL) {
- if (record) {
- monitor_failed++;
- if (thread->monitor && thread->contention_start) {
- uint64_t wait_time = time_base - thread->contention_start;
- if (wait_time > thread->monitor->max_wait_time)
- thread->monitor->max_wait_time = wait_time;
- thread->monitor->wait_time += wait_time;
- thread->monitor = NULL;
- thread->contention_start = 0;
- }
- }
- } else if (event == MONO_PROFILER_MONITOR_DONE) {
- if (record) {
- monitor_acquired++;
- if (thread->monitor && thread->contention_start) {
- uint64_t wait_time = time_base - thread->contention_start;
- if (wait_time > thread->monitor->max_wait_time)
- thread->monitor->max_wait_time = wait_time;
- thread->monitor->wait_time += wait_time;
- thread->monitor = NULL;
- thread->contention_start = 0;
- }
- }
- }
- if (debug)
- fprintf (outfile, "monitor %s for object %p\n", monitor_ev_name (event), (void*)OBJ_ADDR (objdiff));
- if (frames != sframes)
- g_free (frames);
- break;
- }
- case TYPE_EXCEPTION: {
- int subtype = *p & 0x70;
- int has_bt = *p & TYPE_THROW_BT;
- uint64_t tdiff = decode_uleb128 (p + 1, &p);
- MethodDesc* sframes [8];
- MethodDesc** frames = sframes;
- int record;
- LOG_TIME (time_base, tdiff);
- time_base += tdiff;
- record = (!thread_filter || thread_filter == thread->thread_id);
- if (!(time_base >= time_from && time_base < time_to))
- record = 0;
- if (subtype == TYPE_CLAUSE) {
- 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;
- if (record)
- clause_summary [clause_type]++;
- if (debug)
- fprintf (outfile, "clause %s (%d) in method %s\n", clause_name (clause_type), clause_num, lookup_method (method_base)->name);
- } else {
- intptr_t objdiff = decode_sleb128 (p, &p);
- if (record)
- throw_count++;
- if (has_bt) {
- has_bt = 8;
- frames = decode_bt (ctx, sframes, &has_bt, p, &p, ptr_base, &method_base);
- if (!frames) {
- fprintf (outfile, "Cannot load backtrace\n");
- return 0;
- }
- if (record)
- add_trace_methods (frames, has_bt, &exc_traces, 1);
- } else {
- if (record)
- add_trace_thread (thread, &exc_traces, 1);
- }
- if (frames != sframes)
- g_free (frames);
- if (debug)
- fprintf (outfile, "throw %p\n", (void*)OBJ_ADDR (objdiff));
- }
- break;
- }
- case TYPE_RUNTIME: {
- int subtype = *p & 0xf0;
- uint64_t tdiff = decode_uleb128 (p + 1, &p);
- LOG_TIME (time_base, tdiff);
- time_base += tdiff;
- if (subtype == TYPE_JITHELPER) {
- 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;
- if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
- name = (const char *)p;
- while (*p) p++;
- p++;
- } else {
- name = code_buffer_desc (type);
- }
- num_jit_helpers++;
- jit_helpers_code_size += codelen;
- if (debug)
- fprintf (outfile, "jit helper %s, size: %d, code: %p\n", name, codelen, (void*)(ptr_base + codediff));
- }
- break;
- }
- case TYPE_SAMPLE: {
- int subtype = *p & 0xf0;
- if (subtype == TYPE_SAMPLE_HIT) {
- int i;
- 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));
- int count = decode_uleb128 (p, &p);
- for (i = 0; i < count; ++i) {
- uintptr_t ip = ptr_base + decode_sleb128 (p, &p);
- if ((tstamp >= time_from && tstamp < time_to))
- add_stat_sample (sample_type, ip);
- if (debug)
- fprintf (outfile, "sample hit, type: %d at %p for thread %p\n", sample_type, (void*)ip, tid);
- }
- if (ctx->data_version > 5) {
- count = decode_uleb128 (p, &p);
- for (i = 0; i < count; ++i) {
- MethodDesc *method;
- int64_t ptrdiff = decode_sleb128 (p, &p);
- method_base += ptrdiff;
- method = lookup_method (method_base);
- if (debug)
- 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;
- 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);
- add_unmanaged_symbol (addr, name, size);
- if (debug)
- fprintf (outfile, "unmanaged symbol %s at %p\n", name, (void*)addr);
- while (*p) p++;
- p++;
- } else if (subtype == TYPE_SAMPLE_UBIN) {
- /* un unmanaged binary loaded in memory */
- uint64_t tdiff = decode_uleb128 (p + 1, &p);
- uintptr_t addr = decode_sleb128 (p, &p);
- uint64_t offset G_GNUC_UNUSED = decode_uleb128 (p, &p);
- uintptr_t size = decode_uleb128 (p, &p);
- char *name;
- LOG_TIME (time_base, tdiff);
- time_base += tdiff;
- name = pstrdup ((char*)p);
- add_unmanaged_binary (addr, name, size);
- if (debug)
- fprintf (outfile, "unmanaged binary %s at %p\n", name, (void*)addr);
- while (*p) p++;
- p++;
- } else if (subtype == TYPE_SAMPLE_COUNTERS_DESC) {
- 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);
- char *section_str, *name;
- if (section != MONO_COUNTER_PERFCOUNTERS) {
- section_str = (char*) section_name (section);
- } else {
- section_str = pstrdup ((char*)p);
- while (*p++);
- }
- name = pstrdup ((char*)p);
- while (*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);
- }
- } else if (subtype == TYPE_SAMPLE_COUNTERS) {
- int i;
- CounterValue *value, *previous = NULL;
- CounterList *list;
- 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);
- if (index == 0)
- break;
-
- for (list = counters; list; list = list->next) {
- if (list->counter->index == (int)index) {
- previous = list->counter->values_last;
- break;
- }
- }
-
- if (ctx->data_version > 12)
- type = *p++;
- else
- type = decode_uleb128 (p, &p);
-
- value = (CounterValue *)calloc (1, sizeof (CounterValue));
- value->timestamp = timestamp;
-
- switch (type) {
- case MONO_COUNTER_INT:
-#if SIZEOF_VOID_P == 4
- case MONO_COUNTER_WORD:
-#endif
- value->buffer = (unsigned char *)malloc (sizeof (int32_t));
- *(int32_t*)value->buffer = (int32_t)decode_sleb128 (p, &p) + (previous ? (*(int32_t*)previous->buffer) : 0);
- break;
- case MONO_COUNTER_UINT:
- value->buffer = (unsigned char *)malloc (sizeof (uint32_t));
- *(uint32_t*)value->buffer = (uint32_t)decode_uleb128 (p, &p) + (previous ? (*(uint32_t*)previous->buffer) : 0);
- break;
- case MONO_COUNTER_LONG:
-#if SIZEOF_VOID_P == 8
- case MONO_COUNTER_WORD:
-#endif
- case MONO_COUNTER_TIME_INTERVAL:
- value->buffer = (unsigned char *)malloc (sizeof (int64_t));
- *(int64_t*)value->buffer = (int64_t)decode_sleb128 (p, &p) + (previous ? (*(int64_t*)previous->buffer) : 0);
- break;
- case MONO_COUNTER_ULONG:
- value->buffer = (unsigned char *)malloc (sizeof (uint64_t));
- *(uint64_t*)value->buffer = (uint64_t)decode_uleb128 (p, &p) + (previous ? (*(uint64_t*)previous->buffer) : 0);
- break;
- case MONO_COUNTER_DOUBLE:
- value->buffer = (unsigned char *)malloc (sizeof (double));
-#if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
- for (i = 0; i < sizeof (double); i++)
-#else
- for (i = sizeof (double) - 1; i >= 0; i--)
-#endif
- value->buffer[i] = *p++;
- break;
- case MONO_COUNTER_STRING:
- if (*p++ == 0) {
- value->buffer = NULL;
- } else {
- value->buffer = (unsigned char*) pstrdup ((char*)p);
- while (*p++);
- }
- break;
- }
- if (time_between >= time_from && time_between <= time_to)
- add_counter_value (index, value);
- }
- } else {
- return 0;
- }
- break;
- }
- case TYPE_COVERAGE:{
- int subtype = *p & 0xf0;
- switch (subtype) {
- case TYPE_COVERAGE_METHOD: {
- CoverageMethod *method = g_new0 (CoverageMethod, 1);
- const char *assembly, *klass, *name, *sig, *filename;
- 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++;
- sig = (const char *)p; while (*p) p++; p++;
- filename = (const char *)p; while (*p) p++; p++;
-
- token = decode_uleb128 (p, &p);
- method_id = decode_uleb128 (p, &p);
- n_offsets = decode_uleb128 (p, &p);
-
- method->assembly_name = g_strdup (assembly);
- method->class_name = g_strdup (klass);
- method->method_name = g_strdup (name);
- method->method_signature = g_strdup (sig);
- method->filename = g_strdup (filename);
- method->token = token;
- method->n_statements = n_offsets;
- method->coverage = g_ptr_array_new ();
- method->method_id = method_id;
-
- coverage_add_method (method);
-
- break;
- }
- case TYPE_COVERAGE_STATEMENT: {
- CoverageCoverage *coverage = g_new0 (CoverageCoverage, 1);
- 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);
- line = decode_uleb128 (p, &p);
- column = decode_uleb128 (p, &p);
-
- coverage->method_id = method_id;
- coverage->offset = offset;
- coverage->count = count;
- coverage->line = line;
- coverage->column = column;
-
- coverage_add_coverage (coverage);
- break;
- }
- case TYPE_COVERAGE_ASSEMBLY: {
- CoverageAssembly *assembly = g_new0 (CoverageAssembly, 1);
- char *name, *guid, *filename;
- 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++;
- number_of_methods = decode_uleb128 (p, &p);
- fully_covered = decode_uleb128 (p, &p);
- partially_covered = decode_uleb128 (p, &p);
-
- assembly->name = g_strdup (name);
- assembly->guid = g_strdup (guid);
- assembly->filename = g_strdup (filename);
- assembly->number_of_methods = number_of_methods;
- assembly->fully_covered = fully_covered;
- assembly->partially_covered = partially_covered;
-
- coverage_add_assembly (assembly);
- break;
- }
- case TYPE_COVERAGE_CLASS: {
- CoverageClass *klass = g_new0 (CoverageClass, 1);
- char *assembly_name, *class_name;
- 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);
- fully_covered = decode_uleb128 (p, &p);
- partially_covered = decode_uleb128 (p, &p);
-
- klass->assembly_name = g_strdup (assembly_name);
- klass->class_name = g_strdup (class_name);
- klass->number_of_methods = number_of_methods;
- klass->fully_covered = fully_covered;
- klass->partially_covered = partially_covered;
-
- coverage_add_class (klass);
- break;
- }
-
- default:
- break;
- }
- 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);
- }
- record_event_stats (event, p - start);
- }
- thread->last_time = time_base;
- for (i = 0; i < thread->stack_id; ++i)
- thread->stack [i]->recurse_count = 0;
- return 1;
-}
-
-static int
-read_header_string (ProfContext *ctx, char **field)
-{
- if (!load_data (ctx, 4))
- return 0;
-
- if (!load_data (ctx, read_int32 (ctx->buf)))
- return 0;
-
- *field = pstrdup ((const char *) ctx->buf);
-
- return 1;
-}
-
-static ProfContext*
-load_file (char *name)
-{
- unsigned char *p;
- ProfContext *ctx = (ProfContext *)calloc (sizeof (ProfContext), 1);
- if (strcmp (name, "-") == 0)
- ctx->file = stdin;
- else
- ctx->file = fopen (name, "rb");
- if (!ctx->file) {
- printf ("Cannot open file: %s\n", name);
- exit (1);
- }
-#if defined (HAVE_SYS_ZLIB)
- if (ctx->file != stdin)
- ctx->gzfile = gzdopen (fileno (ctx->file), "rb");
-#endif
- if (!load_data (ctx, 30))
- return NULL;
- p = ctx->buf;
- if (read_int32 (p) != LOG_HEADER_ID || p [6] > LOG_DATA_VERSION)
- return NULL;
- ctx->version_major = p [4];
- ctx->version_minor = p [5];
- ctx->data_version = p [6];
- /* reading 64 bit files on 32 bit systems not supported yet */
- if (p [7] > sizeof (void*))
- return NULL;
- if (read_int32 (p + 20)) /* flags must be 0 */
- return NULL;
- ctx->startup_time = read_int64 (p + 8);
- ctx->timer_overhead = read_int32 (p + 16);
- ctx->pid = read_int32 (p + 24);
- ctx->port = read_int16 (p + 28);
- if (ctx->version_major >= 1) {
- if (!read_header_string (ctx, &ctx->args))
- return NULL;
- if (!read_header_string (ctx, &ctx->arch))
- return NULL;
- if (!read_header_string (ctx, &ctx->os))
- return NULL;
- } else {
- if (!load_data (ctx, 2)) /* old opsys field, was never used */
- return NULL;
- }
- return ctx;
-}
-
-enum {
- ALLOC_SORT_BYTES,
- ALLOC_SORT_COUNT
-};
-static int alloc_sort_mode = ALLOC_SORT_BYTES;
-
-static int
-compare_class (const void *a, const void *b)
-{
- ClassDesc *const *A = (ClassDesc *const *)a;
- ClassDesc *const *B = (ClassDesc *const *)b;
- uint64_t vala, valb;
- if (alloc_sort_mode == ALLOC_SORT_BYTES) {
- vala = (*A)->alloc_size;
- valb = (*B)->alloc_size;
- } else {
- vala = (*A)->allocs;
- valb = (*B)->allocs;
- }
- if (valb == vala)
- return 0;
- if (valb < vala)
- return -1;
- return 1;
-}
-
-static void
-dump_header (ProfContext *ctx)
-{
- time_t st = ctx->startup_time / 1000;
- char *t = ctime (&st);
- fprintf (outfile, "\nMono log profiler data\n");
- fprintf (outfile, "\tProfiler version: %d.%d\n", ctx->version_major, ctx->version_minor);
- fprintf (outfile, "\tData version: %d\n", ctx->data_version);
- if (ctx->version_major >= 1) {
- fprintf (outfile, "\tArguments: %s\n", ctx->args);
- fprintf (outfile, "\tArchitecture: %s\n", ctx->arch);
- fprintf (outfile, "\tOperating system: %s\n", ctx->os);
- }
- fprintf (outfile, "\tMean timer overhead: %d nanoseconds\n", ctx->timer_overhead);
- fprintf (outfile, "\tProgram startup: %s", t);
- if (ctx->pid)
- fprintf (outfile, "\tProgram ID: %d\n", ctx->pid);
- if (ctx->port)
- fprintf (outfile, "\tServer listening on: %d\n", ctx->port);
-}
-
-static void
-dump_traces (TraceDesc *traces, const char *desc)
-{
- int j;
- if (!show_traces)
- return;
- if (!traces->count)
- return;
- sort_context_array (traces);
- for (j = 0; j < traces->count; ++j) {
- int k;
- BackTrace *bt;
- bt = traces->traces [j].bt;
- if (!bt->count)
- continue;
- fprintf (outfile, "\t%llu %s from:\n", (unsigned long long) traces->traces [j].count, desc);
- for (k = 0; k < bt->count; ++k)
- fprintf (outfile, "\t\t%s\n", bt->methods [k]->name);
- }
-}
-
-static void
-dump_threads (ProfContext *ctx)
-{
- ThreadContext *thread;
- fprintf (outfile, "\nThread summary\n");
- for (thread = ctx->threads; thread; thread = thread->next) {
- if (thread->thread_id) {
- fprintf (outfile, "\tThread: %p, name: \"%s\"\n", (void*)thread->thread_id, thread->name? thread->name: "");
- }
- }
-}
-
-static void
-dump_domains (ProfContext *ctx)
-{
- fprintf (outfile, "\nDomain summary\n");
-
- for (DomainContext *domain = ctx->domains; domain; domain = domain->next)
- fprintf (outfile, "\tDomain: %p, friendly name: \"%s\"\n", (void *) domain->domain_id, domain->friendly_name);
-}
-
-static void
-dump_remctxs (ProfContext *ctx)
-{
- fprintf (outfile, "\nContext summary\n");
-
- for (RemCtxContext *remctx = ctx->remctxs; remctx; remctx = remctx->next)
- fprintf (outfile, "\tContext: %p, domain: %p\n", (void *) remctx->remctx_id, (void *) remctx->domain_id);
-}
-
-static void
-dump_exceptions (void)
-{
- int i;
- fprintf (outfile, "\nException summary\n");
- fprintf (outfile, "\tThrows: %llu\n", (unsigned long long) throw_count);
- dump_traces (&exc_traces, "throws");
- for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
- if (!clause_summary [i])
- continue;
- fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), (unsigned long long) clause_summary [i]);
- }
-}
-
-static int
-compare_monitor (const void *a, const void *b)
-{
- MonitorDesc *const *A = (MonitorDesc *const *)a;
- MonitorDesc *const *B = (MonitorDesc *const *)b;
- if ((*B)->wait_time == (*A)->wait_time)
- return 0;
- if ((*B)->wait_time < (*A)->wait_time)
- return -1;
- return 1;
-}
-
-static void
-dump_monitors (void)
-{
- MonitorDesc **monitors;
- int i, j;
- if (!num_monitors)
- return;
- monitors = (MonitorDesc **)malloc (sizeof (void*) * num_monitors);
- for (i = 0, j = 0; i < SMALL_HASH_SIZE; ++i) {
- MonitorDesc *mdesc = monitor_hash [i];
- while (mdesc) {
- monitors [j++] = mdesc;
- mdesc = mdesc->next;
- }
- }
- qsort (monitors, num_monitors, sizeof (void*), compare_monitor);
- fprintf (outfile, "\nMonitor lock summary\n");
- for (i = 0; i < num_monitors; ++i) {
- MonitorDesc *mdesc = monitors [i];
- fprintf (outfile, "\tLock object %p: %d contentions\n", (void*)mdesc->objid, (int)mdesc->contentions);
- fprintf (outfile, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
- mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
- dump_traces (&mdesc->traces, "contentions");
- }
- fprintf (outfile, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention);
- fprintf (outfile, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired);
- fprintf (outfile, "\tLock failures: %llu\n", (unsigned long long) monitor_failed);
-}
-
-static void
-dump_gcs (void)
-{
- int i;
- fprintf (outfile, "\nGC summary\n");
- fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
- fprintf (outfile, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size);
- fprintf (outfile, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves);
- for (i = 0; i < 3; ++i) {
- if (!gc_info [i].count)
- continue;
- fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
- i, gc_info [i].count,
- (unsigned long long) (gc_info [i].max_time / 1000),
- (unsigned long long) (gc_info [i].total_time / 1000),
- (unsigned long long) (gc_info [i].total_time / gc_info [i].count / 1000));
- }
- for (i = 0; i < 3; ++i) {
- if (!handle_info [i].max_live)
- continue;
- fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
- get_handle_name (i),
- (unsigned long long) (handle_info [i].created),
- (unsigned long long) (handle_info [i].destroyed),
- (unsigned long long) (handle_info [i].max_live));
- dump_traces (&handle_info [i].traces, "created");
- dump_traces (&handle_info [i].destroy_traces, "destroyed");
- }
-}
-
-static void
-dump_jit (void)
-{
- int i;
- int code_size = 0;
- int compiled_methods = 0;
- MethodDesc* m;
- fprintf (outfile, "\nJIT summary\n");
- for (i = 0; i < HASH_SIZE; ++i) {
- m = method_hash [i];
- for (m = method_hash [i]; m; m = m->next) {
- if (!m->code || m->ignore_jit)
- continue;
- compiled_methods++;
- code_size += m->len;
- }
- }
- fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
- fprintf (outfile, "\tGenerated code size: %d\n", code_size);
- fprintf (outfile, "\tJIT helpers: %d\n", num_jit_helpers);
- fprintf (outfile, "\tJIT helpers code size: %d\n", jit_helpers_code_size);
-}
-
-static void
-dump_allocations (void)
-{
- int i, c;
- intptr_t allocs = 0;
- uint64_t size = 0;
- int header_done = 0;
- ClassDesc **classes = (ClassDesc **)malloc (num_classes * sizeof (void*));
- ClassDesc *cd;
- c = 0;
- for (i = 0; i < HASH_SIZE; ++i) {
- cd = class_hash [i];
- while (cd) {
- classes [c++] = cd;
- cd = cd->next;
- }
- }
- qsort (classes, num_classes, sizeof (void*), compare_class);
- for (i = 0; i < num_classes; ++i) {
- cd = classes [i];
- if (!cd->allocs)
- continue;
- allocs += cd->allocs;
- size += cd->alloc_size;
- if (!header_done++) {
- fprintf (outfile, "\nAllocation summary\n");
- fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
- }
- fprintf (outfile, "%10llu %10zd %8llu %s\n",
- (unsigned long long) (cd->alloc_size),
- cd->allocs,
- (unsigned long long) (cd->alloc_size / cd->allocs),
- cd->name);
- dump_traces (&cd->traces, "bytes");
- }
- if (allocs)
- fprintf (outfile, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size, allocs);
-}
-
-enum {
- METHOD_SORT_TOTAL,
- METHOD_SORT_SELF,
- METHOD_SORT_CALLS
-};
-
-static int method_sort_mode = METHOD_SORT_TOTAL;
-
-static int
-compare_method (const void *a, const void *b)
-{
- MethodDesc *const *A = (MethodDesc *const *)a;
- MethodDesc *const *B = (MethodDesc *const *)b;
- uint64_t vala, valb;
- if (method_sort_mode == METHOD_SORT_SELF) {
- vala = (*A)->self_time;
- valb = (*B)->self_time;
- } else if (method_sort_mode == METHOD_SORT_CALLS) {
- vala = (*A)->calls;
- valb = (*B)->calls;
- } else {
- vala = (*A)->total_time;
- valb = (*B)->total_time;
- }
- if (vala == valb)
- return 0;
- if (valb < vala)
- return -1;
- return 1;
-}
-
-static void
-dump_metadata (void)
-{
- fprintf (outfile, "\nMetadata summary\n");
- fprintf (outfile, "\tLoaded images: %d\n", num_images);
- if (verbose) {
- ImageDesc *image;
- int i;
- for (i = 0; i < SMALL_HASH_SIZE; ++i) {
- image = image_hash [i];
- while (image) {
- fprintf (outfile, "\t\t%s\n", image->filename);
- image = image->next;
- }
- }
- }
- fprintf (outfile, "\tLoaded assemblies: %d\n", num_assemblies);
- if (verbose) {
- AssemblyDesc *assembly;
- int i;
- for (i = 0; i < SMALL_HASH_SIZE; ++i) {
- assembly = assembly_hash [i];
- while (assembly) {
- fprintf (outfile, "\t\t%s\n", assembly->asmname);
- assembly = assembly->next;
- }
- }
- }
-}
-
-static void
-dump_methods (void)
-{
- int i, c;
- uint64_t calls = 0;
- int header_done = 0;
- MethodDesc **methods = (MethodDesc **)malloc (num_methods * sizeof (void*));
- MethodDesc *cd;
- c = 0;
- for (i = 0; i < HASH_SIZE; ++i) {
- cd = method_hash [i];
- while (cd) {
- cd->total_time = cd->self_time + cd->callee_time;
- methods [c++] = cd;
- cd = cd->next;
- }
- }
- qsort (methods, num_methods, sizeof (void*), compare_method);
- for (i = 0; i < num_methods; ++i) {
- uint64_t msecs;
- uint64_t smsecs;
- cd = methods [i];
- if (!cd->calls)
- continue;
- calls += cd->calls;
- msecs = cd->total_time / 1000000;
- smsecs = (cd->total_time - cd->callee_time) / 1000000;
- if (!msecs && !verbose)
- continue;
- if (!header_done++) {
- fprintf (outfile, "\nMethod call summary\n");
- fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
- }
- fprintf (outfile, "%8llu %8llu %10llu %s\n",
- (unsigned long long) (msecs),
- (unsigned long long) (smsecs),
- (unsigned long long) (cd->calls),
- cd->name);
- dump_traces (&cd->traces, "calls");
- }
- if (calls)
- fprintf (outfile, "Total calls: %llu\n", (unsigned long long) calls);
-}
-
-static int
-compare_heap_class (const void *a, const void *b)
-{
- HeapClassDesc *const *A = (HeapClassDesc *const *)a;
- HeapClassDesc *const *B = (HeapClassDesc *const *)b;
- uint64_t vala, valb;
- if (alloc_sort_mode == ALLOC_SORT_BYTES) {
- vala = (*A)->total_size;
- valb = (*B)->total_size;
- } else {
- vala = (*A)->count;
- valb = (*B)->count;
- }
- if (valb == vala)
- return 0;
- if (valb < vala)
- return -1;
- return 1;
-}
-
-static int
-compare_rev_class (const void *a, const void *b)
-{
- const HeapClassRevRef *A = (const HeapClassRevRef *)a;
- const HeapClassRevRef *B = (const HeapClassRevRef *)b;
- if (B->count == A->count)
- return 0;
- if (B->count < A->count)
- return -1;
- return 1;
-}
-
-static void
-dump_rev_claases (HeapClassRevRef *revs, int count)
-{
- int j;
- if (!show_traces)
- return;
- if (!count)
- return;
- for (j = 0; j < count; ++j) {
- HeapClassDesc *cd = revs [j].klass;
- fprintf (outfile, "\t\t%llu references from: %s\n",
- (unsigned long long) (revs [j].count),
- cd->klass->name);
- }
-}
-
-static void
-heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
-{
- uint64_t size = 0;
- uint64_t count = 0;
- int ccount = 0;
- int i;
- HeapClassDesc *cd;
- HeapClassDesc **sorted;
- sorted = (HeapClassDesc **)malloc (sizeof (void*) * hs->class_count);
- for (i = 0; i < hs->hash_size; ++i) {
- cd = hs->class_hash [i];
- if (!cd)
- continue;
- count += cd->count;
- size += cd->total_size;
- sorted [ccount++] = cd;
- }
- hs->sorted = sorted;
- qsort (sorted, ccount, sizeof (void*), compare_heap_class);
- fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
- hs_num,
- (hs->timestamp - startup_time)/1000000000.0,
- (unsigned long long) (size),
- (unsigned long long) (count),
- ccount, hs->num_roots);
- if (!verbose && ccount > 30)
- ccount = 30;
- fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
- for (i = 0; i < ccount; ++i) {
- HeapClassRevRef *rev_sorted;
- int j, k;
- HeapClassDesc *ocd = NULL;
- cd = sorted [i];
- if (last_hs)
- ocd = heap_class_lookup (last_hs, cd->klass);
- fprintf (outfile, "\t%10llu %10llu %8llu %s",
- (unsigned long long) (cd->total_size),
- (unsigned long long) (cd->count),
- (unsigned long long) (cd->total_size / cd->count),
- cd->klass->name);
- if (ocd) {
- int64_t bdiff = cd->total_size - ocd->total_size;
- int64_t cdiff = cd->count - ocd->count;
- fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff, (long long) cdiff);
- } else {
- fprintf (outfile, "\n");
- }
- if (!collect_traces)
- continue;
- rev_sorted = (HeapClassRevRef *)malloc (cd->rev_count * sizeof (HeapClassRevRef));
- k = 0;
- for (j = 0; j < cd->rev_hash_size; ++j) {
- if (cd->rev_hash [j].klass)
- rev_sorted [k++] = cd->rev_hash [j];
- }
- assert (cd->rev_count == k);
- qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
- if (cd->root_references)
- fprintf (outfile, "\t\t%zd root references (%zd pinning)\n", cd->root_references, cd->pinned_references);
- dump_rev_claases (rev_sorted, cd->rev_count);
- g_free (rev_sorted);
- }
- g_free (sorted);
-}
-
-static int
-compare_heap_shots (const void *a, const void *b)
-{
- HeapShot *const *A = (HeapShot *const *)a;
- HeapShot *const *B = (HeapShot *const *)b;
- if ((*B)->timestamp == (*A)->timestamp)
- return 0;
- if ((*B)->timestamp > (*A)->timestamp)
- return -1;
- return 1;
-}
-
-static void
-dump_heap_shots (void)
-{
- HeapShot **hs_sorted;
- HeapShot *hs;
- HeapShot *last_hs = NULL;
- int i;
- if (!heap_shots)
- return;
- hs_sorted = (HeapShot **)malloc (num_heap_shots * sizeof (void*));
- fprintf (outfile, "\nHeap shot summary\n");
- i = 0;
- for (hs = heap_shots; hs; hs = hs->next)
- hs_sorted [i++] = hs;
- qsort (hs_sorted, num_heap_shots, sizeof (void*), compare_heap_shots);
- for (i = 0; i < num_heap_shots; ++i) {
- hs = hs_sorted [i];
- heap_shot_summary (hs, i, last_hs);
- last_hs = hs;
- }
-}
-
-/* This is a very basic escape function that escapes < > and &
- Ideally we'd use g_markup_escape_string but that function isn't
- available in Mono's eglib. This was written without looking at the
- source of that function in glib. */
-static char *
-escape_string_for_xml (const char *string)
-{
- GString *string_builder = g_string_new (NULL);
- const char *start, *p;
-
- start = p = string;
- while (*p) {
- while (*p && *p != '&' && *p != '<' && *p != '>')
- p++;
-
- g_string_append_len (string_builder, start, p - start);
-
- if (*p == '\0')
- break;
-
- switch (*p) {
- case '<':
- g_string_append (string_builder, "<");
- break;
-
- case '>':
- g_string_append (string_builder, ">");
- break;
-
- case '&':
- g_string_append (string_builder, "&");
- break;
-
- default:
- break;
- }
-
- p++;
- start = p;
- }
-
- return g_string_free (string_builder, FALSE);
-}
-
-static int
-sort_assemblies (gconstpointer a, gconstpointer b)
-{
- CoverageAssembly *assembly_a = *(CoverageAssembly **)a;
- CoverageAssembly *assembly_b = *(CoverageAssembly **)b;
-
- if (assembly_a->name == NULL && assembly_b->name == NULL)
- return 0;
- else if (assembly_a->name == NULL)
- return -1;
- else if (assembly_b->name == NULL)
- return 1;
-
- return strcmp (assembly_a->name, assembly_b->name);
-}
-
-static void
-dump_coverage (void)
-{
- if (!coverage_methods && !coverage_assemblies)
- return;
-
- gather_coverage_statements ();
- fprintf (outfile, "\nCoverage Summary:\n");
-
- if (coverage_outfile) {
- fprintf (coverage_outfile, "<?xml version=\"1.0\"?>\n");
- fprintf (coverage_outfile, "<coverage version=\"0.3\">\n");
- }
-
- g_ptr_array_sort (coverage_assemblies, sort_assemblies);
-
- for (guint i = 0; i < coverage_assemblies->len; i++) {
- CoverageAssembly *assembly = (CoverageAssembly *)coverage_assemblies->pdata[i];
- GPtrArray *classes;
-
- if (assembly->number_of_methods != 0) {
- int percentage = ((assembly->fully_covered + assembly->partially_covered) * 100) / assembly->number_of_methods;
- fprintf (outfile, "\t%s (%s) %d%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, percentage, assembly->number_of_methods, assembly->fully_covered);
- } else
- fprintf (outfile, "\t%s (%s) ?%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, assembly->number_of_methods, assembly->fully_covered);
-
- if (coverage_outfile) {
- char *escaped_name, *escaped_filename;
- escaped_name = escape_string_for_xml (assembly->name);
- escaped_filename = escape_string_for_xml (assembly->filename);
-
- fprintf (coverage_outfile, "\t<assembly name=\"%s\" guid=\"%s\" filename=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n", escaped_name, assembly->guid, escaped_filename, assembly->number_of_methods, assembly->fully_covered, assembly->partially_covered);
-
- g_free (escaped_name);
- g_free (escaped_filename);
- }
-
- classes = (GPtrArray *)g_hash_table_lookup (coverage_assembly_classes, assembly->name);
- if (classes) {
- for (guint j = 0; j < classes->len; j++) {
- CoverageClass *klass = (CoverageClass *)classes->pdata [j];
-
- if (klass->number_of_methods > 0) {
- int percentage = ((klass->fully_covered + klass->partially_covered) * 100) / klass->number_of_methods;
- fprintf (outfile, "\t\t%s %d%% covered (%d methods - %d covered)\n", klass->class_name, percentage, klass->number_of_methods, klass->fully_covered);
- } else
- fprintf (outfile, "\t\t%s ?%% covered (%d methods - %d covered)\n", klass->class_name, klass->number_of_methods, klass->fully_covered);
-
- if (coverage_outfile) {
- char *escaped_name;
- escaped_name = escape_string_for_xml (klass->class_name);
-
- fprintf (coverage_outfile, "\t\t<class name=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n", escaped_name, klass->number_of_methods, klass->fully_covered, klass->partially_covered);
- g_free (escaped_name);
- }
- }
- }
- }
-
- for (guint i = 0; i < coverage_methods->len; i++) {
- CoverageMethod *method = (CoverageMethod *)coverage_methods->pdata [i];
-
- if (coverage_outfile) {
- char *escaped_assembly, *escaped_class, *escaped_method, *escaped_sig, *escaped_filename;
-
- escaped_assembly = escape_string_for_xml (method->assembly_name);
- escaped_class = escape_string_for_xml (method->class_name);
- escaped_method = escape_string_for_xml (method->method_name);
- escaped_sig = escape_string_for_xml (method->method_signature);
- escaped_filename = escape_string_for_xml (method->filename);
-
- fprintf (coverage_outfile, "\t<method assembly=\"%s\" class=\"%s\" name=\"%s (%s)\" filename=\"%s\" token=\"%d\">\n", escaped_assembly, escaped_class, escaped_method, escaped_sig, escaped_filename, method->token);
-
- g_free (escaped_assembly);
- g_free (escaped_class);
- g_free (escaped_method);
- g_free (escaped_sig);
- g_free (escaped_filename);
-
- for (guint j = 0; j < method->coverage->len; j++) {
- CoverageCoverage *coverage = (CoverageCoverage *)method->coverage->pdata [j];
- fprintf (coverage_outfile, "\t\t<statement offset=\"%d\" counter=\"%d\" line=\"%d\" column=\"%d\"/>\n", coverage->offset, coverage->count, coverage->line, coverage->column);
- }
- fprintf (coverage_outfile, "\t</method>\n");
- }
- }
-
- if (coverage_outfile) {
- fprintf (coverage_outfile, "</coverage>\n");
- fclose (coverage_outfile);
- coverage_outfile = NULL;
- }
-}
-
-#define DUMP_EVENT_STAT(EVENT,SUBTYPE) dump_event (#EVENT, #SUBTYPE, EVENT, SUBTYPE);
-
-static void
-dump_event (const char *event_name, const char *subtype_name, int event, int subtype)
-{
- int idx = event | subtype;
- EventStat evt = stats [idx];
- if (!evt.count)
- return;
-
- fprintf (outfile, "\t%16s\t%26s\tcount %6d\tmin %3d\tmax %6d\tbytes %d\n", event_name, subtype_name, evt.count, evt.min_size, evt.max_size, evt.bytes);
-}
-
-static void
-dump_stats (void)
-{
- fprintf (outfile, "\nMlpd statistics\n");
- fprintf (outfile, "\tBuffer count %d\toverhead %d (%d bytes per header)\n", buffer_count, buffer_count * BUFFER_HEADER_SIZE, BUFFER_HEADER_SIZE);
- fprintf (outfile, "\nEvent details:\n");
-
- DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_NO_BT);
- DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_BT);
-
- DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_EVENT);
- DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_RESIZE);
- DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_MOVE);
- DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED);
- DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED);
- DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED_BT);
- DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED_BT);
-
- DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_LOAD);
- DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_UNLOAD);
-
- DUMP_EVENT_STAT (TYPE_METHOD, TYPE_LEAVE);
- DUMP_EVENT_STAT (TYPE_METHOD, TYPE_ENTER);
- DUMP_EVENT_STAT (TYPE_METHOD, TYPE_EXC_LEAVE);
- DUMP_EVENT_STAT (TYPE_METHOD, TYPE_JIT);
-
- DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW_NO_BT);
- DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW_BT);
- DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_CLAUSE);
-
- DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_NO_BT);
- DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_BT);
-
- DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_START);
- DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_END);
- DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_OBJECT);
- DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_ROOT);
-
- DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_HIT);
- DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_USYM);
- DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_UBIN);
- DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS_DESC);
- DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS);
-
- DUMP_EVENT_STAT (TYPE_RUNTIME, TYPE_JITHELPER);
-
- DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_ASSEMBLY);
- 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);
-}
-
-
-
-static void
-flush_context (ProfContext *ctx)
-{
- ThreadContext *thread;
- /* FIXME: sometimes there are leftovers: indagate */
- for (thread = ctx->threads; thread; thread = thread->next) {
- while (thread->stack_id) {
- if (debug)
- fprintf (outfile, "thread %p has %d items on stack\n", (void*)thread->thread_id, thread->stack_id);
- pop_method (thread, thread->stack [thread->stack_id - 1], thread->last_time);
- }
- }
-}
-
-static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot,counters,coverage";
-
-static const char*
-match_option (const char *p, const char *opt)
-{
- int len = strlen (opt);
- if (strncmp (p, opt, len) == 0) {
- if (p [len] == ',')
- len++;
- return p + len;
- }
- return p;
-}
-
-static int
-print_reports (ProfContext *ctx, const char *reps, int parse_only)
-{
- const char *opt;
- const char *p;
- for (p = reps; *p; p = opt) {
- if ((opt = match_option (p, "header")) != p) {
- if (!parse_only)
- dump_header (ctx);
- continue;
- }
- if ((opt = match_option (p, "thread")) != p) {
- if (!parse_only)
- dump_threads (ctx);
- continue;
- }
- if ((opt = match_option (p, "domain")) != p) {
- if (!parse_only)
- dump_domains (ctx);
- continue;
- }
- if ((opt = match_option (p, "context")) != p) {
- if (!parse_only)
- dump_remctxs (ctx);
- continue;
- }
- if ((opt = match_option (p, "gc")) != p) {
- if (!parse_only)
- dump_gcs ();
- continue;
- }
- if ((opt = match_option (p, "jit")) != p) {
- if (!parse_only)
- dump_jit ();
- continue;
- }
- if ((opt = match_option (p, "alloc")) != p) {
- if (!parse_only)
- dump_allocations ();
- continue;
- }
- if ((opt = match_option (p, "call")) != p) {
- if (!parse_only)
- dump_methods ();
- continue;
- }
- if ((opt = match_option (p, "metadata")) != p) {
- if (!parse_only)
- dump_metadata ();
- continue;
- }
- if ((opt = match_option (p, "exception")) != p) {
- if (!parse_only)
- dump_exceptions ();
- continue;
- }
- if ((opt = match_option (p, "monitor")) != p) {
- if (!parse_only)
- dump_monitors ();
- continue;
- }
- if ((opt = match_option (p, "heapshot")) != p) {
- if (!parse_only)
- dump_heap_shots ();
- continue;
- }
- if ((opt = match_option (p, "sample")) != p) {
- if (!parse_only)
- dump_samples ();
- continue;
- }
- if ((opt = match_option (p, "counters")) != p) {
- if (!parse_only)
- dump_counters ();
- continue;
- }
- if ((opt = match_option (p, "coverage")) != p) {
- if (!parse_only)
- dump_coverage ();
- continue;
- }
- if ((opt = match_option (p, "stats")) != p) {
- if (!parse_only)
- dump_stats ();
- continue;
- }
- return 0;
- }
- return 1;
-}
-
-static int
-add_find_spec (const char *p)
-{
- if (p [0] == 'S' && p [1] == ':') {
- char *vale;
- find_size = strtoul (p + 2, &vale, 10);
- return 1;
- } else if (p [0] == 'T' && p [1] == ':') {
- find_name = p + 2;
- return 1;
- }
- return 0;
-}
-
-static void
-usage (void)
-{
- printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR);
- printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
- printf ("FILENAME can be '-' to read from standard input.\n");
- printf ("Options:\n");
- printf ("\t--help display this help\n");
- printf ("\t--out=FILE write to FILE instead of stdout\n");
- printf ("\t--traces collect and show backtraces\n");
- printf ("\t--maxframes=NUM limit backtraces to NUM entries\n");
- printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
- printf ("\t %s\n", reports);
- printf ("\t--method-sort=MODE sort methods according to MODE: total, self, calls\n");
- printf ("\t--alloc-sort=MODE sort allocations according to MODE: bytes, count\n");
- printf ("\t--counters-sort=MODE sort counters according to MODE: time, category\n");
- printf ("\t only accessible in verbose mode\n");
- printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
- printf ("\t--find=FINDSPEC find and track objects matching FINFSPEC, where FINDSPEC is:\n");
- printf ("\t S:minimum_size or T:partial_name\n");
- printf ("\t--thread=THREADID consider just the data for thread THREADID\n");
- printf ("\t--time=FROM-TO consider data FROM seconds from startup up to TO seconds\n");
- printf ("\t--verbose increase verbosity level\n");
- printf ("\t--debug display decoding debug info for mprof-report devs\n");
- printf ("\t--coverage-out=FILE write the coverage info to FILE as XML\n");
-}
-
-int
-main (int argc, char *argv[])
-{
- ProfContext *ctx;
- int i;
- outfile = stdout;
- for (i = 1; i < argc; ++i) {
- if (strcmp ("--debug", argv [i]) == 0) {
- debug++;
- } else if (strcmp ("--help", argv [i]) == 0) {
- usage ();
- return 0;
- } else if (strncmp ("--alloc-sort=", argv [i], 13) == 0) {
- const char *val = argv [i] + 13;
- if (strcmp (val, "bytes") == 0) {
- alloc_sort_mode = ALLOC_SORT_BYTES;
- } else if (strcmp (val, "count") == 0) {
- alloc_sort_mode = ALLOC_SORT_COUNT;
- } else {
- usage ();
- return 1;
- }
- } else if (strncmp ("--method-sort=", argv [i], 14) == 0) {
- const char *val = argv [i] + 14;
- if (strcmp (val, "total") == 0) {
- method_sort_mode = METHOD_SORT_TOTAL;
- } else if (strcmp (val, "self") == 0) {
- method_sort_mode = METHOD_SORT_SELF;
- } else if (strcmp (val, "calls") == 0) {
- method_sort_mode = METHOD_SORT_CALLS;
- } else {
- usage ();
- return 1;
- }
- } else if (strncmp ("--counters-sort=", argv [i], 16) == 0) {
- const char *val = argv [i] + 16;
- if (strcmp (val, "time") == 0) {
- counters_sort_mode = COUNTERS_SORT_TIME;
- } else if (strcmp (val, "category") == 0) {
- counters_sort_mode = COUNTERS_SORT_CATEGORY;
- } else {
- usage ();
- return 1;
- }
- } else if (strncmp ("--reports=", argv [i], 10) == 0) {
- const char *val = argv [i] + 10;
- if (!print_reports (NULL, val, 1)) {
- usage ();
- return 1;
- }
- reports = val;
- } else if (strncmp ("--out=", argv [i], 6) == 0) {
- const char *val = argv [i] + 6;
- outfile = fopen (val, "w");
- if (!outfile) {
- printf ("Cannot open output file: %s\n", val);
- return 1;
- }
- } else if (strncmp ("--maxframes=", argv [i], 12) == 0) {
- const char *val = argv [i] + 12;
- char *vale;
- trace_max = strtoul (val, &vale, 10);
- } else if (strncmp ("--find=", argv [i], 7) == 0) {
- const char *val = argv [i] + 7;
- if (!add_find_spec (val)) {
- usage ();
- return 1;
- }
- } else if (strncmp ("--track=", argv [i], 8) == 0) {
- const char *val = argv [i] + 8;
- char *vale;
- while (*val) {
- uintptr_t tracked_obj;
- if (*val == ',') {
- val++;
- continue;
- }
- tracked_obj = strtoul (val, &vale, 0);
- found_object (tracked_obj);
- val = vale;
- }
- } else if (strncmp ("--thread=", argv [i], 9) == 0) {
- const char *val = argv [i] + 9;
- char *vale;
- thread_filter = strtoul (val, &vale, 0);
- } else if (strncmp ("--time=", argv [i], 7) == 0) {
- char *val = pstrdup (argv [i] + 7);
- double from_secs, to_secs;
- char *top = strchr (val, '-');
- if (!top) {
- usage ();
- return 1;
- }
- *top++ = 0;
- from_secs = atof (val);
- to_secs = atof (top);
- g_free (val);
- if (from_secs > to_secs) {
- usage ();
- return 1;
- }
- time_from = from_secs * 1000000000;
- time_to = to_secs * 1000000000;
- use_time_filter = 1;
- } else if (strcmp ("--verbose", argv [i]) == 0) {
- verbose++;
- } else if (strcmp ("--traces", argv [i]) == 0) {
- show_traces = 1;
- collect_traces = 1;
- } else if (strncmp ("--coverage-out=", argv [i], 15) == 0) {
- const char *val = argv [i] + 15;
- coverage_outfile = fopen (val, "w");
- if (!coverage_outfile) {
- printf ("Cannot open output file: %s\n", val);
- return 1;
- }
- } else {
- break;
- }
- }
- if (i >= argc) {
- usage ();
- return 2;
- }
- ctx = load_file (argv [i]);
- if (!ctx) {
- printf ("Not a log profiler data file (or unsupported version).\n");
- return 1;
- }
- while (decode_buffer (ctx));
- flush_context (ctx);
- if (num_tracked_objects)
- return 0;
- print_reports (ctx, reports, 0);
- return 0;
-}
--- /dev/null
+/*
+ * mono-profiler-log.c: mono log profiler
+ *
+ * Authors:
+ * Paolo Molaro (lupus@ximian.com)
+ * Alex Rønne Petersen (alexrp@xamarin.com)
+ *
+ * Copyright 2010 Novell, Inc (http://www.novell.com)
+ * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+ */
+
+#include <config.h>
+#include <mono/metadata/assembly.h>
+#include <mono/metadata/debug-helpers.h>
+#include "../metadata/metadata-internals.h"
+#include <mono/metadata/mono-config.h>
+#include <mono/metadata/mono-gc.h>
+#include <mono/metadata/mono-perfcounters.h>
+#include <mono/metadata/profiler.h>
+#include <mono/utils/atomic.h>
+#include <mono/utils/hazard-pointer.h>
+#include <mono/utils/lock-free-alloc.h>
+#include <mono/utils/lock-free-queue.h>
+#include <mono/utils/mono-conc-hashtable.h>
+#include <mono/utils/mono-counters.h>
+#include <mono/utils/mono-linked-list-set.h>
+#include <mono/utils/mono-membar.h>
+#include <mono/utils/mono-mmap.h>
+#include <mono/utils/mono-os-mutex.h>
+#include <mono/utils/mono-os-semaphore.h>
+#include <mono/utils/mono-threads.h>
+#include <mono/utils/mono-threads-api.h>
+#include "mono-profiler-log.h"
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_LINK_H
+#include <link.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if defined(__APPLE__)
+#include <mach/mach_time.h>
+#endif
+#include <netinet/in.h>
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#include <sys/socket.h>
+#if defined (HAVE_SYS_ZLIB)
+#include <zlib.h>
+#endif
+
+#define BUFFER_SIZE (4096 * 16)
+
+/* Worst-case size in bytes of a 64-bit value encoded with LEB128. */
+#define LEB128_SIZE 10
+
+/* Size of a value encoded as a single byte. */
+#undef BYTE_SIZE // mach/i386/vm_param.h on OS X defines this to 8, but it isn't used for anything.
+#define BYTE_SIZE 1
+
+/* Size in bytes of the event prefix (ID + time). */
+#define EVENT_SIZE (BYTE_SIZE + LEB128_SIZE)
+
+static volatile gint32 runtime_inited;
+static volatile gint32 in_shutdown;
+
+static gboolean no_counters;
+static int nocalls = 0;
+static int notraces = 0;
+static int use_zip = 0;
+static int do_report = 0;
+static int do_heap_shot = 0;
+static int max_call_depth = 100;
+static int command_port = 0;
+static int heapshot_requested = 0;
+static int sample_freq = 0;
+static int do_mono_sample = 0;
+static int do_debug = 0;
+static int do_coverage = 0;
+static gboolean only_coverage;
+static gboolean debug_coverage = FALSE;
+static MonoProfileSamplingMode sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
+static int max_allocated_sample_hits;
+
+// Statistics for internal profiler data structures.
+static gint32 sample_allocations_ctr,
+ buffer_allocations_ctr;
+
+// Statistics for profiler events.
+static gint32 sync_points_ctr,
+ heap_objects_ctr,
+ heap_starts_ctr,
+ heap_ends_ctr,
+ heap_roots_ctr,
+ gc_events_ctr,
+ gc_resizes_ctr,
+ gc_allocs_ctr,
+ gc_moves_ctr,
+ gc_handle_creations_ctr,
+ gc_handle_deletions_ctr,
+ finalize_begins_ctr,
+ finalize_ends_ctr,
+ finalize_object_begins_ctr,
+ finalize_object_ends_ctr,
+ image_loads_ctr,
+ image_unloads_ctr,
+ assembly_loads_ctr,
+ assembly_unloads_ctr,
+ class_loads_ctr,
+ class_unloads_ctr,
+ method_entries_ctr,
+ method_exits_ctr,
+ method_exception_exits_ctr,
+ method_jits_ctr,
+ code_buffers_ctr,
+ exception_throws_ctr,
+ exception_clauses_ctr,
+ monitor_contentions_ctr,
+ monitor_acquisitions_ctr,
+ monitor_failures_ctr,
+ thread_starts_ctr,
+ thread_ends_ctr,
+ thread_names_ctr,
+ domain_loads_ctr,
+ domain_unloads_ctr,
+ domain_names_ctr,
+ context_loads_ctr,
+ context_unloads_ctr,
+ sample_ubins_ctr,
+ sample_usyms_ctr,
+ sample_hits_ctr,
+ counter_descriptors_ctr,
+ counter_samples_ctr,
+ perfcounter_descriptors_ctr,
+ perfcounter_samples_ctr,
+ coverage_methods_ctr,
+ coverage_statements_ctr,
+ coverage_classes_ctr,
+ coverage_assemblies_ctr;
+
+static MonoLinkedListSet profiler_thread_list;
+
+/*
+ * file format:
+ * [header] [buffer]*
+ *
+ * The file is composed by a header followed by 0 or more buffers.
+ * Each buffer contains events that happened on a thread: for a given thread
+ * buffers that appear later in the file are guaranteed to contain events
+ * that happened later in time. Buffers from separate threads could be interleaved,
+ * though.
+ * Buffers are not required to be aligned.
+ *
+ * header format:
+ * [id: 4 bytes] constant value: LOG_HEADER_ID
+ * [major: 1 byte] [minor: 1 byte] major and minor version of the log profiler
+ * [format: 1 byte] version of the data format for the rest of the file
+ * [ptrsize: 1 byte] size in bytes of a pointer in the profiled program
+ * [startup time: 8 bytes] time in milliseconds since the unix epoch when the program started
+ * [timer overhead: 4 bytes] approximate overhead in nanoseconds of the timer
+ * [flags: 4 bytes] file format flags, should be 0 for now
+ * [pid: 4 bytes] pid of the profiled process
+ * [port: 2 bytes] tcp port for server if != 0
+ * [args size: 4 bytes] size of args
+ * [args: string] arguments passed to the profiler
+ * [arch size: 4 bytes] size of arch
+ * [arch: string] architecture the profiler is running on
+ * [os size: 4 bytes] size of os
+ * [os: string] operating system the profiler is running on
+ *
+ * The multiple byte integers are in little-endian format.
+ *
+ * buffer format:
+ * [buffer header] [event]*
+ * Buffers have a fixed-size header followed by 0 or more bytes of event data.
+ * Timing information and other values in the event data are usually stored
+ * as uleb128 or sleb128 integers. To save space, as noted for each item below,
+ * some data is represented as a difference between the actual value and
+ * either the last value of the same type (like for timing information) or
+ * as the difference from a value stored in a buffer header.
+ *
+ * For timing information the data is stored as uleb128, since timing
+ * increases in a monotonic way in each thread: the value is the number of
+ * nanoseconds to add to the last seen timing data in a buffer. The first value
+ * in a buffer will be calculated from the time_base field in the buffer head.
+ *
+ * Object or heap sizes are stored as uleb128.
+ * Pointer differences are stored as sleb128, instead.
+ *
+ * If an unexpected value is found, the rest of the buffer should be ignored,
+ * as generally the later values need the former to be interpreted correctly.
+ *
+ * buffer header format:
+ * [bufid: 4 bytes] constant value: BUF_ID
+ * [len: 4 bytes] size of the data following the buffer header
+ * [time_base: 8 bytes] time base in nanoseconds since an unspecified epoch
+ * [ptr_base: 8 bytes] base value for pointers
+ * [obj_base: 8 bytes] base value for object addresses
+ * [thread id: 8 bytes] system-specific thread ID (pthread_t for example)
+ * [method_base: 8 bytes] base value for MonoMethod pointers
+ *
+ * event format:
+ * [extended info: upper 4 bits] [type: lower 4 bits]
+ * [time diff: uleb128] nanoseconds since last timing
+ * [data]*
+ * The data that follows depends on type and the extended info.
+ * Type is one of the enum values in mono-profiler-log.h: TYPE_ALLOC, TYPE_GC,
+ * TYPE_METADATA, TYPE_METHOD, TYPE_EXCEPTION, TYPE_MONITOR, TYPE_HEAP.
+ * The extended info bits are interpreted based on type, see
+ * each individual event description below.
+ * strings are represented as a 0-terminated utf8 sequence.
+ *
+ * backtrace format:
+ * [num: uleb128] number of frames following
+ * [frame: sleb128]* mum MonoMethod* as a pointer difference from the last such
+ * pointer or the buffer method_base
+ *
+ * type alloc format:
+ * type: TYPE_ALLOC
+ * exinfo: flags: TYPE_ALLOC_BT
+ * [ptr: sleb128] class as a byte difference from ptr_base
+ * [obj: sleb128] object address as a byte difference from obj_base
+ * [size: uleb128] size of the object in the heap
+ * If the TYPE_ALLOC_BT flag is set, a backtrace follows.
+ *
+ * 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_FINALIZE_START, TYPE_GC_FINALIZE_END,
+ * TYPE_GC_FINALIZE_OBJECT_START, TYPE_GC_FINALIZE_OBJECT_END
+ * if exinfo == TYPE_GC_RESIZE
+ * [heap_size: uleb128] new heap size
+ * if exinfo == TYPE_GC_EVENT
+ * [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
+ * num is always an even number: the even items are the old
+ * addresses, the odd numbers are the respective new object addresses
+ * if exinfo == TYPE_GC_HANDLE_CREATED[_BT]
+ * [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
+ * upper bits reserved as flags
+ * [handle: uleb128] GC handle value
+ * [objaddr: sleb128] object pointer differences from obj_base
+ * If exinfo == TYPE_GC_HANDLE_CREATED_BT, a backtrace follows.
+ * if exinfo == TYPE_GC_HANDLE_DESTROYED[_BT]
+ * [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
+ * 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
+ * exinfo: one of: TYPE_END_LOAD, TYPE_END_UNLOAD (optional for TYPE_THREAD and TYPE_DOMAIN)
+ * [mtype: byte] metadata type, one of: TYPE_CLASS, TYPE_IMAGE, TYPE_ASSEMBLY, TYPE_DOMAIN,
+ * TYPE_THREAD, TYPE_CONTEXT
+ * [pointer: sleb128] pointer of the metadata type depending on mtype
+ * if mtype == TYPE_CLASS
+ * [image: sleb128] MonoImage* as a pointer difference from ptr_base
+ * [name: string] full class name
+ * if mtype == TYPE_IMAGE
+ * [name: string] image file name
+ * if mtype == TYPE_ASSEMBLY
+ * [name: string] assembly name
+ * if mtype == TYPE_DOMAIN && exinfo == 0
+ * [name: string] domain friendly name
+ * if mtype == TYPE_CONTEXT
+ * [domain: sleb128] domain id as pointer
+ * if mtype == TYPE_THREAD && exinfo == 0
+ * [name: string] thread name
+ *
+ * type method format:
+ * type: TYPE_METHOD
+ * exinfo: one of: TYPE_LEAVE, TYPE_ENTER, TYPE_EXC_LEAVE, TYPE_JIT
+ * [method: sleb128] MonoMethod* as a pointer difference from the last such
+ * pointer or the buffer method_base
+ * if exinfo == TYPE_JIT
+ * [code address: sleb128] pointer to the native code as a diff from ptr_base
+ * [code size: uleb128] size of the generated code
+ * [name: string] full method name
+ *
+ * type exception format:
+ * type: TYPE_EXCEPTION
+ * exinfo: TYPE_THROW_BT flag or one of: TYPE_CLAUSE
+ * if exinfo == TYPE_CLAUSE
+ * [clause type: byte] MonoExceptionEnum enum value
+ * [clause index: uleb128] index of the current clause
+ * [method: sleb128] MonoMethod* as a pointer difference from the last such
+ * pointer or the buffer method_base
+ * else
+ * [object: sleb128] the exception object as a difference from obj_base
+ * if exinfo has TYPE_THROW_BT set, a backtrace follows.
+ *
+ * type runtime format:
+ * type: TYPE_RUNTIME
+ * exinfo: one of: TYPE_JITHELPER
+ * if exinfo == TYPE_JITHELPER
+ * [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
+ * [name: string] buffer description name
+ *
+ * type monitor format:
+ * type: TYPE_MONITOR
+ * exinfo: TYPE_MONITOR_BT flag and one of: MONO_PROFILER_MONITOR_(CONTENTION|FAIL|DONE)
+ * [object: sleb128] the lock object as a difference from obj_base
+ * if exinfo.low3bits == MONO_PROFILER_MONITOR_CONTENTION
+ * If the TYPE_MONITOR_BT flag is set, a backtrace follows.
+ *
+ * type heap format
+ * type: TYPE_HEAP
+ * exinfo: one of TYPE_HEAP_START, TYPE_HEAP_END, TYPE_HEAP_OBJECT, TYPE_HEAP_ROOT
+ * if exinfo == TYPE_HEAP_OBJECT
+ * [object: sleb128] the object as a difference from obj_base
+ * [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
+ * 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
+ * provide additional referenced objects.
+ * if exinfo == TYPE_HEAP_ROOT
+ * [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: 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 sample format
+ * 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
+ * [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
+ * [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)
+ * [name: string] symbol name
+ * if exinfo == TYPE_SAMPLE_UBIN
+ * [address: sleb128] address where binary has been loaded
+ * [offset: uleb128] file offset of mapping (the same file can be mapped multiple times)
+ * [size: uleb128] memory size
+ * [name: string] binary name
+ * if exinfo == TYPE_SAMPLE_COUNTERS_DESC
+ * [len: uleb128] number of counters
+ * for i = 0 to len
+ * [section: uleb128] section of counter
+ * if section == MONO_COUNTER_PERFCOUNTERS:
+ * [section_name: string] section name of counter
+ * [name: string] name 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
+ * while true:
+ * [index: uleb128] unique index of counter
+ * if index == 0:
+ * break
+ * [type: byte] type of counter value
+ * if type == string:
+ * if value == null:
+ * [0: uleb128] 0 -> value is null
+ * else:
+ * [1: uleb128] 1 -> value is not null
+ * [value: string] counter value
+ * else:
+ * [value: uleb128/sleb128/double] counter value, can be sleb128, uleb128 or double (determined by using type)
+ *
+ * type coverage format
+ * type: TYPE_COVERAGE
+ * exinfo: one of TYPE_COVERAGE_METHOD, TYPE_COVERAGE_STATEMENT, TYPE_COVERAGE_ASSEMBLY, TYPE_COVERAGE_CLASS
+ * if exinfo == TYPE_COVERAGE_METHOD
+ * [assembly: string] name of assembly
+ * [class: string] name of the class
+ * [name: string] name of the method
+ * [signature: string] the signature of the method
+ * [filename: string] the file path of the file that contains this method
+ * [token: uleb128] the method token
+ * [method_id: uleb128] an ID for this data to associate with the buffers of TYPE_COVERAGE_STATEMENTS
+ * [len: uleb128] the number of TYPE_COVERAGE_BUFFERS associated with this method
+ * if exinfo == TYPE_COVERAGE_STATEMENTS
+ * [method_id: uleb128] an the TYPE_COVERAGE_METHOD buffer to associate this with
+ * [offset: uleb128] the il offset relative to the previous offset
+ * [counter: uleb128] the counter for this instruction
+ * [line: uleb128] the line of filename containing this instruction
+ * [column: uleb128] the column containing this instruction
+ * if exinfo == TYPE_COVERAGE_ASSEMBLY
+ * [name: string] assembly name
+ * [guid: string] assembly GUID
+ * [filename: string] assembly filename
+ * [number_of_methods: uleb128] the number of methods in this assembly
+ * [fully_covered: uleb128] the number of fully covered methods
+ * [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.
+ * if exinfo == TYPE_COVERAGE_CLASS
+ * [name: string] assembly name
+ * [class: string] class name
+ * [number_of_methods: uleb128] the number of methods in this class
+ * [fully_covered: uleb128] the number of fully covered methods
+ * [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.
+ *
+ * type meta format:
+ * type: TYPE_META
+ * exinfo: one of: TYPE_SYNC_POINT
+ * 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;
+
+ uint64_t time_base;
+ uint64_t last_time;
+ uintptr_t ptr_base;
+ uintptr_t method_base;
+ uintptr_t last_method;
+ uintptr_t obj_base;
+ uintptr_t thread_id;
+
+ // Bytes allocated for this LogBuffer
+ int size;
+
+ // Start of currently unused space in buffer
+ unsigned char* cursor;
+
+ // Pointer to start-of-structure-plus-size (for convenience)
+ unsigned char* buf_end;
+
+ // Start of data in buffer. Contents follow "buffer format" described above.
+ unsigned char buf [1];
+};
+
+typedef struct {
+ MonoLinkedListSetNode node;
+
+ // Convenience pointer to the profiler structure.
+ MonoProfiler *profiler;
+
+ // Was this thread added to the LLS?
+ gboolean attached;
+
+ // 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`.
+ gboolean busy;
+
+ // Has this thread written a thread end event to `buffer`?
+ gboolean ended;
+} MonoProfilerThread;
+
+static uintptr_t
+thread_id (void)
+{
+ return (uintptr_t) mono_native_thread_id_get ();
+}
+
+static uintptr_t
+process_id (void)
+{
+#ifdef HOST_WIN32
+ return (uintptr_t) GetCurrentProcessId ();
+#else
+ return (uintptr_t) getpid ();
+#endif
+}
+
+#ifdef __APPLE__
+static mach_timebase_info_data_t timebase_info;
+#elif defined (HOST_WIN32)
+static LARGE_INTEGER pcounter_freq;
+#endif
+
+#define TICKS_PER_SEC 1000000000LL
+
+static uint64_t
+current_time (void)
+{
+#ifdef __APPLE__
+ uint64_t time = mach_absolute_time ();
+
+ time *= timebase_info.numer;
+ time /= timebase_info.denom;
+
+ return time;
+#elif defined (HOST_WIN32)
+ LARGE_INTEGER value;
+
+ QueryPerformanceCounter (&value);
+
+ return value.QuadPart * TICKS_PER_SEC / pcounter_freq.QuadPart;
+#elif defined (CLOCK_MONOTONIC)
+ struct timespec tspec;
+
+ clock_gettime (CLOCK_MONOTONIC, &tspec);
+
+ return ((uint64_t) tspec.tv_sec * TICKS_PER_SEC + tspec.tv_nsec);
+#else
+ struct timeval tv;
+
+ gettimeofday (&tv, NULL);
+
+ return ((uint64_t) tv.tv_sec * TICKS_PER_SEC + tv.tv_usec * 1000);
+#endif
+}
+
+static int timer_overhead;
+
+static void
+init_time (void)
+{
+#ifdef __APPLE__
+ mach_timebase_info (&timebase_info);
+#elif defined (HOST_WIN32)
+ QueryPerformanceFrequency (&pcounter_freq);
+#endif
+
+ uint64_t time_start = current_time ();
+
+ for (int i = 0; i < 256; ++i)
+ current_time ();
+
+ uint64_t time_end = current_time ();
+
+ timer_overhead = (time_end - time_start) / 256;
+}
+
+/*
+ * These macros should be used when writing an event to a log buffer. They take
+ * care of a bunch of stuff that can be repetitive and error-prone, such as
+ * acquiring/releasing the buffer lock, incrementing the event counter,
+ * expanding the log buffer, processing requests, etc. They also create a scope
+ * so that it's harder to leak the LogBuffer pointer, which can be problematic
+ * as the pointer is unstable when the buffer lock isn't acquired.
+ */
+
+#define ENTER_LOG(COUNTER, BUFFER, SIZE) \
+ do { \
+ MonoProfilerThread *thread__ = PROF_TLS_GET (); \
+ if (thread__->attached) \
+ buffer_lock (); \
+ g_assert (!thread__->busy && "Why are we trying to write a new event while already writing one?"); \
+ thread__->busy = TRUE; \
+ InterlockedIncrement ((COUNTER)); \
+ LogBuffer *BUFFER = ensure_logbuf_unsafe (thread__, (SIZE))
+
+#define EXIT_LOG_EXPLICIT(SEND, REQUESTS) \
+ thread__->busy = FALSE; \
+ if ((SEND)) \
+ send_log_unsafe (TRUE); \
+ if (thread__->attached) \
+ buffer_unlock (); \
+ if ((REQUESTS)) \
+ process_requests (); \
+ } while (0)
+
+// Pass these to EXIT_LOG_EXPLICIT () for easier reading.
+#define DO_SEND TRUE
+#define NO_SEND FALSE
+#define DO_REQUESTS TRUE
+#define NO_REQUESTS FALSE
+
+#define EXIT_LOG EXIT_LOG_EXPLICIT (DO_SEND, DO_REQUESTS)
+
+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;
+ char *name;
+};
+
+struct _MonoProfiler {
+ FILE* file;
+#if defined (HAVE_SYS_ZLIB)
+ gzFile gzfile;
+#endif
+ char *args;
+ uint64_t startup_time;
+ int pipe_output;
+ int command_port;
+ int server_socket;
+ int pipes [2];
+ MonoNativeThreadId helper_thread;
+ MonoNativeThreadId writer_thread;
+ MonoNativeThreadId dumper_thread;
+ volatile gint32 run_writer_thread;
+ MonoLockFreeAllocSizeClass writer_entry_size_class;
+ MonoLockFreeAllocator writer_entry_allocator;
+ MonoLockFreeQueue writer_queue;
+ MonoSemType writer_queue_sem;
+ MonoConcurrentHashTable *method_table;
+ mono_mutex_t method_table_mutex;
+ volatile gint32 run_dumper_thread;
+ MonoLockFreeQueue dumper_queue;
+ MonoSemType dumper_queue_sem;
+ MonoLockFreeAllocSizeClass sample_size_class;
+ MonoLockFreeAllocator sample_allocator;
+ MonoLockFreeQueue sample_reuse_queue;
+ BinaryObject *binary_objects;
+ GPtrArray *coverage_filters;
+};
+
+typedef struct {
+ MonoLockFreeQueueNode node;
+ GPtrArray *methods;
+ LogBuffer *buffer;
+} WriterQueueEntry;
+
+#define WRITER_ENTRY_BLOCK_SIZE (mono_pagesize ())
+
+typedef struct {
+ MonoMethod *method;
+ MonoJitInfo *ji;
+ uint64_t time;
+} MethodInfo;
+
+#ifdef HOST_WIN32
+
+#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 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 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)
+{
+ int len = strlen (s) + 1;
+ char *p = (char *) g_malloc (len);
+ memcpy (p, s, len);
+ return p;
+}
+
+static void *
+alloc_buffer (int size)
+{
+ return mono_valloc (NULL, size, MONO_MMAP_READ | MONO_MMAP_WRITE | MONO_MMAP_ANON | MONO_MMAP_PRIVATE, MONO_MEM_ACCOUNT_PROFILER);
+}
+
+static void
+free_buffer (void *buf, int size)
+{
+ mono_vfree (buf, size, MONO_MEM_ACCOUNT_PROFILER);
+}
+
+static LogBuffer*
+create_buffer (uintptr_t tid)
+{
+ LogBuffer* buf = (LogBuffer *) alloc_buffer (BUFFER_SIZE);
+
+ InterlockedIncrement (&buffer_allocations_ctr);
+
+ buf->size = BUFFER_SIZE;
+ buf->time_base = current_time ();
+ buf->last_time = buf->time_base;
+ buf->buf_end = (unsigned char *) buf + buf->size;
+ buf->cursor = buf->buf;
+ buf->thread_id = tid;
+
+ return buf;
+}
+
+/*
+ * Must be called with the reader lock held if thread is the current thread, or
+ * the exclusive lock if thread is a different thread. However, if thread is
+ * the current thread, and init_thread () was called with add_to_lls = FALSE,
+ * then no locking is necessary.
+ */
+static void
+init_buffer_state (MonoProfilerThread *thread)
+{
+ thread->buffer = create_buffer (thread->node.key);
+ 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 (MonoProfiler *prof, 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 = g_malloc (sizeof (MonoProfilerThread));
+ thread->node.key = thread_id ();
+ thread->profiler = prof;
+ thread->attached = add_to_lls;
+ thread->call_depth = 0;
+ thread->busy = 0;
+ thread->ended = FALSE;
+
+ 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);
+ }
+
+ PROF_TLS_SET (thread);
+
+ return thread;
+}
+
+// Only valid if init_thread () was called with add_to_lls = FALSE.
+static void
+deinit_thread (MonoProfilerThread *thread)
+{
+ g_assert (!thread->attached && "Why are we manually freeing an attached thread?");
+
+ g_free (thread);
+ PROF_TLS_SET (NULL);
+}
+
+// Only valid if init_thread () was called with add_to_lls = FALSE.
+static LogBuffer *
+ensure_logbuf_unsafe (MonoProfilerThread *thread, int bytes)
+{
+ LogBuffer *old = thread->buffer;
+
+ if (old && old->cursor + bytes + 100 < old->buf_end)
+ return old;
+
+ LogBuffer *new_ = create_buffer (thread->node.key);
+ new_->next = old;
+ thread->buffer = new_;
+
+ return new_;
+}
+
+static void
+encode_uleb128 (uint64_t value, uint8_t *buf, uint8_t **endbuf)
+{
+ uint8_t *p = buf;
+
+ do {
+ uint8_t b = value & 0x7f;
+ value >>= 7;
+
+ if (value != 0) /* more bytes to come */
+ b |= 0x80;
+
+ *p ++ = b;
+ } while (value);
+
+ *endbuf = p;
+}
+
+static void
+encode_sleb128 (intptr_t value, uint8_t *buf, uint8_t **endbuf)
+{
+ int more = 1;
+ int negative = (value < 0);
+ unsigned int size = sizeof (intptr_t) * 8;
+ uint8_t byte;
+ uint8_t *p = buf;
+
+ while (more) {
+ byte = value & 0x7f;
+ value >>= 7;
+
+ /* the following is unnecessary if the
+ * implementation of >>= uses an arithmetic rather
+ * than logical shift for a signed left operand
+ */
+ if (negative)
+ /* sign extend */
+ value |= - ((intptr_t) 1 <<(size - 7));
+
+ /* sign bit of byte is second high order bit (0x40) */
+ if ((value == 0 && !(byte & 0x40)) ||
+ (value == -1 && (byte & 0x40)))
+ more = 0;
+ else
+ byte |= 0x80;
+
+ *p ++= byte;
+ }
+
+ *endbuf = p;
+}
+
+static void
+emit_byte (LogBuffer *logbuffer, int value)
+{
+ logbuffer->cursor [0] = value;
+ logbuffer->cursor++;
+
+ g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
+}
+
+static void
+emit_value (LogBuffer *logbuffer, int value)
+{
+ encode_uleb128 (value, logbuffer->cursor, &logbuffer->cursor);
+
+ g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
+}
+
+static void
+emit_time (LogBuffer *logbuffer, uint64_t value)
+{
+ uint64_t tdiff = value - logbuffer->last_time;
+ encode_uleb128 (tdiff, logbuffer->cursor, &logbuffer->cursor);
+ logbuffer->last_time = value;
+
+ g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer 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)
+{
+ encode_sleb128 (value, logbuffer->cursor, &logbuffer->cursor);
+
+ g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
+}
+
+static void
+emit_uvalue (LogBuffer *logbuffer, uint64_t value)
+{
+ encode_uleb128 (value, logbuffer->cursor, &logbuffer->cursor);
+
+ g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
+}
+
+static void
+emit_ptr (LogBuffer *logbuffer, void *ptr)
+{
+ if (!logbuffer->ptr_base)
+ logbuffer->ptr_base = (uintptr_t) ptr;
+
+ emit_svalue (logbuffer, (intptr_t) ptr - logbuffer->ptr_base);
+
+ g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
+}
+
+static void
+emit_method_inner (LogBuffer *logbuffer, void *method)
+{
+ if (!logbuffer->method_base) {
+ logbuffer->method_base = (intptr_t) method;
+ logbuffer->last_method = (intptr_t) method;
+ }
+
+ encode_sleb128 ((intptr_t) ((char *) method - (char *) logbuffer->last_method), logbuffer->cursor, &logbuffer->cursor);
+ logbuffer->last_method = (intptr_t) method;
+
+ g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
+}
+
+static void
+register_method_local (MonoMethod *method, MonoJitInfo *ji)
+{
+ MonoProfilerThread *thread = PROF_TLS_GET ();
+
+ if (!mono_conc_hashtable_lookup (thread->profiler->method_table, method)) {
+ MethodInfo *info = (MethodInfo *) g_malloc (sizeof (MethodInfo));
+
+ info->method = method;
+ info->ji = ji;
+ info->time = current_time ();
+
+ GPtrArray *arr = thread->methods ? thread->methods : (thread->methods = g_ptr_array_new ());
+ g_ptr_array_add (arr, info);
+ }
+}
+
+static void
+emit_method (LogBuffer *logbuffer, MonoMethod *method)
+{
+ register_method_local (method, NULL);
+ emit_method_inner (logbuffer, method);
+}
+
+static void
+emit_obj (LogBuffer *logbuffer, void *ptr)
+{
+ if (!logbuffer->obj_base)
+ logbuffer->obj_base = (uintptr_t) ptr >> 3;
+
+ emit_svalue (logbuffer, ((uintptr_t) ptr >> 3) - logbuffer->obj_base);
+
+ g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
+}
+
+static void
+emit_string (LogBuffer *logbuffer, const char *str, size_t size)
+{
+ size_t i = 0;
+ if (str) {
+ for (; i < size; i++) {
+ if (str[i] == '\0')
+ break;
+ emit_byte (logbuffer, str [i]);
+ }
+ }
+ emit_byte (logbuffer, '\0');
+}
+
+static void
+emit_double (LogBuffer *logbuffer, double value)
+{
+ int i;
+ unsigned char buffer[8];
+ memcpy (buffer, &value, 8);
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ for (i = 7; i >= 0; i--)
+#else
+ for (i = 0; i < 8; i++)
+#endif
+ emit_byte (logbuffer, buffer[i]);
+}
+
+static char*
+write_int16 (char *buf, int32_t value)
+{
+ int i;
+ for (i = 0; i < 2; ++i) {
+ buf [i] = value;
+ value >>= 8;
+ }
+ return buf + 2;
+}
+
+static char*
+write_int32 (char *buf, int32_t value)
+{
+ int i;
+ for (i = 0; i < 4; ++i) {
+ buf [i] = value;
+ value >>= 8;
+ }
+ return buf + 4;
+}
+
+static char*
+write_int64 (char *buf, int64_t value)
+{
+ int i;
+ for (i = 0; i < 8; ++i) {
+ buf [i] = value;
+ value >>= 8;
+ }
+ return buf + 8;
+}
+
+static char *
+write_header_string (char *p, const char *str)
+{
+ size_t len = strlen (str) + 1;
+
+ p = write_int32 (p, len);
+ strcpy (p, str);
+
+ return p + len;
+}
+
+static void
+dump_header (MonoProfiler *profiler)
+{
+ const char *args = profiler->args;
+ const char *arch = mono_config_get_cpu ();
+ const char *os = mono_config_get_os ();
+
+ char *hbuf = g_malloc (
+ sizeof (gint32) /* header id */ +
+ sizeof (gint8) /* major version */ +
+ sizeof (gint8) /* minor version */ +
+ sizeof (gint8) /* data version */ +
+ sizeof (gint8) /* word size */ +
+ sizeof (gint64) /* startup time */ +
+ sizeof (gint32) /* timer overhead */ +
+ sizeof (gint32) /* flags */ +
+ sizeof (gint32) /* process id */ +
+ sizeof (gint16) /* command port */ +
+ sizeof (gint32) + strlen (args) + 1 /* arguments */ +
+ sizeof (gint32) + strlen (arch) + 1 /* architecture */ +
+ sizeof (gint32) + strlen (os) + 1 /* operating system */
+ );
+ char *p = hbuf;
+
+ p = write_int32 (p, LOG_HEADER_ID);
+ *p++ = LOG_VERSION_MAJOR;
+ *p++ = LOG_VERSION_MINOR;
+ *p++ = LOG_DATA_VERSION;
+ *p++ = sizeof (void *);
+ p = write_int64 (p, ((uint64_t) time (NULL)) * 1000);
+ p = write_int32 (p, timer_overhead);
+ p = write_int32 (p, 0); /* flags */
+ p = write_int32 (p, process_id ());
+ p = write_int16 (p, profiler->command_port);
+ p = write_header_string (p, args);
+ p = write_header_string (p, arch);
+ p = write_header_string (p, os);
+
+#if defined (HAVE_SYS_ZLIB)
+ if (profiler->gzfile) {
+ gzwrite (profiler->gzfile, hbuf, p - hbuf);
+ } else
+#endif
+ {
+ fwrite (hbuf, p - hbuf, 1, profiler->file);
+ fflush (profiler->file);
+ }
+
+ g_free (hbuf);
+}
+
+/*
+ * Must be called with the reader lock held if thread is the current thread, or
+ * the exclusive lock if thread is a different thread. However, if thread is
+ * the current thread, and init_thread () was called with add_to_lls = FALSE,
+ * then no locking is necessary.
+ */
+static void
+send_buffer (MonoProfilerThread *thread)
+{
+ WriterQueueEntry *entry = mono_lock_free_alloc (&thread->profiler->writer_entry_allocator);
+ entry->methods = thread->methods;
+ entry->buffer = thread->buffer;
+
+ mono_lock_free_queue_node_init (&entry->node, FALSE);
+
+ mono_lock_free_queue_enqueue (&thread->profiler->writer_queue, &entry->node);
+ mono_os_sem_post (&thread->profiler->writer_queue_sem);
+}
+
+static void
+free_thread (gpointer p)
+{
+ MonoProfilerThread *thread = p;
+
+ if (!thread->ended) {
+ /*
+ * 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.
+ */
+
+ InterlockedIncrement (&thread_ends_ctr);
+
+ LogBuffer *buf = ensure_logbuf_unsafe (thread,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* tid */
+ );
+
+ emit_event (buf, TYPE_END_UNLOAD | TYPE_METADATA);
+ emit_byte (buf, TYPE_THREAD);
+ emit_ptr (buf, (void *) thread->node.key);
+ }
+
+ send_buffer (thread);
+
+ g_free (thread);
+}
+
+static void
+remove_thread (MonoProfilerThread *thread)
+{
+ MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
+
+ if (mono_lls_remove (&profiler_thread_list, hp, &thread->node))
+ mono_thread_hazardous_try_free (thread, free_thread);
+
+ clear_hazard_pointers (hp);
+}
+
+static void
+dump_buffer (MonoProfiler *profiler, LogBuffer *buf)
+{
+ char hbuf [128];
+ char *p = hbuf;
+
+ if (buf->next)
+ dump_buffer (profiler, buf->next);
+
+ if (buf->cursor - buf->buf) {
+ p = write_int32 (p, BUF_ID);
+ p = write_int32 (p, buf->cursor - buf->buf);
+ p = write_int64 (p, buf->time_base);
+ p = write_int64 (p, buf->ptr_base);
+ p = write_int64 (p, buf->obj_base);
+ p = write_int64 (p, buf->thread_id);
+ p = write_int64 (p, buf->method_base);
+
+#if defined (HAVE_SYS_ZLIB)
+ if (profiler->gzfile) {
+ gzwrite (profiler->gzfile, hbuf, p - hbuf);
+ gzwrite (profiler->gzfile, buf->buf, buf->cursor - buf->buf);
+ } else
+#endif
+ {
+ fwrite (hbuf, p - hbuf, 1, profiler->file);
+ fwrite (buf->buf, buf->cursor - buf->buf, 1, profiler->file);
+ fflush (profiler->file);
+ }
+ }
+
+ 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 (void)
+{
+ if (heapshot_requested)
+ mono_gc_collect (mono_gc_max_generation ());
+}
+
+// Only valid if init_thread () was called with add_to_lls = FALSE.
+static void
+send_log_unsafe (gboolean if_needed)
+{
+ MonoProfilerThread *thread = PROF_TLS_GET ();
+
+ if (!if_needed || (if_needed && thread->buffer->next)) {
+ if (!thread->attached)
+ for (LogBuffer *iter = thread->buffer; iter; iter = iter->next)
+ iter->thread_id = 0;
+
+ send_buffer (thread);
+ init_buffer_state (thread);
+ }
+}
+
+// Assumes that the exclusive lock is held.
+static void
+sync_point_flush (void)
+{
+ 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) {
+ g_assert (thread->attached && "Why is a thread in the LLS not attached?");
+
+ send_buffer (thread);
+ init_buffer_state (thread);
+ } MONO_LLS_FOREACH_SAFE_END
+}
+
+// Assumes that the exclusive lock is held.
+static void
+sync_point_mark (MonoProfilerSyncPointType type)
+{
+ g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id () && "Why don't we hold the exclusive lock?");
+
+ ENTER_LOG (&sync_points_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* type */
+ );
+
+ emit_event (logbuffer, TYPE_META | TYPE_SYNC_POINT);
+ emit_byte (logbuffer, type);
+
+ EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
+
+ send_log_unsafe (FALSE);
+}
+
+// Assumes that the exclusive lock is held.
+static void
+sync_point (MonoProfilerSyncPointType type)
+{
+ sync_point_flush ();
+ sync_point_mark (type);
+}
+
+static int
+gc_reference (MonoObject *obj, MonoClass *klass, uintptr_t size, uintptr_t num, MonoObject **refs, uintptr_t *offsets, void *data)
+{
+ /* account for object alignment in the heap */
+ size += 7;
+ size &= ~7;
+
+ ENTER_LOG (&heap_objects_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* obj */ +
+ LEB128_SIZE /* klass */ +
+ LEB128_SIZE /* size */ +
+ LEB128_SIZE /* num */ +
+ num * (
+ LEB128_SIZE /* offset */ +
+ LEB128_SIZE /* ref */
+ )
+ );
+
+ emit_event (logbuffer, TYPE_HEAP_OBJECT | TYPE_HEAP);
+ emit_obj (logbuffer, obj);
+ emit_ptr (logbuffer, klass);
+ emit_value (logbuffer, size);
+ emit_value (logbuffer, num);
+
+ 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]);
+ }
+
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+ return 0;
+}
+
+static unsigned int hs_mode_ms = 0;
+static unsigned int hs_mode_gc = 0;
+static unsigned int hs_mode_ondemand = 0;
+static unsigned int gc_count = 0;
+static uint64_t last_hs_time = 0;
+static gboolean do_heap_walk = FALSE;
+
+static void
+heap_walk (MonoProfiler *profiler)
+{
+ ENTER_LOG (&heap_starts_ctr, logbuffer,
+ EVENT_SIZE /* event */
+ );
+
+ emit_event (logbuffer, TYPE_HEAP_START | TYPE_HEAP);
+
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+ mono_gc_walk_heap (0, gc_reference, NULL);
+
+ ENTER_LOG (&heap_ends_ctr, logbuffer,
+ EVENT_SIZE /* event */
+ );
+
+ emit_event (logbuffer, TYPE_HEAP_END | TYPE_HEAP);
+
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+
+static void
+gc_roots (MonoProfiler *prof, int num, void **objects, int *root_types, uintptr_t *extra_info)
+{
+ ENTER_LOG (&heap_roots_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* num */ +
+ LEB128_SIZE /* collections */ +
+ num * (
+ LEB128_SIZE /* object */ +
+ LEB128_SIZE /* root type */ +
+ LEB128_SIZE /* extra info */
+ )
+ );
+
+ emit_event (logbuffer, TYPE_HEAP_ROOT | TYPE_HEAP);
+ emit_value (logbuffer, num);
+ emit_value (logbuffer, mono_gc_collection_count (mono_gc_max_generation ()));
+
+ for (int i = 0; i < num; ++i) {
+ emit_obj (logbuffer, objects [i]);
+ emit_byte (logbuffer, root_types [i]);
+ emit_value (logbuffer, extra_info [i]);
+ }
+
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+
+static void
+gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation)
+{
+ ENTER_LOG (&gc_events_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* gc event */ +
+ BYTE_SIZE /* generation */
+ );
+
+ emit_event (logbuffer, TYPE_GC_EVENT | TYPE_GC);
+ emit_byte (logbuffer, ev);
+ emit_byte (logbuffer, generation);
+
+ EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
+
+ switch (ev) {
+ case MONO_GC_EVENT_START:
+ if (generation == mono_gc_max_generation ())
+ gc_count++;
+
+ uint64_t now = current_time ();
+
+ if (hs_mode_ms && (now - last_hs_time) / 1000 * 1000 >= hs_mode_ms)
+ do_heap_walk = TRUE;
+ else if (hs_mode_gc && !(gc_count % hs_mode_gc))
+ do_heap_walk = TRUE;
+ else if (hs_mode_ondemand)
+ do_heap_walk = heapshot_requested;
+ else if (!hs_mode_ms && !hs_mode_gc && generation == mono_gc_max_generation ())
+ do_heap_walk = TRUE;
+ 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 (SYNC_POINT_WORLD_STOP);
+ break;
+ case MONO_GC_EVENT_PRE_START_WORLD:
+ if (do_heap_shot && do_heap_walk) {
+ heap_walk (profiler);
+
+ do_heap_walk = FALSE;
+ heapshot_requested = 0;
+ last_hs_time = current_time ();
+ }
+ 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 (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)
+{
+ ENTER_LOG (&gc_resizes_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* new size */
+ );
+
+ emit_event (logbuffer, TYPE_GC_RESIZE | TYPE_GC);
+ emit_value (logbuffer, new_size);
+
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+
+// If you alter MAX_FRAMES, you may need to alter SAMPLE_BLOCK_SIZE too.
+#define MAX_FRAMES 32
+
+typedef struct {
+ int count;
+ MonoMethod* methods [MAX_FRAMES];
+ int32_t il_offsets [MAX_FRAMES];
+ int32_t native_offsets [MAX_FRAMES];
+} FrameData;
+
+static int num_frames = MAX_FRAMES;
+
+static mono_bool
+walk_stack (MonoMethod *method, int32_t native_offset, int32_t il_offset, mono_bool managed, void* data)
+{
+ FrameData *frame = (FrameData *)data;
+ if (method && frame->count < num_frames) {
+ frame->il_offsets [frame->count] = il_offset;
+ frame->native_offsets [frame->count] = native_offset;
+ frame->methods [frame->count++] = method;
+ //printf ("In %d %s at %d (native: %d)\n", frame->count, mono_method_get_name (method), il_offset, native_offset);
+ }
+ return frame->count == num_frames;
+}
+
+/*
+ * a note about stack walks: they can cause more profiler events to fire,
+ * so we need to make sure they don't happen after we started emitting an
+ * event, hence the collect_bt/emit_bt split.
+ */
+static void
+collect_bt (FrameData *data)
+{
+ data->count = 0;
+ mono_stack_walk_no_il (walk_stack, data);
+}
+
+static void
+emit_bt (MonoProfiler *prof, LogBuffer *logbuffer, FrameData *data)
+{
+ /* FIXME: this is actually tons of data and we should
+ * just output it the first time and use an id the next
+ */
+ if (data->count > num_frames)
+ printf ("bad num frames: %d\n", data->count);
+ 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 (logbuffer, data->methods [--data->count]);
+ }
+}
+
+static void
+gc_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
+{
+ init_thread (prof, TRUE);
+
+ int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces) ? TYPE_ALLOC_BT : 0;
+ FrameData data;
+ 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);
+
+ ENTER_LOG (&gc_allocs_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* klass */ +
+ LEB128_SIZE /* obj */ +
+ LEB128_SIZE /* size */ +
+ (do_bt ? (
+ LEB128_SIZE /* count */ +
+ data.count * (
+ LEB128_SIZE /* method */
+ )
+ ) : 0)
+ );
+
+ 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;
+}
+
+static void
+gc_moves (MonoProfiler *prof, void **objects, int num)
+{
+ ENTER_LOG (&gc_moves_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* num */ +
+ num * (
+ LEB128_SIZE /* object */
+ )
+ );
+
+ emit_event (logbuffer, TYPE_GC_MOVE | TYPE_GC);
+ emit_value (logbuffer, num);
+
+ for (int i = 0; i < num; ++i)
+ emit_obj (logbuffer, objects [i]);
+
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+
+static void
+gc_handle (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj)
+{
+ int do_bt = nocalls && InterlockedRead (&runtime_inited) && !notraces;
+ FrameData data;
+
+ if (do_bt)
+ collect_bt (&data);
+
+ gint32 *ctr = op == MONO_PROFILER_GC_HANDLE_CREATED ? &gc_handle_creations_ctr : &gc_handle_deletions_ctr;
+
+ ENTER_LOG (ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* type */ +
+ LEB128_SIZE /* handle */ +
+ (op == MONO_PROFILER_GC_HANDLE_CREATED ? (
+ LEB128_SIZE /* obj */
+ ) : 0) +
+ (do_bt ? (
+ LEB128_SIZE /* count */ +
+ data.count * (
+ LEB128_SIZE /* method */
+ )
+ ) : 0)
+ );
+
+ if (op == MONO_PROFILER_GC_HANDLE_CREATED)
+ 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_event (logbuffer, (do_bt ? TYPE_GC_HANDLE_DESTROYED_BT : TYPE_GC_HANDLE_DESTROYED) | TYPE_GC);
+ else
+ g_assert_not_reached ();
+
+ emit_value (logbuffer, type);
+ emit_value (logbuffer, handle);
+
+ if (op == MONO_PROFILER_GC_HANDLE_CREATED)
+ emit_obj (logbuffer, obj);
+
+ if (do_bt)
+ emit_bt (prof, logbuffer, &data);
+
+ EXIT_LOG;
+}
+
+static void
+finalize_begin (MonoProfiler *prof)
+{
+ ENTER_LOG (&finalize_begins_ctr, buf,
+ EVENT_SIZE /* event */
+ );
+
+ emit_event (buf, TYPE_GC_FINALIZE_START | TYPE_GC);
+
+ EXIT_LOG;
+}
+
+static void
+finalize_end (MonoProfiler *prof)
+{
+ ENTER_LOG (&finalize_ends_ctr, buf,
+ EVENT_SIZE /* event */
+ );
+
+ emit_event (buf, TYPE_GC_FINALIZE_END | TYPE_GC);
+
+ EXIT_LOG;
+}
+
+static void
+finalize_object_begin (MonoProfiler *prof, MonoObject *obj)
+{
+ ENTER_LOG (&finalize_object_begins_ctr, buf,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* obj */
+ );
+
+ emit_event (buf, TYPE_GC_FINALIZE_OBJECT_START | TYPE_GC);
+ emit_obj (buf, obj);
+
+ EXIT_LOG;
+}
+
+static void
+finalize_object_end (MonoProfiler *prof, MonoObject *obj)
+{
+ ENTER_LOG (&finalize_object_ends_ctr, buf,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* obj */
+ );
+
+ emit_event (buf, TYPE_GC_FINALIZE_OBJECT_END | TYPE_GC);
+ emit_obj (buf, obj);
+
+ EXIT_LOG;
+}
+
+static char*
+push_nesting (char *p, MonoClass *klass)
+{
+ MonoClass *nesting;
+ const char *name;
+ const char *nspace;
+ nesting = mono_class_get_nesting_type (klass);
+ if (nesting) {
+ p = push_nesting (p, nesting);
+ *p++ = '/';
+ *p = 0;
+ }
+ name = mono_class_get_name (klass);
+ nspace = mono_class_get_namespace (klass);
+ if (*nspace) {
+ strcpy (p, nspace);
+ p += strlen (nspace);
+ *p++ = '.';
+ *p = 0;
+ }
+ strcpy (p, name);
+ p += strlen (name);
+ return p;
+}
+
+static char*
+type_name (MonoClass *klass)
+{
+ char buf [1024];
+ char *p;
+ push_nesting (buf, klass);
+ p = (char *) g_malloc (strlen (buf) + 1);
+ strcpy (p, buf);
+ return p;
+}
+
+static void
+image_loaded (MonoProfiler *prof, MonoImage *image, int result)
+{
+ if (result != MONO_PROFILE_OK)
+ return;
+
+ const char *name = mono_image_get_filename (image);
+ int nlen = strlen (name) + 1;
+
+ ENTER_LOG (&image_loads_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* image */ +
+ nlen /* name */
+ );
+
+ emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
+ emit_byte (logbuffer, TYPE_IMAGE);
+ emit_ptr (logbuffer, image);
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
+
+ EXIT_LOG;
+}
+
+static void
+image_unloaded (MonoProfiler *prof, MonoImage *image)
+{
+ const char *name = mono_image_get_filename (image);
+ int nlen = strlen (name) + 1;
+
+ ENTER_LOG (&image_unloads_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* image */ +
+ nlen /* name */
+ );
+
+ emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+ emit_byte (logbuffer, TYPE_IMAGE);
+ emit_ptr (logbuffer, image);
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
+
+ EXIT_LOG;
+}
+
+static void
+assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly, int result)
+{
+ if (result != MONO_PROFILE_OK)
+ return;
+
+ char *name = mono_stringify_assembly_name (mono_assembly_get_name (assembly));
+ int nlen = strlen (name) + 1;
+
+ ENTER_LOG (&assembly_loads_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* assembly */ +
+ nlen /* name */
+ );
+
+ emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
+ emit_byte (logbuffer, TYPE_ASSEMBLY);
+ emit_ptr (logbuffer, assembly);
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
+
+ EXIT_LOG;
+
+ mono_free (name);
+}
+
+static void
+assembly_unloaded (MonoProfiler *prof, MonoAssembly *assembly)
+{
+ char *name = mono_stringify_assembly_name (mono_assembly_get_name (assembly));
+ int nlen = strlen (name) + 1;
+
+ ENTER_LOG (&assembly_unloads_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* assembly */ +
+ nlen /* name */
+ );
+
+ emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+ emit_byte (logbuffer, TYPE_ASSEMBLY);
+ emit_ptr (logbuffer, assembly);
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
+
+ EXIT_LOG;
+
+ mono_free (name);
+}
+
+static void
+class_loaded (MonoProfiler *prof, MonoClass *klass, int result)
+{
+ 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);
+
+ int nlen = strlen (name) + 1;
+ MonoImage *image = mono_class_get_image (klass);
+
+ ENTER_LOG (&class_loads_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* klass */ +
+ LEB128_SIZE /* image */ +
+ nlen /* name */
+ );
+
+ emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
+ emit_byte (logbuffer, TYPE_CLASS);
+ emit_ptr (logbuffer, klass);
+ emit_ptr (logbuffer, image);
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
+
+ EXIT_LOG;
+
+ if (runtime_inited)
+ mono_free (name);
+ else
+ g_free (name);
+}
+
+static void
+class_unloaded (MonoProfiler *prof, MonoClass *klass)
+{
+ char *name;
+
+ if (InterlockedRead (&runtime_inited))
+ name = mono_type_get_name (mono_class_get_type (klass));
+ else
+ name = type_name (klass);
+
+ int nlen = strlen (name) + 1;
+ MonoImage *image = mono_class_get_image (klass);
+
+ ENTER_LOG (&class_unloads_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* klass */ +
+ LEB128_SIZE /* image */ +
+ nlen /* name */
+ );
+
+ emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+ emit_byte (logbuffer, TYPE_CLASS);
+ emit_ptr (logbuffer, klass);
+ emit_ptr (logbuffer, image);
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
+
+ EXIT_LOG;
+
+ if (runtime_inited)
+ mono_free (name);
+ else
+ g_free (name);
+}
+
+static void process_method_enter_coverage (MonoProfiler *prof, MonoMethod *method);
+
+static void
+method_enter (MonoProfiler *prof, MonoMethod *method)
+{
+ process_method_enter_coverage (prof, method);
+
+ if (!only_coverage && PROF_TLS_GET ()->call_depth++ <= max_call_depth) {
+ ENTER_LOG (&method_entries_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* method */
+ );
+
+ emit_event (logbuffer, TYPE_ENTER | TYPE_METHOD);
+ emit_method (logbuffer, method);
+
+ EXIT_LOG;
+ }
+}
+
+static void
+method_leave (MonoProfiler *prof, MonoMethod *method)
+{
+ if (!only_coverage && --PROF_TLS_GET ()->call_depth <= max_call_depth) {
+ ENTER_LOG (&method_exits_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* method */
+ );
+
+ emit_event (logbuffer, TYPE_LEAVE | TYPE_METHOD);
+ emit_method (logbuffer, method);
+
+ EXIT_LOG;
+ }
+}
+
+static void
+method_exc_leave (MonoProfiler *prof, MonoMethod *method)
+{
+ if (!only_coverage && !nocalls && --PROF_TLS_GET ()->call_depth <= max_call_depth) {
+ ENTER_LOG (&method_exception_exits_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* method */
+ );
+
+ emit_event (logbuffer, TYPE_EXC_LEAVE | TYPE_METHOD);
+ emit_method (logbuffer, method);
+
+ EXIT_LOG;
+ }
+}
+
+static void
+method_jitted (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *ji, int result)
+{
+ if (result != MONO_PROFILE_OK)
+ return;
+
+ register_method_local (method, ji);
+
+ process_requests ();
+}
+
+static void
+code_buffer_new (MonoProfiler *prof, void *buffer, int size, MonoProfilerCodeBufferType type, void *data)
+{
+ char *name;
+ int nlen;
+
+ if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
+ name = (char *) data;
+ nlen = strlen (name) + 1;
+ } else {
+ name = NULL;
+ nlen = 0;
+ }
+
+ ENTER_LOG (&code_buffers_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* buffer */ +
+ LEB128_SIZE /* size */ +
+ (name ? (
+ nlen /* name */
+ ) : 0)
+ );
+
+ 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;
+}
+
+static void
+throw_exc (MonoProfiler *prof, MonoObject *object)
+{
+ int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces) ? TYPE_THROW_BT : 0;
+ FrameData data;
+
+ if (do_bt)
+ collect_bt (&data);
+
+ ENTER_LOG (&exception_throws_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* object */ +
+ (do_bt ? (
+ LEB128_SIZE /* count */ +
+ data.count * (
+ LEB128_SIZE /* method */
+ )
+ ) : 0)
+ );
+
+ emit_event (logbuffer, do_bt | TYPE_EXCEPTION);
+ emit_obj (logbuffer, object);
+
+ if (do_bt)
+ emit_bt (prof, logbuffer, &data);
+
+ EXIT_LOG;
+}
+
+static void
+clause_exc (MonoProfiler *prof, MonoMethod *method, int clause_type, int clause_num)
+{
+ ENTER_LOG (&exception_clauses_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* clause type */ +
+ LEB128_SIZE /* clause num */ +
+ LEB128_SIZE /* method */
+ );
+
+ emit_event (logbuffer, TYPE_EXCEPTION | TYPE_CLAUSE);
+ emit_byte (logbuffer, clause_type);
+ emit_value (logbuffer, clause_num);
+ emit_method (logbuffer, method);
+
+ EXIT_LOG;
+}
+
+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;
+ FrameData data;
+
+ if (do_bt)
+ collect_bt (&data);
+
+ gint32 *ctr;
+
+ switch (event) {
+ case MONO_PROFILER_MONITOR_CONTENTION:
+ ctr = &monitor_contentions_ctr;
+ break;
+ case MONO_PROFILER_MONITOR_DONE:
+ ctr = &monitor_acquisitions_ctr;
+ break;
+ case MONO_PROFILER_MONITOR_FAIL:
+ ctr = &monitor_failures_ctr;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ ENTER_LOG (ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* object */ +
+ (do_bt ? (
+ LEB128_SIZE /* count */ +
+ data.count * (
+ LEB128_SIZE /* method */
+ )
+ ) : 0)
+ );
+
+ emit_event (logbuffer, (event << 4) | do_bt | TYPE_MONITOR);
+ emit_obj (logbuffer, object);
+
+ if (do_bt)
+ emit_bt (profiler, logbuffer, &data);
+
+ EXIT_LOG;
+}
+
+static void
+thread_start (MonoProfiler *prof, uintptr_t tid)
+{
+ init_thread (prof, TRUE);
+
+ ENTER_LOG (&thread_starts_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* tid */
+ );
+
+ emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
+ emit_byte (logbuffer, TYPE_THREAD);
+ emit_ptr (logbuffer, (void*) tid);
+
+ EXIT_LOG;
+}
+
+static void
+thread_end (MonoProfiler *prof, uintptr_t tid)
+{
+ ENTER_LOG (&thread_ends_ctr, logbuffer,
+ 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);
+
+ EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
+
+ MonoProfilerThread *thread = PROF_TLS_GET ();
+
+ thread->ended = TRUE;
+ remove_thread (thread);
+
+ PROF_TLS_SET (NULL);
+}
+
+static void
+thread_name (MonoProfiler *prof, uintptr_t tid, const char *name)
+{
+ int len = strlen (name) + 1;
+
+ ENTER_LOG (&thread_names_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* tid */ +
+ len /* name */
+ );
+
+ emit_event (logbuffer, TYPE_METADATA);
+ emit_byte (logbuffer, TYPE_THREAD);
+ emit_ptr (logbuffer, (void*)tid);
+ memcpy (logbuffer->cursor, name, len);
+ logbuffer->cursor += len;
+
+ EXIT_LOG;
+}
+
+static void
+domain_loaded (MonoProfiler *prof, MonoDomain *domain, int result)
+{
+ if (result != MONO_PROFILE_OK)
+ return;
+
+ ENTER_LOG (&domain_loads_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* domain id */
+ );
+
+ emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
+ emit_byte (logbuffer, TYPE_DOMAIN);
+ emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
+
+ EXIT_LOG;
+}
+
+static void
+domain_unloaded (MonoProfiler *prof, MonoDomain *domain)
+{
+ ENTER_LOG (&domain_unloads_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* domain id */
+ );
+
+ emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+ emit_byte (logbuffer, TYPE_DOMAIN);
+ emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
+
+ EXIT_LOG;
+}
+
+static void
+domain_name (MonoProfiler *prof, MonoDomain *domain, const char *name)
+{
+ int nlen = strlen (name) + 1;
+
+ ENTER_LOG (&domain_names_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* domain id */ +
+ nlen /* name */
+ );
+
+ emit_event (logbuffer, TYPE_METADATA);
+ emit_byte (logbuffer, TYPE_DOMAIN);
+ emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
+
+ EXIT_LOG;
+}
+
+static void
+context_loaded (MonoProfiler *prof, MonoAppContext *context)
+{
+ ENTER_LOG (&context_loads_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* context id */ +
+ LEB128_SIZE /* domain id */
+ );
+
+ 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_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
+
+ EXIT_LOG;
+}
+
+static void
+context_unloaded (MonoProfiler *prof, MonoAppContext *context)
+{
+ ENTER_LOG (&context_unloads_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* context id */ +
+ LEB128_SIZE /* domain id */
+ );
+
+ 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_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
+
+ EXIT_LOG;
+}
+
+typedef struct {
+ MonoMethod *method;
+ MonoDomain *domain;
+ void *base_address;
+ int offset;
+} AsyncFrameInfo;
+
+typedef struct {
+ MonoLockFreeQueueNode node;
+ MonoProfiler *prof;
+ uint64_t time;
+ uintptr_t tid;
+ void *ip;
+ int count;
+ AsyncFrameInfo frames [MONO_ZERO_LEN_ARRAY];
+} SampleHit;
+
+static mono_bool
+async_walk_stack (MonoMethod *method, MonoDomain *domain, void *base_address, int offset, void *data)
+{
+ SampleHit *sample = (SampleHit *) data;
+
+ if (sample->count < num_frames) {
+ int i = sample->count;
+
+ sample->frames [i].method = method;
+ sample->frames [i].domain = domain;
+ sample->frames [i].base_address = base_address;
+ sample->frames [i].offset = offset;
+
+ sample->count++;
+ }
+
+ return sample->count == num_frames;
+}
+
+#define SAMPLE_SLOT_SIZE(FRAMES) (sizeof (SampleHit) + sizeof (AsyncFrameInfo) * (FRAMES - MONO_ZERO_LEN_ARRAY))
+#define SAMPLE_BLOCK_SIZE (mono_pagesize ())
+
+static void
+enqueue_sample_hit (gpointer p)
+{
+ SampleHit *sample = p;
+
+ mono_lock_free_queue_node_unpoison (&sample->node);
+ mono_lock_free_queue_enqueue (&sample->prof->dumper_queue, &sample->node);
+ mono_os_sem_post (&sample->prof->dumper_queue_sem);
+}
+
+static void
+mono_sample_hit (MonoProfiler *profiler, unsigned char *ip, void *context)
+{
+ /*
+ * Please note: We rely on the runtime loading the profiler with
+ * MONO_DL_EAGER (RTLD_NOW) so that references to runtime functions within
+ * this function (and its siblings) are resolved when the profiler is
+ * loaded. Otherwise, we would potentially invoke the dynamic linker when
+ * invoking runtime functions, which is not async-signal-safe.
+ */
+
+ if (InterlockedRead (&in_shutdown))
+ return;
+
+ SampleHit *sample = (SampleHit *) mono_lock_free_queue_dequeue (&profiler->sample_reuse_queue);
+
+ if (!sample) {
+ /*
+ * If we're out of reusable sample events and we're not allowed to
+ * allocate more, we have no choice but to drop the event.
+ */
+ if (InterlockedRead (&sample_allocations_ctr) >= max_allocated_sample_hits)
+ return;
+
+ sample = mono_lock_free_alloc (&profiler->sample_allocator);
+ sample->prof = profiler;
+ mono_lock_free_queue_node_init (&sample->node, TRUE);
+
+ InterlockedIncrement (&sample_allocations_ctr);
+ }
+
+ sample->count = 0;
+ mono_stack_walk_async_safe (&async_walk_stack, context, sample);
+
+ sample->time = current_time ();
+ sample->tid = thread_id ();
+ sample->ip = ip;
+
+ mono_thread_hazardous_try_free (sample, enqueue_sample_hit);
+}
+
+static uintptr_t *code_pages = 0;
+static int num_code_pages = 0;
+static int size_code_pages = 0;
+#define CPAGE_SHIFT (9)
+#define CPAGE_SIZE (1 << CPAGE_SHIFT)
+#define CPAGE_MASK (~(CPAGE_SIZE - 1))
+#define CPAGE_ADDR(p) ((p) & CPAGE_MASK)
+
+static uintptr_t
+add_code_page (uintptr_t *hash, uintptr_t hsize, uintptr_t page)
+{
+ uintptr_t i;
+ uintptr_t start_pos;
+ start_pos = (page >> CPAGE_SHIFT) % hsize;
+ i = start_pos;
+ do {
+ if (hash [i] && CPAGE_ADDR (hash [i]) == CPAGE_ADDR (page)) {
+ return 0;
+ } else if (!hash [i]) {
+ hash [i] = page;
+ return 1;
+ }
+ /* wrap around */
+ if (++i == hsize)
+ i = 0;
+ } while (i != start_pos);
+ /* should not happen */
+ printf ("failed code page store\n");
+ return 0;
+}
+
+static void
+add_code_pointer (uintptr_t ip)
+{
+ uintptr_t i;
+ if (num_code_pages * 2 >= size_code_pages) {
+ uintptr_t *n;
+ uintptr_t old_size = size_code_pages;
+ size_code_pages *= 2;
+ if (size_code_pages == 0)
+ size_code_pages = 16;
+ n = (uintptr_t *) g_calloc (sizeof (uintptr_t) * size_code_pages, 1);
+ for (i = 0; i < old_size; ++i) {
+ if (code_pages [i])
+ add_code_page (n, size_code_pages, code_pages [i]);
+ }
+ if (code_pages)
+ g_free (code_pages);
+ code_pages = n;
+ }
+ num_code_pages += add_code_page (code_pages, size_code_pages, ip & CPAGE_MASK);
+}
+
+/* ELF code crashes on some systems. */
+//#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
+#if 0
+static void
+dump_ubin (MonoProfiler *prof, const char *filename, uintptr_t load_addr, uint64_t offset, uintptr_t size)
+{
+ int len = strlen (filename) + 1;
+
+ ENTER_LOG (&sample_ubins_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* load address */ +
+ LEB128_SIZE /* offset */ +
+ LEB128_SIZE /* size */ +
+ nlen /* file name */
+ );
+
+ 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_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+#endif
+
+static void
+dump_usym (MonoProfiler *prof, const char *name, uintptr_t value, uintptr_t size)
+{
+ int len = strlen (name) + 1;
+
+ ENTER_LOG (&sample_usyms_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* value */ +
+ LEB128_SIZE /* size */ +
+ len /* name */
+ );
+
+ 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_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+
+/* ELF code crashes on some systems. */
+//#if defined(ELFMAG0)
+#if 0
+
+#if SIZEOF_VOID_P == 4
+#define ELF_WSIZE 32
+#else
+#define ELF_WSIZE 64
+#endif
+#ifndef ElfW
+#define ElfW(type) _ElfW (Elf, ELF_WSIZE, type)
+#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t)
+#define _ElfW_1(e,w,t) e##w##t
+#endif
+
+static void
+dump_elf_symbols (MonoProfiler *prof, ElfW(Sym) *symbols, int num_symbols, const char *strtab, void *load_addr)
+{
+ int i;
+ for (i = 0; i < num_symbols; ++i) {
+ const char* sym;
+ sym = strtab + symbols [i].st_name;
+ if (!symbols [i].st_name || !symbols [i].st_size || (symbols [i].st_info & 0xf) != STT_FUNC)
+ continue;
+ //printf ("symbol %s at %d\n", sym, symbols [i].st_value);
+ dump_usym (sym, (uintptr_t)load_addr + symbols [i].st_value, symbols [i].st_size);
+ }
+}
+
+static int
+read_elf_symbols (MonoProfiler *prof, const char *filename, void *load_addr)
+{
+ int fd, i;
+ void *data;
+ struct stat statb;
+ uint64_t file_size;
+ ElfW(Ehdr) *header;
+ ElfW(Shdr) *sheader;
+ ElfW(Shdr) *shstrtabh;
+ ElfW(Shdr) *symtabh = NULL;
+ ElfW(Shdr) *strtabh = NULL;
+ ElfW(Sym) *symbols = NULL;
+ const char *strtab;
+ int num_symbols;
+
+ fd = open (filename, O_RDONLY);
+ if (fd < 0)
+ return 0;
+ if (fstat (fd, &statb) != 0) {
+ close (fd);
+ return 0;
+ }
+ file_size = statb.st_size;
+ data = mmap (NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ close (fd);
+ if (data == MAP_FAILED)
+ return 0;
+ header = data;
+ if (header->e_ident [EI_MAG0] != ELFMAG0 ||
+ header->e_ident [EI_MAG1] != ELFMAG1 ||
+ header->e_ident [EI_MAG2] != ELFMAG2 ||
+ header->e_ident [EI_MAG3] != ELFMAG3 ) {
+ munmap (data, file_size);
+ return 0;
+ }
+ sheader = (void*)((char*)data + header->e_shoff);
+ shstrtabh = (void*)((char*)sheader + (header->e_shentsize * header->e_shstrndx));
+ strtab = (const char*)data + shstrtabh->sh_offset;
+ for (i = 0; i < header->e_shnum; ++i) {
+ //printf ("section header: %d\n", sheader->sh_type);
+ if (sheader->sh_type == SHT_SYMTAB) {
+ symtabh = sheader;
+ strtabh = (void*)((char*)data + header->e_shoff + sheader->sh_link * header->e_shentsize);
+ /*printf ("symtab section header: %d, .strstr: %d\n", i, sheader->sh_link);*/
+ break;
+ }
+ sheader = (void*)((char*)sheader + header->e_shentsize);
+ }
+ if (!symtabh || !strtabh) {
+ munmap (data, file_size);
+ return 0;
+ }
+ strtab = (const char*)data + strtabh->sh_offset;
+ num_symbols = symtabh->sh_size / symtabh->sh_entsize;
+ symbols = (void*)((char*)data + symtabh->sh_offset);
+ dump_elf_symbols (symbols, num_symbols, strtab, load_addr);
+ munmap (data, file_size);
+ return 1;
+}
+#endif
+
+/* ELF code crashes on some systems. */
+//#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
+#if 0
+static int
+elf_dl_callback (struct dl_phdr_info *info, size_t size, void *data)
+{
+ MonoProfiler *prof = data;
+ char buf [256];
+ const char *filename;
+ BinaryObject *obj;
+ char *a = (void*)info->dlpi_addr;
+ int i, num_sym;
+ ElfW(Dyn) *dyn = NULL;
+ ElfW(Sym) *symtab = NULL;
+ ElfW(Word) *hash_table = NULL;
+ ElfW(Ehdr) *header = NULL;
+ const char* strtab = NULL;
+ for (obj = prof->binary_objects; obj; obj = obj->next) {
+ if (obj->addr == a)
+ return 0;
+ }
+ filename = info->dlpi_name;
+ if (!filename)
+ return 0;
+ if (!info->dlpi_addr && !filename [0]) {
+ int l = readlink ("/proc/self/exe", buf, sizeof (buf) - 1);
+ if (l > 0) {
+ buf [l] = 0;
+ filename = buf;
+ }
+ }
+ obj = g_calloc (sizeof (BinaryObject), 1);
+ obj->addr = (void*)info->dlpi_addr;
+ obj->name = pstrdup (filename);
+ obj->next = prof->binary_objects;
+ prof->binary_objects = obj;
+ //printf ("loaded file: %s at %p, segments: %d\n", filename, (void*)info->dlpi_addr, info->dlpi_phnum);
+ a = NULL;
+ for (i = 0; i < info->dlpi_phnum; ++i) {
+ //printf ("segment type %d file offset: %d, size: %d\n", info->dlpi_phdr[i].p_type, info->dlpi_phdr[i].p_offset, info->dlpi_phdr[i].p_memsz);
+ if (info->dlpi_phdr[i].p_type == PT_LOAD && !header) {
+ header = (ElfW(Ehdr)*)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
+ if (header->e_ident [EI_MAG0] != ELFMAG0 ||
+ header->e_ident [EI_MAG1] != ELFMAG1 ||
+ header->e_ident [EI_MAG2] != ELFMAG2 ||
+ header->e_ident [EI_MAG3] != ELFMAG3 ) {
+ header = NULL;
+ }
+ dump_ubin (prof, filename, info->dlpi_addr + info->dlpi_phdr[i].p_vaddr, info->dlpi_phdr[i].p_offset, info->dlpi_phdr[i].p_memsz);
+ } else if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
+ dyn = (ElfW(Dyn) *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
+ }
+ }
+ if (read_elf_symbols (prof, filename, (void*)info->dlpi_addr))
+ return 0;
+ if (!info->dlpi_name || !info->dlpi_name[0])
+ return 0;
+ if (!dyn)
+ return 0;
+ for (i = 0; dyn [i].d_tag != DT_NULL; ++i) {
+ if (dyn [i].d_tag == DT_SYMTAB) {
+ if (symtab && do_debug)
+ printf ("multiple symtabs: %d\n", i);
+ symtab = (ElfW(Sym) *)(a + dyn [i].d_un.d_ptr);
+ } else if (dyn [i].d_tag == DT_HASH) {
+ hash_table = (ElfW(Word) *)(a + dyn [i].d_un.d_ptr);
+ } else if (dyn [i].d_tag == DT_STRTAB) {
+ strtab = (const char*)(a + dyn [i].d_un.d_ptr);
+ }
+ }
+ if (!hash_table)
+ return 0;
+ num_sym = hash_table [1];
+ dump_elf_symbols (prof, symtab, num_sym, strtab, (void*)info->dlpi_addr);
+ return 0;
+}
+
+static int
+load_binaries (MonoProfiler *prof)
+{
+ dl_iterate_phdr (elf_dl_callback, prof);
+ return 1;
+}
+#else
+static int
+load_binaries (MonoProfiler *prof)
+{
+ return 0;
+}
+#endif
+
+static const char*
+symbol_for (uintptr_t code)
+{
+#ifdef HAVE_DLADDR
+ void *ip = (void*)code;
+ Dl_info di;
+ if (dladdr (ip, &di)) {
+ if (di.dli_sname)
+ return di.dli_sname;
+ } else {
+ /* char **names;
+ names = backtrace_symbols (&ip, 1);
+ if (names) {
+ const char* p = names [0];
+ g_free (names);
+ return p;
+ }
+ */
+ }
+#endif
+ return NULL;
+}
+
+static void
+dump_unmanaged_coderefs (MonoProfiler *prof)
+{
+ int i;
+ const char* last_symbol;
+ uintptr_t addr, page_end;
+
+ if (load_binaries (prof))
+ return;
+ for (i = 0; i < size_code_pages; ++i) {
+ const char* sym;
+ if (!code_pages [i] || code_pages [i] & 1)
+ continue;
+ last_symbol = NULL;
+ addr = CPAGE_ADDR (code_pages [i]);
+ page_end = addr + CPAGE_SIZE;
+ code_pages [i] |= 1;
+ /* we dump the symbols for the whole page */
+ for (; addr < page_end; addr += 16) {
+ sym = symbol_for (addr);
+ if (sym && sym == last_symbol)
+ continue;
+ last_symbol = sym;
+ if (!sym)
+ continue;
+ dump_usym (prof, sym, addr, 0); /* let's not guess the size */
+ //printf ("found symbol at %p: %s\n", (void*)addr, sym);
+ }
+ }
+}
+
+static int
+mono_cpu_count (void)
+{
+#ifdef PLATFORM_ANDROID
+ /* Android tries really hard to save power by powering off CPUs on SMP phones which
+ * means the normal way to query cpu count returns a wrong value with userspace API.
+ * Instead we use /sys entries to query the actual hardware CPU count.
+ */
+ int count = 0;
+ char buffer[8] = {'\0'};
+ int present = open ("/sys/devices/system/cpu/present", O_RDONLY);
+ /* Format of the /sys entry is a cpulist of indexes which in the case
+ * of present is always of the form "0-(n-1)" when there is more than
+ * 1 core, n being the number of CPU cores in the system. Otherwise
+ * the value is simply 0
+ */
+ if (present != -1 && read (present, (char*)buffer, sizeof (buffer)) > 3)
+ count = strtol (((char*)buffer) + 2, NULL, 10);
+ if (present != -1)
+ close (present);
+ if (count > 0)
+ return count + 1;
+#endif
+
+#if defined(HOST_ARM) || defined (HOST_ARM64)
+
+ /* ARM platforms tries really hard to save power by powering off CPUs on SMP phones which
+ * means the normal way to query cpu count returns a wrong value with userspace API. */
+
+#ifdef _SC_NPROCESSORS_CONF
+ {
+ int count = sysconf (_SC_NPROCESSORS_CONF);
+ if (count > 0)
+ return count;
+ }
+#endif
+
+#else
+
+#ifdef HAVE_SCHED_GETAFFINITY
+ {
+ cpu_set_t set;
+ if (sched_getaffinity (getpid (), sizeof (set), &set) == 0)
+ return CPU_COUNT (&set);
+ }
+#endif
+#ifdef _SC_NPROCESSORS_ONLN
+ {
+ int count = sysconf (_SC_NPROCESSORS_ONLN);
+ if (count > 0)
+ return count;
+ }
+#endif
+
+#endif /* defined(HOST_ARM) || defined (HOST_ARM64) */
+
+#ifdef USE_SYSCTL
+ {
+ int count;
+ int mib [2];
+ size_t len = sizeof (int);
+ mib [0] = CTL_HW;
+ mib [1] = HW_NCPU;
+ if (sysctl (mib, 2, &count, &len, NULL, 0) == 0)
+ return count;
+ }
+#endif
+#ifdef HOST_WIN32
+ {
+ SYSTEM_INFO info;
+ GetSystemInfo (&info);
+ return info.dwNumberOfProcessors;
+ }
+#endif
+
+ static gboolean warned;
+
+ if (!warned) {
+ g_warning ("Don't know how to determine CPU count on this platform; assuming 1");
+ warned = TRUE;
+ }
+
+ return 1;
+}
+
+typedef struct MonoCounterAgent {
+ MonoCounter *counter;
+ // MonoCounterAgent specific data :
+ void *value;
+ size_t value_size;
+ short index;
+ short emitted;
+ struct MonoCounterAgent *next;
+} MonoCounterAgent;
+
+static MonoCounterAgent* counters;
+static int counters_index = 1;
+static mono_mutex_t counters_mutex;
+
+static void
+counters_add_agent (MonoCounter *counter)
+{
+ if (InterlockedRead (&in_shutdown))
+ return;
+
+ MonoCounterAgent *agent, *item;
+
+ mono_os_mutex_lock (&counters_mutex);
+
+ for (agent = counters; agent; agent = agent->next) {
+ if (agent->counter == counter) {
+ agent->value_size = 0;
+ if (agent->value) {
+ g_free (agent->value);
+ agent->value = NULL;
+ }
+ goto done;
+ }
+ }
+
+ agent = (MonoCounterAgent *) g_malloc (sizeof (MonoCounterAgent));
+ agent->counter = counter;
+ agent->value = NULL;
+ agent->value_size = 0;
+ agent->index = counters_index++;
+ agent->emitted = 0;
+ agent->next = NULL;
+
+ if (!counters) {
+ counters = agent;
+ } else {
+ item = counters;
+ while (item->next)
+ item = item->next;
+ item->next = agent;
+ }
+
+done:
+ mono_os_mutex_unlock (&counters_mutex);
+}
+
+static mono_bool
+counters_init_foreach_callback (MonoCounter *counter, gpointer data)
+{
+ counters_add_agent (counter);
+ return TRUE;
+}
+
+static void
+counters_init (MonoProfiler *profiler)
+{
+ mono_os_mutex_init (&counters_mutex);
+
+ mono_counters_on_register (&counters_add_agent);
+ mono_counters_foreach (counters_init_foreach_callback, NULL);
+}
+
+static void
+counters_emit (MonoProfiler *profiler)
+{
+ MonoCounterAgent *agent;
+ int len = 0;
+ int size =
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* len */
+ ;
+
+ mono_os_mutex_lock (&counters_mutex);
+
+ for (agent = counters; agent; agent = agent->next) {
+ if (agent->emitted)
+ continue;
+
+ size +=
+ LEB128_SIZE /* section */ +
+ strlen (mono_counter_get_name (agent->counter)) + 1 /* name */ +
+ BYTE_SIZE /* type */ +
+ BYTE_SIZE /* unit */ +
+ BYTE_SIZE /* variance */ +
+ LEB128_SIZE /* index */
+ ;
+
+ len++;
+ }
+
+ if (!len)
+ goto done;
+
+ ENTER_LOG (&counter_descriptors_ctr, logbuffer, size);
+
+ emit_event (logbuffer, TYPE_SAMPLE_COUNTERS_DESC | TYPE_SAMPLE);
+ emit_value (logbuffer, len);
+
+ for (agent = counters; agent; agent = agent->next) {
+ const char *name;
+
+ if (agent->emitted)
+ continue;
+
+ name = mono_counter_get_name (agent->counter);
+ emit_value (logbuffer, mono_counter_get_section (agent->counter));
+ emit_string (logbuffer, name, strlen (name) + 1);
+ 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_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+done:
+ mono_os_mutex_unlock (&counters_mutex);
+}
+
+static void
+counters_sample (MonoProfiler *profiler, uint64_t timestamp)
+{
+ MonoCounterAgent *agent;
+ MonoCounter *counter;
+ int type;
+ int buffer_size;
+ void *buffer;
+ int size;
+
+ counters_emit (profiler);
+
+ buffer_size = 8;
+ buffer = g_calloc (1, buffer_size);
+
+ mono_os_mutex_lock (&counters_mutex);
+
+ size =
+ EVENT_SIZE /* event */
+ ;
+
+ for (agent = counters; agent; agent = agent->next) {
+ size +=
+ LEB128_SIZE /* index */ +
+ BYTE_SIZE /* type */ +
+ mono_counter_get_size (agent->counter) /* value */
+ ;
+ }
+
+ size +=
+ LEB128_SIZE /* stop marker */
+ ;
+
+ ENTER_LOG (&counter_samples_ctr, logbuffer, size);
+
+ emit_event_time (logbuffer, TYPE_SAMPLE_COUNTERS | TYPE_SAMPLE, timestamp);
+
+ for (agent = counters; agent; agent = agent->next) {
+ size_t size;
+
+ counter = agent->counter;
+
+ size = mono_counter_get_size (counter);
+
+ if (size > buffer_size) {
+ buffer_size = size;
+ buffer = g_realloc (buffer, buffer_size);
+ }
+
+ memset (buffer, 0, buffer_size);
+
+ g_assert (mono_counters_sample (counter, buffer, size));
+
+ type = mono_counter_get_type (counter);
+
+ if (!agent->value) {
+ agent->value = g_calloc (1, size);
+ agent->value_size = size;
+ } else {
+ if (type == MONO_COUNTER_STRING) {
+ if (strcmp (agent->value, buffer) == 0)
+ continue;
+ } else {
+ if (agent->value_size == size && memcmp (agent->value, buffer, size) == 0)
+ continue;
+ }
+ }
+
+ emit_uvalue (logbuffer, agent->index);
+ emit_byte (logbuffer, type);
+ switch (type) {
+ case MONO_COUNTER_INT:
+#if SIZEOF_VOID_P == 4
+ case MONO_COUNTER_WORD:
+#endif
+ emit_svalue (logbuffer, *(int*)buffer - *(int*)agent->value);
+ break;
+ case MONO_COUNTER_UINT:
+ emit_uvalue (logbuffer, *(guint*)buffer - *(guint*)agent->value);
+ break;
+ case MONO_COUNTER_TIME_INTERVAL:
+ case MONO_COUNTER_LONG:
+#if SIZEOF_VOID_P == 8
+ case MONO_COUNTER_WORD:
+#endif
+ emit_svalue (logbuffer, *(gint64*)buffer - *(gint64*)agent->value);
+ break;
+ case MONO_COUNTER_ULONG:
+ emit_uvalue (logbuffer, *(guint64*)buffer - *(guint64*)agent->value);
+ break;
+ case MONO_COUNTER_DOUBLE:
+ emit_double (logbuffer, *(double*)buffer);
+ break;
+ case MONO_COUNTER_STRING:
+ if (size == 0) {
+ emit_byte (logbuffer, 0);
+ } else {
+ emit_byte (logbuffer, 1);
+ emit_string (logbuffer, (char*)buffer, size);
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (type == MONO_COUNTER_STRING && size > agent->value_size) {
+ agent->value = g_realloc (agent->value, size);
+ agent->value_size = size;
+ }
+
+ if (size > 0)
+ memcpy (agent->value, buffer, size);
+ }
+ g_free (buffer);
+
+ emit_value (logbuffer, 0);
+
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+ mono_os_mutex_unlock (&counters_mutex);
+}
+
+typedef struct _PerfCounterAgent PerfCounterAgent;
+struct _PerfCounterAgent {
+ PerfCounterAgent *next;
+ int index;
+ char *category_name;
+ char *name;
+ int type;
+ gint64 value;
+ guint8 emitted;
+ guint8 updated;
+ guint8 deleted;
+};
+
+static PerfCounterAgent *perfcounters = NULL;
+
+static void
+perfcounters_emit (MonoProfiler *profiler)
+{
+ PerfCounterAgent *pcagent;
+ int len = 0;
+ int size =
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* len */
+ ;
+
+ for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
+ if (pcagent->emitted)
+ continue;
+
+ size +=
+ LEB128_SIZE /* section */ +
+ strlen (pcagent->category_name) + 1 /* category name */ +
+ strlen (pcagent->name) + 1 /* name */ +
+ BYTE_SIZE /* type */ +
+ BYTE_SIZE /* unit */ +
+ BYTE_SIZE /* variance */ +
+ LEB128_SIZE /* index */
+ ;
+
+ len++;
+ }
+
+ if (!len)
+ return;
+
+ ENTER_LOG (&perfcounter_descriptors_ctr, logbuffer, 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_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_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+
+static gboolean
+perfcounters_foreach (char *category_name, char *name, unsigned char type, gint64 value, gpointer user_data)
+{
+ PerfCounterAgent *pcagent;
+
+ for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
+ if (strcmp (pcagent->category_name, category_name) != 0 || strcmp (pcagent->name, name) != 0)
+ continue;
+ if (pcagent->value == value)
+ return TRUE;
+
+ pcagent->value = value;
+ pcagent->updated = 1;
+ pcagent->deleted = 0;
+ return TRUE;
+ }
+
+ pcagent = g_new0 (PerfCounterAgent, 1);
+ pcagent->next = perfcounters;
+ pcagent->index = counters_index++;
+ pcagent->category_name = g_strdup (category_name);
+ pcagent->name = g_strdup (name);
+ pcagent->type = (int) type;
+ pcagent->value = value;
+ pcagent->emitted = 0;
+ pcagent->updated = 1;
+ pcagent->deleted = 0;
+
+ perfcounters = pcagent;
+
+ return TRUE;
+}
+
+static void
+perfcounters_sample (MonoProfiler *profiler, uint64_t timestamp)
+{
+ PerfCounterAgent *pcagent;
+ int len = 0;
+ int size;
+
+ mono_os_mutex_lock (&counters_mutex);
+
+ /* mark all perfcounters as deleted, foreach will unmark them as necessary */
+ for (pcagent = perfcounters; pcagent; pcagent = pcagent->next)
+ pcagent->deleted = 1;
+
+ mono_perfcounter_foreach (perfcounters_foreach, perfcounters);
+
+ perfcounters_emit (profiler);
+
+ size =
+ EVENT_SIZE /* event */
+ ;
+
+ for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
+ if (pcagent->deleted || !pcagent->updated)
+ continue;
+
+ size +=
+ LEB128_SIZE /* index */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* value */
+ ;
+
+ len++;
+ }
+
+ if (!len)
+ goto done;
+
+ size +=
+ LEB128_SIZE /* stop marker */
+ ;
+
+ ENTER_LOG (&perfcounter_samples_ctr, logbuffer, size);
+
+ emit_event_time (logbuffer, TYPE_SAMPLE_COUNTERS | TYPE_SAMPLE, timestamp);
+
+ for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
+ if (pcagent->deleted || !pcagent->updated)
+ continue;
+ emit_uvalue (logbuffer, pcagent->index);
+ emit_byte (logbuffer, MONO_COUNTER_LONG);
+ emit_svalue (logbuffer, pcagent->value);
+
+ pcagent->updated = 0;
+ }
+
+ emit_value (logbuffer, 0);
+
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+done:
+ mono_os_mutex_unlock (&counters_mutex);
+}
+
+static void
+counters_and_perfcounters_sample (MonoProfiler *prof)
+{
+ uint64_t now = current_time ();
+
+ counters_sample (prof, now);
+ perfcounters_sample (prof, now);
+}
+
+#define COVERAGE_DEBUG(x) if (debug_coverage) {x}
+static mono_mutex_t coverage_mutex;
+static MonoConcurrentHashTable *coverage_methods = NULL;
+static MonoConcurrentHashTable *coverage_assemblies = NULL;
+static MonoConcurrentHashTable *coverage_classes = NULL;
+
+static MonoConcurrentHashTable *filtered_classes = NULL;
+static MonoConcurrentHashTable *entered_methods = NULL;
+static MonoConcurrentHashTable *image_to_methods = NULL;
+static MonoConcurrentHashTable *suppressed_assemblies = NULL;
+static gboolean coverage_initialized = FALSE;
+
+static GPtrArray *coverage_data = NULL;
+static int previous_offset = 0;
+
+typedef struct {
+ MonoLockFreeQueueNode node;
+ MonoMethod *method;
+} MethodNode;
+
+typedef struct {
+ int offset;
+ int counter;
+ char *filename;
+ int line;
+ int column;
+} CoverageEntry;
+
+static void
+free_coverage_entry (gpointer data, gpointer userdata)
+{
+ CoverageEntry *entry = (CoverageEntry *)data;
+ g_free (entry->filename);
+ g_free (entry);
+}
+
+static void
+obtain_coverage_for_method (MonoProfiler *prof, const MonoProfileCoverageEntry *entry)
+{
+ int offset = entry->iloffset - previous_offset;
+ CoverageEntry *e = g_new (CoverageEntry, 1);
+
+ previous_offset = entry->iloffset;
+
+ e->offset = offset;
+ e->counter = entry->counter;
+ e->filename = g_strdup(entry->filename ? entry->filename : "");
+ e->line = entry->line;
+ e->column = entry->col;
+
+ g_ptr_array_add (coverage_data, e);
+}
+
+static char *
+parse_generic_type_names(char *name)
+{
+ char *new_name, *ret;
+ int within_generic_declaration = 0, generic_members = 1;
+
+ if (name == NULL || *name == '\0')
+ return g_strdup ("");
+
+ if (!(ret = new_name = (char *) g_calloc (strlen (name) * 4 + 1, sizeof (char))))
+ return NULL;
+
+ do {
+ switch (*name) {
+ case '<':
+ within_generic_declaration = 1;
+ break;
+
+ case '>':
+ within_generic_declaration = 0;
+
+ if (*(name - 1) != '<') {
+ *new_name++ = '`';
+ *new_name++ = '0' + generic_members;
+ } else {
+ memcpy (new_name, "<>", 8);
+ new_name += 8;
+ }
+
+ generic_members = 0;
+ break;
+
+ case ',':
+ generic_members++;
+ break;
+
+ default:
+ if (!within_generic_declaration)
+ *new_name++ = *name;
+
+ break;
+ }
+ } while (*name++);
+
+ return ret;
+}
+
+static int method_id;
+static void
+build_method_buffer (gpointer key, gpointer value, gpointer userdata)
+{
+ MonoMethod *method = (MonoMethod *)value;
+ MonoProfiler *prof = (MonoProfiler *)userdata;
+ MonoClass *klass;
+ MonoImage *image;
+ char *class_name;
+ const char *image_name, *method_name, *sig, *first_filename;
+ guint i;
+
+ previous_offset = 0;
+ coverage_data = g_ptr_array_new ();
+
+ mono_profiler_coverage_get (prof, method, obtain_coverage_for_method);
+
+ klass = mono_method_get_class (method);
+ image = mono_class_get_image (klass);
+ image_name = mono_image_get_name (image);
+
+ sig = mono_signature_get_desc (mono_method_signature (method), TRUE);
+ class_name = parse_generic_type_names (mono_type_get_name (mono_class_get_type (klass)));
+ method_name = mono_method_get_name (method);
+
+ if (coverage_data->len != 0) {
+ CoverageEntry *entry = (CoverageEntry *)coverage_data->pdata[0];
+ first_filename = entry->filename ? entry->filename : "";
+ } else
+ first_filename = "";
+
+ image_name = image_name ? image_name : "";
+ sig = sig ? sig : "";
+ method_name = method_name ? method_name : "";
+
+ ENTER_LOG (&coverage_methods_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ strlen (image_name) + 1 /* image name */ +
+ strlen (class_name) + 1 /* class name */ +
+ strlen (method_name) + 1 /* method name */ +
+ strlen (sig) + 1 /* signature */ +
+ strlen (first_filename) + 1 /* first file name */ +
+ LEB128_SIZE /* token */ +
+ LEB128_SIZE /* method id */ +
+ LEB128_SIZE /* entries */
+ );
+
+ 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_string (logbuffer, sig, strlen (sig) + 1);
+ emit_string (logbuffer, first_filename, strlen (first_filename) + 1);
+
+ emit_uvalue (logbuffer, mono_method_get_token (method));
+ emit_uvalue (logbuffer, method_id);
+ emit_value (logbuffer, coverage_data->len);
+
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+ for (i = 0; i < coverage_data->len; i++) {
+ CoverageEntry *entry = (CoverageEntry *)coverage_data->pdata[i];
+
+ ENTER_LOG (&coverage_statements_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* method id */ +
+ LEB128_SIZE /* offset */ +
+ LEB128_SIZE /* counter */ +
+ LEB128_SIZE /* line */ +
+ LEB128_SIZE /* column */
+ );
+
+ 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_EXPLICIT (DO_SEND, NO_REQUESTS);
+ }
+
+ method_id++;
+
+ g_free (class_name);
+
+ g_ptr_array_foreach (coverage_data, free_coverage_entry, NULL);
+ g_ptr_array_free (coverage_data, TRUE);
+ coverage_data = NULL;
+}
+
+/* This empties the queue */
+static guint
+count_queue (MonoLockFreeQueue *queue)
+{
+ MonoLockFreeQueueNode *node;
+ guint count = 0;
+
+ while ((node = mono_lock_free_queue_dequeue (queue))) {
+ count++;
+ mono_thread_hazardous_try_free (node, g_free);
+ }
+
+ return count;
+}
+
+static void
+build_class_buffer (gpointer key, gpointer value, gpointer userdata)
+{
+ MonoClass *klass = (MonoClass *)key;
+ MonoLockFreeQueue *class_methods = (MonoLockFreeQueue *)value;
+ MonoImage *image;
+ char *class_name;
+ const char *assembly_name;
+ int number_of_methods, partially_covered;
+ guint fully_covered;
+
+ image = mono_class_get_image (klass);
+ assembly_name = mono_image_get_name (image);
+ class_name = mono_type_get_name (mono_class_get_type (klass));
+
+ assembly_name = assembly_name ? assembly_name : "";
+ number_of_methods = mono_class_num_methods (klass);
+ fully_covered = count_queue (class_methods);
+ /* We don't handle partial covered yet */
+ partially_covered = 0;
+
+ ENTER_LOG (&coverage_classes_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ strlen (assembly_name) + 1 /* assembly name */ +
+ strlen (class_name) + 1 /* class name */ +
+ LEB128_SIZE /* no. methods */ +
+ LEB128_SIZE /* fully covered */ +
+ LEB128_SIZE /* partially covered */
+ );
+
+ 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_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+ g_free (class_name);
+}
+
+static void
+get_coverage_for_image (MonoImage *image, int *number_of_methods, guint *fully_covered, int *partially_covered)
+{
+ MonoLockFreeQueue *image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (image_to_methods, image);
+
+ *number_of_methods = mono_image_get_table_rows (image, MONO_TABLE_METHOD);
+ if (image_methods)
+ *fully_covered = count_queue (image_methods);
+ else
+ *fully_covered = 0;
+
+ // FIXME: We don't handle partially covered yet.
+ *partially_covered = 0;
+}
+
+static void
+build_assembly_buffer (gpointer key, gpointer value, gpointer userdata)
+{
+ MonoAssembly *assembly = (MonoAssembly *)value;
+ MonoImage *image = mono_assembly_get_image (assembly);
+ const char *name, *guid, *filename;
+ int number_of_methods = 0, partially_covered = 0;
+ guint fully_covered = 0;
+
+ name = mono_image_get_name (image);
+ guid = mono_image_get_guid (image);
+ filename = mono_image_get_filename (image);
+
+ name = name ? name : "";
+ guid = guid ? guid : "";
+ filename = filename ? filename : "";
+
+ get_coverage_for_image (image, &number_of_methods, &fully_covered, &partially_covered);
+
+ ENTER_LOG (&coverage_assemblies_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ strlen (name) + 1 /* name */ +
+ strlen (guid) + 1 /* guid */ +
+ strlen (filename) + 1 /* file name */ +
+ LEB128_SIZE /* no. methods */ +
+ LEB128_SIZE /* fully covered */ +
+ LEB128_SIZE /* partially covered */
+ );
+
+ 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_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+
+static void
+dump_coverage (MonoProfiler *prof)
+{
+ if (!coverage_initialized)
+ return;
+
+ COVERAGE_DEBUG(fprintf (stderr, "Coverage: Started dump\n");)
+ method_id = 0;
+
+ mono_os_mutex_lock (&coverage_mutex);
+ mono_conc_hashtable_foreach (coverage_assemblies, build_assembly_buffer, NULL);
+ mono_conc_hashtable_foreach (coverage_classes, build_class_buffer, NULL);
+ mono_conc_hashtable_foreach (coverage_methods, build_method_buffer, prof);
+ mono_os_mutex_unlock (&coverage_mutex);
+
+ COVERAGE_DEBUG(fprintf (stderr, "Coverage: Finished dump\n");)
+}
+
+static void
+process_method_enter_coverage (MonoProfiler *prof, MonoMethod *method)
+{
+ MonoClass *klass;
+ MonoImage *image;
+
+ if (!coverage_initialized)
+ return;
+
+ klass = mono_method_get_class (method);
+ image = mono_class_get_image (klass);
+
+ if (mono_conc_hashtable_lookup (suppressed_assemblies, (gpointer) mono_image_get_name (image)))
+ return;
+
+ mono_os_mutex_lock (&coverage_mutex);
+ mono_conc_hashtable_insert (entered_methods, method, method);
+ mono_os_mutex_unlock (&coverage_mutex);
+}
+
+static MonoLockFreeQueueNode *
+create_method_node (MonoMethod *method)
+{
+ MethodNode *node = (MethodNode *) g_malloc (sizeof (MethodNode));
+ mono_lock_free_queue_node_init ((MonoLockFreeQueueNode *) node, FALSE);
+ node->method = method;
+
+ return (MonoLockFreeQueueNode *) node;
+}
+
+static gboolean
+coverage_filter (MonoProfiler *prof, MonoMethod *method)
+{
+ MonoError error;
+ MonoClass *klass;
+ MonoImage *image;
+ MonoAssembly *assembly;
+ MonoMethodHeader *header;
+ guint32 iflags, flags, code_size;
+ char *fqn, *classname;
+ gboolean has_positive, found;
+ MonoLockFreeQueue *image_methods, *class_methods;
+ MonoLockFreeQueueNode *node;
+
+ g_assert (coverage_initialized && "Why are we being asked for coverage filter info when we're not doing coverage?");
+
+ COVERAGE_DEBUG(fprintf (stderr, "Coverage filter for %s\n", mono_method_get_name (method));)
+
+ flags = mono_method_get_flags (method, &iflags);
+ if ((iflags & 0x1000 /*METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL*/) ||
+ (flags & 0x2000 /*METHOD_ATTRIBUTE_PINVOKE_IMPL*/)) {
+ COVERAGE_DEBUG(fprintf (stderr, " Internal call or pinvoke - ignoring\n");)
+ return FALSE;
+ }
+
+ // Don't need to do anything else if we're already tracking this method
+ if (mono_conc_hashtable_lookup (coverage_methods, method)) {
+ COVERAGE_DEBUG(fprintf (stderr, " Already tracking\n");)
+ return TRUE;
+ }
+
+ klass = mono_method_get_class (method);
+ image = mono_class_get_image (klass);
+
+ // Don't handle coverage for the core assemblies
+ if (mono_conc_hashtable_lookup (suppressed_assemblies, (gpointer) mono_image_get_name (image)) != NULL)
+ return FALSE;
+
+ if (prof->coverage_filters) {
+ /* Check already filtered classes first */
+ if (mono_conc_hashtable_lookup (filtered_classes, klass)) {
+ COVERAGE_DEBUG(fprintf (stderr, " Already filtered\n");)
+ return FALSE;
+ }
+
+ classname = mono_type_get_name (mono_class_get_type (klass));
+
+ fqn = g_strdup_printf ("[%s]%s", mono_image_get_name (image), classname);
+
+ COVERAGE_DEBUG(fprintf (stderr, " Looking for %s in filter\n", fqn);)
+ // Check positive filters first
+ has_positive = FALSE;
+ found = FALSE;
+ for (guint i = 0; i < prof->coverage_filters->len; ++i) {
+ char *filter = (char *)g_ptr_array_index (prof->coverage_filters, i);
+
+ if (filter [0] == '+') {
+ filter = &filter [1];
+
+ COVERAGE_DEBUG(fprintf (stderr, " Checking against +%s ...", filter);)
+
+ if (strstr (fqn, filter) != NULL) {
+ COVERAGE_DEBUG(fprintf (stderr, "matched\n");)
+ found = TRUE;
+ } else
+ COVERAGE_DEBUG(fprintf (stderr, "no match\n");)
+
+ has_positive = TRUE;
+ }
+ }
+
+ if (has_positive && !found) {
+ COVERAGE_DEBUG(fprintf (stderr, " Positive match was not found\n");)
+
+ mono_os_mutex_lock (&coverage_mutex);
+ mono_conc_hashtable_insert (filtered_classes, klass, klass);
+ mono_os_mutex_unlock (&coverage_mutex);
+ g_free (fqn);
+ g_free (classname);
+
+ return FALSE;
+ }
+
+ for (guint i = 0; i < prof->coverage_filters->len; ++i) {
+ // FIXME: Is substring search sufficient?
+ char *filter = (char *)g_ptr_array_index (prof->coverage_filters, i);
+ if (filter [0] == '+')
+ continue;
+
+ // Skip '-'
+ filter = &filter [1];
+ COVERAGE_DEBUG(fprintf (stderr, " Checking against -%s ...", filter);)
+
+ if (strstr (fqn, filter) != NULL) {
+ COVERAGE_DEBUG(fprintf (stderr, "matched\n");)
+
+ mono_os_mutex_lock (&coverage_mutex);
+ mono_conc_hashtable_insert (filtered_classes, klass, klass);
+ mono_os_mutex_unlock (&coverage_mutex);
+ g_free (fqn);
+ g_free (classname);
+
+ return FALSE;
+ } else
+ COVERAGE_DEBUG(fprintf (stderr, "no match\n");)
+
+ }
+
+ g_free (fqn);
+ g_free (classname);
+ }
+
+ COVERAGE_DEBUG(fprintf (stderr, " Handling coverage for %s\n", mono_method_get_name (method));)
+ header = mono_method_get_header_checked (method, &error);
+ mono_error_cleanup (&error);
+
+ mono_method_header_get_code (header, &code_size, NULL);
+
+ assembly = mono_image_get_assembly (image);
+
+ // Need to keep the assemblies around for as long as they are kept in the hashtable
+ // Nunit, for example, has a habit of unloading them before the coverage statistics are
+ // generated causing a crash. See https://bugzilla.xamarin.com/show_bug.cgi?id=39325
+ mono_assembly_addref (assembly);
+
+ mono_os_mutex_lock (&coverage_mutex);
+ mono_conc_hashtable_insert (coverage_methods, method, method);
+ mono_conc_hashtable_insert (coverage_assemblies, assembly, assembly);
+ mono_os_mutex_unlock (&coverage_mutex);
+
+ image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (image_to_methods, image);
+
+ if (image_methods == NULL) {
+ image_methods = (MonoLockFreeQueue *) g_malloc (sizeof (MonoLockFreeQueue));
+ mono_lock_free_queue_init (image_methods);
+ mono_os_mutex_lock (&coverage_mutex);
+ mono_conc_hashtable_insert (image_to_methods, image, image_methods);
+ mono_os_mutex_unlock (&coverage_mutex);
+ }
+
+ node = create_method_node (method);
+ mono_lock_free_queue_enqueue (image_methods, node);
+
+ class_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (coverage_classes, klass);
+
+ if (class_methods == NULL) {
+ class_methods = (MonoLockFreeQueue *) g_malloc (sizeof (MonoLockFreeQueue));
+ mono_lock_free_queue_init (class_methods);
+ mono_os_mutex_lock (&coverage_mutex);
+ mono_conc_hashtable_insert (coverage_classes, klass, class_methods);
+ mono_os_mutex_unlock (&coverage_mutex);
+ }
+
+ node = create_method_node (method);
+ mono_lock_free_queue_enqueue (class_methods, node);
+
+ return TRUE;
+}
+
+#define LINE_BUFFER_SIZE 4096
+/* Max file limit of 128KB */
+#define MAX_FILE_SIZE 128 * 1024
+static char *
+get_file_content (FILE *stream)
+{
+ char *buffer;
+ ssize_t bytes_read;
+ long filesize;
+ int res, offset = 0;
+
+ res = fseek (stream, 0, SEEK_END);
+ if (res < 0)
+ return NULL;
+
+ filesize = ftell (stream);
+ if (filesize < 0)
+ return NULL;
+
+ res = fseek (stream, 0, SEEK_SET);
+ if (res < 0)
+ return NULL;
+
+ if (filesize > MAX_FILE_SIZE)
+ return NULL;
+
+ buffer = (char *) g_malloc ((filesize + 1) * sizeof (char));
+ while ((bytes_read = fread (buffer + offset, 1, LINE_BUFFER_SIZE, stream)) > 0)
+ offset += bytes_read;
+
+ /* NULL terminate our buffer */
+ buffer[filesize] = '\0';
+ return buffer;
+}
+
+static char *
+get_next_line (char *contents, char **next_start)
+{
+ char *p = contents;
+
+ if (p == NULL || *p == '\0') {
+ *next_start = NULL;
+ return NULL;
+ }
+
+ while (*p != '\n' && *p != '\0')
+ p++;
+
+ if (*p == '\n') {
+ *p = '\0';
+ *next_start = p + 1;
+ } else
+ *next_start = NULL;
+
+ return contents;
+}
+
+static void
+init_suppressed_assemblies (void)
+{
+ char *content;
+ char *line;
+ FILE *sa_file;
+
+ suppressed_assemblies = mono_conc_hashtable_new (g_str_hash, g_str_equal);
+ sa_file = fopen (SUPPRESSION_DIR "/mono-profiler-log.suppression", "r");
+ if (sa_file == NULL)
+ return;
+
+ /* Don't need to free @content as it is referred to by the lines stored in @suppressed_assemblies */
+ content = get_file_content (sa_file);
+ if (content == NULL) {
+ g_error ("mono-profiler-log.suppression is greater than 128kb - aborting\n");
+ }
+
+ while ((line = get_next_line (content, &content))) {
+ line = g_strchomp (g_strchug (line));
+ /* No locking needed as we're doing initialization */
+ mono_conc_hashtable_insert (suppressed_assemblies, line, line);
+ }
+
+ fclose (sa_file);
+}
+
+static void
+coverage_init (MonoProfiler *prof)
+{
+ g_assert (!coverage_initialized && "Why are we initializing coverage twice?");
+
+ COVERAGE_DEBUG(fprintf (stderr, "Coverage initialized\n");)
+
+ mono_os_mutex_init (&coverage_mutex);
+ coverage_methods = mono_conc_hashtable_new (NULL, NULL);
+ coverage_assemblies = mono_conc_hashtable_new (NULL, NULL);
+ coverage_classes = mono_conc_hashtable_new (NULL, NULL);
+ filtered_classes = mono_conc_hashtable_new (NULL, NULL);
+ entered_methods = mono_conc_hashtable_new (NULL, NULL);
+ image_to_methods = mono_conc_hashtable_new (NULL, NULL);
+ init_suppressed_assemblies ();
+
+ coverage_initialized = TRUE;
+}
+
+static void
+unref_coverage_assemblies (gpointer key, gpointer value, gpointer userdata)
+{
+ MonoAssembly *assembly = (MonoAssembly *)value;
+ mono_assembly_close (assembly);
+}
+
+static void
+free_sample_hit (gpointer p)
+{
+ mono_lock_free_free (p, SAMPLE_BLOCK_SIZE);
+}
+
+static void
+cleanup_reusable_samples (MonoProfiler *prof)
+{
+ SampleHit *sample;
+
+ while ((sample = (SampleHit *) mono_lock_free_queue_dequeue (&prof->sample_reuse_queue)))
+ mono_thread_hazardous_try_free (sample, free_sample_hit);
+}
+
+static void
+log_shutdown (MonoProfiler *prof)
+{
+ InterlockedWrite (&in_shutdown, 1);
+
+ if (!no_counters)
+ counters_and_perfcounters_sample (prof);
+
+ dump_coverage (prof);
+
+ char c = 1;
+
+ if (write (prof->pipes [1], &c, 1) != 1) {
+ fprintf (stderr, "Could not write to pipe: %s\n", strerror (errno));
+ exit (1);
+ }
+
+ mono_native_thread_join (prof->helper_thread);
+
+ mono_os_mutex_destroy (&counters_mutex);
+
+ MonoCounterAgent *mc_next;
+
+ for (MonoCounterAgent *cur = counters; cur; cur = mc_next) {
+ mc_next = cur->next;
+ g_free (cur);
+ }
+
+ PerfCounterAgent *pc_next;
+
+ for (PerfCounterAgent *cur = perfcounters; cur; cur = pc_next) {
+ pc_next = cur->next;
+ g_free (cur);
+ }
+
+ /*
+ * 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) {
+ g_assert (thread->attached && "Why is a thread in the LLS not attached?");
+
+ remove_thread (thread);
+ } MONO_LLS_FOREACH_SAFE_END
+ }
+
+ /*
+ * Ensure that all threads have been freed, so that we don't miss any
+ * buffers when we shut down the writer thread below.
+ */
+ mono_thread_hazardous_try_free_all ();
+
+ InterlockedWrite (&prof->run_dumper_thread, 0);
+ mono_os_sem_post (&prof->dumper_queue_sem);
+ mono_native_thread_join (prof->dumper_thread);
+ mono_os_sem_destroy (&prof->dumper_queue_sem);
+
+ InterlockedWrite (&prof->run_writer_thread, 0);
+ mono_os_sem_post (&prof->writer_queue_sem);
+ mono_native_thread_join (prof->writer_thread);
+ mono_os_sem_destroy (&prof->writer_queue_sem);
+
+ /*
+ * Free all writer queue entries, and ensure that all sample hits will be
+ * added to the sample reuse queue.
+ */
+ mono_thread_hazardous_try_free_all ();
+
+ cleanup_reusable_samples (prof);
+
+ /*
+ * Finally, make sure that all sample hits are freed. This should cover all
+ * hazardous data from the profiler. We can now be sure that the runtime
+ * won't later invoke free functions in the profiler library after it has
+ * been unloaded.
+ */
+ mono_thread_hazardous_try_free_all ();
+
+ 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);
+#endif
+ if (prof->pipe_output)
+ pclose (prof->file);
+ else
+ fclose (prof->file);
+
+ mono_conc_hashtable_destroy (prof->method_table);
+ mono_os_mutex_destroy (&prof->method_table_mutex);
+
+ if (coverage_initialized) {
+ mono_os_mutex_lock (&coverage_mutex);
+ mono_conc_hashtable_foreach (coverage_assemblies, unref_coverage_assemblies, prof);
+ mono_os_mutex_unlock (&coverage_mutex);
+
+ mono_conc_hashtable_destroy (coverage_methods);
+ mono_conc_hashtable_destroy (coverage_assemblies);
+ mono_conc_hashtable_destroy (coverage_classes);
+ mono_conc_hashtable_destroy (filtered_classes);
+
+ mono_conc_hashtable_destroy (entered_methods);
+ mono_conc_hashtable_destroy (image_to_methods);
+ mono_conc_hashtable_destroy (suppressed_assemblies);
+ mono_os_mutex_destroy (&coverage_mutex);
+ }
+
+ PROF_TLS_FREE ();
+
+ g_free (prof->args);
+ g_free (prof);
+}
+
+static char*
+new_filename (const char* filename)
+{
+ time_t t = time (NULL);
+ int pid = process_id ();
+ char pid_buf [16];
+ char time_buf [16];
+ char *res, *d;
+ const char *p;
+ int count_dates = 0;
+ int count_pids = 0;
+ int s_date, s_pid;
+ struct tm *ts;
+ for (p = filename; *p; p++) {
+ if (*p != '%')
+ continue;
+ p++;
+ if (*p == 't')
+ count_dates++;
+ else if (*p == 'p')
+ count_pids++;
+ else if (*p == 0)
+ break;
+ }
+ if (!count_dates && !count_pids)
+ return pstrdup (filename);
+ snprintf (pid_buf, sizeof (pid_buf), "%d", pid);
+ ts = gmtime (&t);
+ snprintf (time_buf, sizeof (time_buf), "%d%02d%02d%02d%02d%02d",
+ 1900 + ts->tm_year, 1 + ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec);
+ s_date = strlen (time_buf);
+ s_pid = strlen (pid_buf);
+ d = res = (char *) g_malloc (strlen (filename) + s_date * count_dates + s_pid * count_pids);
+ for (p = filename; *p; p++) {
+ if (*p != '%') {
+ *d++ = *p;
+ continue;
+ }
+ p++;
+ if (*p == 't') {
+ strcpy (d, time_buf);
+ d += s_date;
+ continue;
+ } else if (*p == 'p') {
+ strcpy (d, pid_buf);
+ d += s_pid;
+ continue;
+ } else if (*p == '%') {
+ *d++ = '%';
+ continue;
+ } else if (*p == 0)
+ break;
+ *d++ = '%';
+ *d++ = *p;
+ }
+ *d = 0;
+ return res;
+}
+
+static void
+add_to_fd_set (fd_set *set, int fd, int *max_fd)
+{
+ /*
+ * This should only trigger for the basic FDs (server socket, pipes) at
+ * startup if for some mysterious reason they're too large. In this case,
+ * the profiler really can't function, and we're better off printing an
+ * error and exiting.
+ */
+ if (fd >= FD_SETSIZE) {
+ fprintf (stderr, "File descriptor is out of bounds for fd_set: %d\n", fd);
+ exit (1);
+ }
+
+ FD_SET (fd, set);
+
+ if (*max_fd < fd)
+ *max_fd = fd;
+}
+
+static void *
+helper_thread (void *arg)
+{
+ MonoProfiler *prof = (MonoProfiler *) arg;
+
+ mono_threads_attach_tools_thread ();
+ mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler helper");
+
+ MonoProfilerThread *thread = init_thread (prof, FALSE);
+
+ GArray *command_sockets = g_array_new (FALSE, FALSE, sizeof (int));
+
+ while (1) {
+ fd_set rfds;
+ int max_fd = -1;
+
+ FD_ZERO (&rfds);
+
+ add_to_fd_set (&rfds, prof->server_socket, &max_fd);
+ add_to_fd_set (&rfds, prof->pipes [0], &max_fd);
+
+ for (gint i = 0; i < command_sockets->len; i++)
+ add_to_fd_set (&rfds, g_array_index (command_sockets, int, i), &max_fd);
+
+ struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
+
+ // Sleep for 1sec or until a file descriptor has data.
+ if (select (max_fd + 1, &rfds, NULL, NULL, &tv) == -1) {
+ if (errno == EINTR)
+ continue;
+
+ fprintf (stderr, "Error in mono-profiler-log server: %s", strerror (errno));
+ exit (1);
+ }
+
+ if (!no_counters)
+ counters_and_perfcounters_sample (prof);
+
+ buffer_lock_excl ();
+
+ sync_point (SYNC_POINT_PERIODIC);
+
+ buffer_unlock_excl ();
+
+ // Are we shutting down?
+ if (FD_ISSET (prof->pipes [0], &rfds)) {
+ char c;
+ read (prof->pipes [0], &c, 1);
+ break;
+ }
+
+ for (gint i = 0; i < command_sockets->len; i++) {
+ int fd = g_array_index (command_sockets, int, i);
+
+ if (!FD_ISSET (fd, &rfds))
+ continue;
+
+ char buf [64];
+ int len = read (fd, buf, sizeof (buf) - 1);
+
+ if (len == -1)
+ continue;
+
+ if (!len) {
+ // The other end disconnected.
+ g_array_remove_index (command_sockets, i);
+ close (fd);
+
+ continue;
+ }
+
+ buf [len] = 0;
+
+ if (!strcmp (buf, "heapshot\n") && hs_mode_ondemand) {
+ // Rely on the finalization callbacks invoking process_requests ().
+ heapshot_requested = 1;
+ mono_gc_finalize_notify ();
+ }
+ }
+
+ if (FD_ISSET (prof->server_socket, &rfds)) {
+ int fd = accept (prof->server_socket, NULL, NULL);
+
+ if (fd != -1) {
+ if (fd >= FD_SETSIZE)
+ close (fd);
+ else
+ g_array_append_val (command_sockets, fd);
+ }
+ }
+ }
+
+ for (gint i = 0; i < command_sockets->len; i++)
+ close (g_array_index (command_sockets, int, i));
+
+ g_array_free (command_sockets, TRUE);
+
+ send_log_unsafe (FALSE);
+ deinit_thread (thread);
+
+ mono_thread_info_detach ();
+
+ return NULL;
+}
+
+static void
+start_helper_thread (MonoProfiler* prof)
+{
+ if (pipe (prof->pipes) == -1) {
+ fprintf (stderr, "Cannot create pipe: %s\n", strerror (errno));
+ exit (1);
+ }
+
+ prof->server_socket = socket (PF_INET, SOCK_STREAM, 0);
+
+ if (prof->server_socket == -1) {
+ fprintf (stderr, "Cannot create server socket: %s\n", strerror (errno));
+ exit (1);
+ }
+
+ struct sockaddr_in server_address;
+
+ memset (&server_address, 0, sizeof (server_address));
+ server_address.sin_family = AF_INET;
+ server_address.sin_addr.s_addr = INADDR_ANY;
+ server_address.sin_port = htons (prof->command_port);
+
+ if (bind (prof->server_socket, (struct sockaddr *) &server_address, sizeof (server_address)) == -1) {
+ fprintf (stderr, "Cannot bind server socket on port %d: %s\n", prof->command_port, strerror (errno));
+ close (prof->server_socket);
+ exit (1);
+ }
+
+ if (listen (prof->server_socket, 1) == -1) {
+ fprintf (stderr, "Cannot listen on server socket: %s\n", strerror (errno));
+ close (prof->server_socket);
+ exit (1);
+ }
+
+ socklen_t slen = sizeof (server_address);
+
+ if (getsockname (prof->server_socket, (struct sockaddr *) &server_address, &slen)) {
+ fprintf (stderr, "Could not get assigned port: %s\n", strerror (errno));
+ close (prof->server_socket);
+ exit (1);
+ }
+
+ prof->command_port = ntohs (server_address.sin_port);
+
+ if (!mono_native_thread_create (&prof->helper_thread, helper_thread, prof)) {
+ fprintf (stderr, "Could not start helper thread\n");
+ close (prof->server_socket);
+ exit (1);
+ }
+}
+
+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))) {
+ if (!entry->methods)
+ goto no_methods;
+
+ gboolean wrote_methods = FALSE;
+
+ /*
+ * 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);
+
+ if (mono_conc_hashtable_lookup (prof->method_table, info->method))
+ goto free_info; // This method already has metadata emitted.
+
+ /*
+ * Other threads use this hash table to get a general
+ * idea of whether a method has already been emitted to
+ * the stream. Due to the way we add to this table, it
+ * can easily happen that multiple threads queue up the
+ * same methods, but that's OK since eventually all
+ * methods will be in this table and the thread-local
+ * method lists will just be empty for the rest of the
+ * app's lifetime.
+ */
+ mono_os_mutex_lock (&prof->method_table_mutex);
+ mono_conc_hashtable_insert (prof->method_table, info->method, info->method);
+ mono_os_mutex_unlock (&prof->method_table_mutex);
+
+ char *name = mono_method_full_name (info->method, 1);
+ int nlen = strlen (name) + 1;
+ 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;
+
+ ENTER_LOG (&method_jits_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* method */ +
+ LEB128_SIZE /* start */ +
+ LEB128_SIZE /* size */ +
+ nlen /* name */
+ );
+
+ emit_event_time (logbuffer, TYPE_JIT | TYPE_METHOD, info->time);
+ emit_method_inner (logbuffer, info->method);
+ emit_ptr (logbuffer, cstart);
+ emit_value (logbuffer, csize);
+
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
+
+ EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
+
+ mono_free (name);
+
+ wrote_methods = TRUE;
+
+ free_info:
+ g_free (info);
+ }
+
+ g_ptr_array_free (entry->methods, TRUE);
+
+ if (wrote_methods) {
+ dump_buffer_threadless (prof, PROF_TLS_GET ()->buffer);
+ init_buffer_state (PROF_TLS_GET ());
+ }
+
+ no_methods:
+ dump_buffer (prof, entry->buffer);
+
+ mono_thread_hazardous_try_free (entry, free_writer_entry);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void *
+writer_thread (void *arg)
+{
+ MonoProfiler *prof = (MonoProfiler *)arg;
+
+ mono_threads_attach_tools_thread ();
+ mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler writer");
+
+ dump_header (prof);
+
+ MonoProfilerThread *thread = init_thread (prof, 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;
+}
+
+static void
+start_writer_thread (MonoProfiler* prof)
+{
+ InterlockedWrite (&prof->run_writer_thread, 1);
+
+ if (!mono_native_thread_create (&prof->writer_thread, writer_thread, prof)) {
+ fprintf (stderr, "Could not start writer thread\n");
+ exit (1);
+ }
+}
+
+static void
+reuse_sample_hit (gpointer p)
+{
+ SampleHit *sample = p;
+
+ mono_lock_free_queue_node_unpoison (&sample->node);
+ mono_lock_free_queue_enqueue (&sample->prof->sample_reuse_queue, &sample->node);
+}
+
+static gboolean
+handle_dumper_queue_entry (MonoProfiler *prof)
+{
+ SampleHit *sample;
+
+ if ((sample = (SampleHit *) mono_lock_free_queue_dequeue (&prof->dumper_queue))) {
+ for (int i = 0; i < sample->count; ++i) {
+ MonoMethod *method = sample->frames [i].method;
+ MonoDomain *domain = sample->frames [i].domain;
+ void *address = sample->frames [i].base_address;
+
+ if (!method) {
+ 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);
+
+ if (ji)
+ sample->frames [i].method = mono_jit_info_get_method (ji);
+ }
+ }
+
+ ENTER_LOG (&sample_hits_ctr, logbuffer,
+ EVENT_SIZE /* event */ +
+ BYTE_SIZE /* type */ +
+ LEB128_SIZE /* tid */ +
+ LEB128_SIZE /* count */ +
+ 1 * (
+ LEB128_SIZE /* ip */
+ ) +
+ LEB128_SIZE /* managed count */ +
+ sample->count * (
+ LEB128_SIZE /* method */
+ )
+ );
+
+ emit_event_time (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_HIT, sample->time);
+ emit_byte (logbuffer, SAMPLE_CYCLES);
+ emit_ptr (logbuffer, (void *) sample->tid);
+ emit_value (logbuffer, 1);
+
+ // TODO: Actual native unwinding.
+ for (int i = 0; i < 1; ++i) {
+ emit_ptr (logbuffer, sample->ip);
+ add_code_pointer ((uintptr_t) sample->ip);
+ }
+
+ /* new in data version 6 */
+ emit_uvalue (logbuffer, sample->count);
+
+ for (int i = 0; i < sample->count; ++i)
+ emit_method (logbuffer, sample->frames [i].method);
+
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+ mono_thread_hazardous_try_free (sample, reuse_sample_hit);
+
+ dump_unmanaged_coderefs (prof);
+ }
+
+ return FALSE;
+}
+
+static void *
+dumper_thread (void *arg)
+{
+ MonoProfiler *prof = (MonoProfiler *)arg;
+
+ mono_threads_attach_tools_thread ();
+ mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler dumper");
+
+ MonoProfilerThread *thread = init_thread (prof, 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));
+
+ send_log_unsafe (FALSE);
+ deinit_thread (thread);
+
+ mono_thread_info_detach ();
+
+ return NULL;
+}
+
+static void
+start_dumper_thread (MonoProfiler* prof)
+{
+ InterlockedWrite (&prof->run_dumper_thread, 1);
+
+ if (!mono_native_thread_create (&prof->dumper_thread, dumper_thread, prof)) {
+ fprintf (stderr, "Could not start dumper thread\n");
+ exit (1);
+ }
+}
+
+static void
+register_counter (const char *name, gint32 *counter)
+{
+ mono_counters_register (name, MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, counter);
+}
+
+static void
+runtime_initialized (MonoProfiler *profiler)
+{
+ InterlockedWrite (&runtime_inited, 1);
+
+ start_writer_thread (profiler);
+ start_dumper_thread (profiler);
+
+ register_counter ("Sample events allocated", &sample_allocations_ctr);
+ register_counter ("Log buffers allocated", &buffer_allocations_ctr);
+
+ register_counter ("Event: Sync points", &sync_points_ctr);
+ register_counter ("Event: Heap objects", &heap_objects_ctr);
+ register_counter ("Event: Heap starts", &heap_starts_ctr);
+ register_counter ("Event: Heap ends", &heap_ends_ctr);
+ register_counter ("Event: Heap roots", &heap_roots_ctr);
+ register_counter ("Event: GC events", &gc_events_ctr);
+ register_counter ("Event: GC resizes", &gc_resizes_ctr);
+ register_counter ("Event: GC allocations", &gc_allocs_ctr);
+ register_counter ("Event: GC moves", &gc_moves_ctr);
+ register_counter ("Event: GC handle creations", &gc_handle_creations_ctr);
+ register_counter ("Event: GC handle deletions", &gc_handle_deletions_ctr);
+ register_counter ("Event: GC finalize starts", &finalize_begins_ctr);
+ register_counter ("Event: GC finalize ends", &finalize_ends_ctr);
+ register_counter ("Event: GC finalize object starts", &finalize_object_begins_ctr);
+ register_counter ("Event: GC finalize object ends", &finalize_object_ends_ctr);
+ register_counter ("Event: Image loads", &image_loads_ctr);
+ register_counter ("Event: Image unloads", &image_unloads_ctr);
+ register_counter ("Event: Assembly loads", &assembly_loads_ctr);
+ register_counter ("Event: Assembly unloads", &assembly_unloads_ctr);
+ register_counter ("Event: Class loads", &class_loads_ctr);
+ register_counter ("Event: Class unloads", &class_unloads_ctr);
+ register_counter ("Event: Method entries", &method_entries_ctr);
+ register_counter ("Event: Method exits", &method_exits_ctr);
+ register_counter ("Event: Method exception leaves", &method_exception_exits_ctr);
+ register_counter ("Event: Method JITs", &method_jits_ctr);
+ register_counter ("Event: Code buffers", &code_buffers_ctr);
+ register_counter ("Event: Exception throws", &exception_throws_ctr);
+ register_counter ("Event: Exception clauses", &exception_clauses_ctr);
+ register_counter ("Event: Monitor contentions", &monitor_contentions_ctr);
+ register_counter ("Event: Monitor acquisitions", &monitor_acquisitions_ctr);
+ register_counter ("Event: Monitor failures", &monitor_failures_ctr);
+ register_counter ("Event: Thread starts", &thread_starts_ctr);
+ register_counter ("Event: Thread ends", &thread_ends_ctr);
+ register_counter ("Event: Thread names", &thread_names_ctr);
+ register_counter ("Event: Domain loads", &domain_loads_ctr);
+ register_counter ("Event: Domain unloads", &domain_unloads_ctr);
+ register_counter ("Event: Domain names", &domain_names_ctr);
+ register_counter ("Event: Context loads", &context_loads_ctr);
+ register_counter ("Event: Context unloads", &context_unloads_ctr);
+ register_counter ("Event: Sample binaries", &sample_ubins_ctr);
+ register_counter ("Event: Sample symbols", &sample_usyms_ctr);
+ register_counter ("Event: Sample hits", &sample_hits_ctr);
+ register_counter ("Event: Counter descriptors", &counter_descriptors_ctr);
+ register_counter ("Event: Counter samples", &counter_samples_ctr);
+ register_counter ("Event: Performance counter descriptors", &perfcounter_descriptors_ctr);
+ register_counter ("Event: Performance counter samples", &perfcounter_samples_ctr);
+ register_counter ("Event: Coverage methods", &coverage_methods_ctr);
+ register_counter ("Event: Coverage statements", &coverage_statements_ctr);
+ register_counter ("Event: Coverage classes", &coverage_classes_ctr);
+ register_counter ("Event: Coverage assemblies", &coverage_assemblies_ctr);
+
+ counters_init (profiler);
+
+ start_helper_thread (profiler);
+}
+
+static MonoProfiler*
+create_profiler (const char *args, const char *filename, GPtrArray *filters)
+{
+ MonoProfiler *prof;
+ char *nf;
+ int force_delete = 0;
+ prof = (MonoProfiler *) g_calloc (1, sizeof (MonoProfiler));
+
+ prof->args = pstrdup (args);
+ prof->command_port = command_port;
+ if (filename && *filename == '-') {
+ force_delete = 1;
+ filename++;
+ }
+ if (!filename) {
+ if (do_report)
+ filename = "|mprof-report -";
+ else
+ filename = "output.mlpd";
+ nf = (char*)filename;
+ } else {
+ nf = new_filename (filename);
+ if (do_report) {
+ int s = strlen (nf) + 32;
+ char *p = (char *) g_malloc (s);
+ snprintf (p, s, "|mprof-report '--out=%s' -", nf);
+ g_free (nf);
+ nf = p;
+ }
+ }
+ if (*nf == '|') {
+ prof->file = popen (nf + 1, "w");
+ prof->pipe_output = 1;
+ } else if (*nf == '#') {
+ int fd = strtol (nf + 1, NULL, 10);
+ prof->file = fdopen (fd, "a");
+ } else {
+ if (force_delete)
+ unlink (nf);
+ prof->file = fopen (nf, "wb");
+ }
+ if (!prof->file) {
+ fprintf (stderr, "Cannot create profiler output: %s\n", nf);
+ exit (1);
+ }
+
+#if defined (HAVE_SYS_ZLIB)
+ if (use_zip)
+ prof->gzfile = gzdopen (fileno (prof->file), "wb");
+#endif
+
+ /*
+ * If you hit this assert while increasing MAX_FRAMES, you need to increase
+ * SAMPLE_BLOCK_SIZE as well.
+ */
+ g_assert (SAMPLE_SLOT_SIZE (MAX_FRAMES) * 2 < LOCK_FREE_ALLOC_SB_USABLE_SIZE (SAMPLE_BLOCK_SIZE));
+
+ // FIXME: We should free this stuff too.
+ mono_lock_free_allocator_init_size_class (&prof->sample_size_class, SAMPLE_SLOT_SIZE (num_frames), SAMPLE_BLOCK_SIZE);
+ mono_lock_free_allocator_init_allocator (&prof->sample_allocator, &prof->sample_size_class, MONO_MEM_ACCOUNT_PROFILER);
+
+ mono_lock_free_queue_init (&prof->sample_reuse_queue);
+
+ 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_MEM_ACCOUNT_PROFILER);
+
+ mono_lock_free_queue_init (&prof->writer_queue);
+ mono_os_sem_init (&prof->writer_queue_sem, 0);
+
+ mono_lock_free_queue_init (&prof->dumper_queue);
+ mono_os_sem_init (&prof->dumper_queue_sem, 0);
+
+ mono_os_mutex_init (&prof->method_table_mutex);
+ prof->method_table = mono_conc_hashtable_new (NULL, NULL);
+
+ if (do_coverage)
+ coverage_init (prof);
+ prof->coverage_filters = filters;
+
+ prof->startup_time = current_time ();
+ return prof;
+}
+
+static void
+usage (int do_exit)
+{
+ printf ("Log profiler version %d.%d (format: %d)\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
+ printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
+ printf ("Options:\n");
+ printf ("\thelp show this usage info\n");
+ printf ("\t[no]alloc enable/disable recording allocation info\n");
+ printf ("\t[no]calls enable/disable recording enter/leave method events\n");
+ printf ("\theapshot[=MODE] record heap shot info (by default at each major collection)\n");
+ printf ("\t MODE: every XXms milliseconds, every YYgc collections, ondemand\n");
+ printf ("\tcounters sample counters every 1s\n");
+ printf ("\tsample[=TYPE] use statistical sampling mode (by default cycles/100)\n");
+ printf ("\t TYPE: cycles,instr,cacherefs,cachemiss,branches,branchmiss\n");
+ printf ("\t TYPE can be followed by /FREQUENCY\n");
+ printf ("\tmaxframes=NUM collect up to NUM stack frames\n");
+ printf ("\tcalldepth=NUM ignore method events for call chain depth bigger than NUM\n");
+ printf ("\toutput=FILENAME write the data to file FILENAME (-FILENAME to overwrite)\n");
+ printf ("\toutput=|PROGRAM write the data to the stdin of PROGRAM\n");
+ printf ("\t %%t is subtituted with date and time, %%p with the pid\n");
+ printf ("\treport create a report instead of writing the raw data to a file\n");
+ printf ("\tzip compress the output data\n");
+ printf ("\tport=PORTNUM use PORTNUM for the listening command server\n");
+ printf ("\tcoverage enable collection of code coverage data\n");
+ printf ("\tcovfilter=ASSEMBLY add an assembly to the code coverage filters\n");
+ printf ("\t add a + to include the assembly or a - to exclude it\n");
+ printf ("\t filter=-mscorlib\n");
+ printf ("\tcovfilter-file=FILE use FILE to generate the list of assemblies to be filtered\n");
+ if (do_exit)
+ exit (1);
+}
+
+static const char*
+match_option (const char* p, const char *opt, char **rval)
+{
+ int len = strlen (opt);
+ if (strncmp (p, opt, len) == 0) {
+ if (rval) {
+ if (p [len] == '=' && p [len + 1]) {
+ const char *opt = p + len + 1;
+ const char *end = strchr (opt, ',');
+ char *val;
+ int l;
+ if (end == NULL) {
+ l = strlen (opt);
+ } else {
+ l = end - opt;
+ }
+ val = (char *) g_malloc (l + 1);
+ memcpy (val, opt, l);
+ val [l] = 0;
+ *rval = val;
+ return opt + l;
+ }
+ if (p [len] == 0 || p [len] == ',') {
+ *rval = NULL;
+ return p + len + (p [len] == ',');
+ }
+ usage (1);
+ } else {
+ if (p [len] == 0)
+ return p + len;
+ if (p [len] == ',')
+ return p + len + 1;
+ }
+ }
+ return p;
+}
+
+static void
+set_sample_freq (char *val)
+{
+ do_mono_sample = 1;
+ sample_freq = 100;
+
+ if (!val)
+ return;
+
+ char *p = val;
+
+ // Is it only the frequency (new option style)?
+ if (isdigit (*p))
+ goto parse;
+
+ // Skip the sample type for backwards compatibility.
+ while (isalpha (*p))
+ p++;
+
+ // Skip the forward slash only if we got a sample type.
+ if (p != val && *p == '/') {
+ p++;
+
+ char *end;
+
+ parse:
+ sample_freq = strtoul (p, &end, 10);
+
+ if (p == end)
+ usage (1);
+
+ p = end;
+ }
+
+ if (*p)
+ usage (1);
+
+ g_free (val);
+}
+
+static void
+set_hsmode (char* val, int allow_empty)
+{
+ char *end;
+ unsigned int count;
+ if (allow_empty && !val)
+ return;
+ if (strcmp (val, "ondemand") == 0) {
+ hs_mode_ondemand = 1;
+ g_free (val);
+ return;
+ }
+ count = strtoul (val, &end, 10);
+ if (val == end)
+ usage (1);
+ if (strcmp (end, "ms") == 0)
+ hs_mode_ms = count;
+ else if (strcmp (end, "gc") == 0)
+ hs_mode_gc = count;
+ else
+ usage (1);
+ g_free (val);
+}
+
+/*
+ * declaration to silence the compiler: this is the entry point that
+ * mono will load from the shared library and call.
+ */
+extern void
+mono_profiler_startup (const char *desc);
+
+extern void
+mono_profiler_startup_log (const char *desc);
+
+/*
+ * this is the entry point that will be used when the profiler
+ * is embedded inside the main executable.
+ */
+void
+mono_profiler_startup_log (const char *desc)
+{
+ mono_profiler_startup (desc);
+}
+
+void
+mono_profiler_startup (const char *desc)
+{
+ MonoProfiler *prof;
+ GPtrArray *filters = NULL;
+ char *filename = NULL;
+ const char *p;
+ const char *opt;
+ int calls_enabled = 0;
+ int allocs_enabled = 0;
+ int events = MONO_PROFILE_GC|MONO_PROFILE_ALLOCATIONS|
+ MONO_PROFILE_GC_MOVES|MONO_PROFILE_CLASS_EVENTS|MONO_PROFILE_THREADS|
+ 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_GC_FINALIZATION;
+
+ max_allocated_sample_hits = mono_cpu_count () * 1000;
+
+ p = desc;
+ if (strncmp (p, "log", 3))
+ usage (1);
+ p += 3;
+ if (*p == ':')
+ p++;
+ for (; *p; p = opt) {
+ char *val;
+ if (*p == ',') {
+ opt = p + 1;
+ continue;
+ }
+ if ((opt = match_option (p, "help", NULL)) != p) {
+ usage (0);
+ continue;
+ }
+ if ((opt = match_option (p, "calls", NULL)) != p) {
+ calls_enabled = 1;
+ continue;
+ }
+ if ((opt = match_option (p, "nocalls", NULL)) != p) {
+ events &= ~MONO_PROFILE_ENTER_LEAVE;
+ nocalls = 1;
+ continue;
+ }
+ if ((opt = match_option (p, "alloc", NULL)) != p) {
+ allocs_enabled = 1;
+ continue;
+ }
+ if ((opt = match_option (p, "noalloc", NULL)) != p) {
+ events &= ~MONO_PROFILE_ALLOCATIONS;
+ events &= ~MONO_PROFILE_GC_MOVES;
+ continue;
+ }
+ if ((opt = match_option (p, "nocounters", NULL)) != p) {
+ no_counters = TRUE;
+ continue;
+ }
+ if ((opt = match_option (p, "time", &val)) != p) {
+ // For backwards compatibility.
+ if (strcmp (val, "fast") && strcmp (val, "null"))
+ usage (1);
+ g_free (val);
+ continue;
+ }
+ if ((opt = match_option (p, "report", NULL)) != p) {
+ do_report = 1;
+ continue;
+ }
+ if ((opt = match_option (p, "debug", NULL)) != p) {
+ do_debug = 1;
+ continue;
+ }
+ if ((opt = match_option (p, "sampling-real", NULL)) != p) {
+ sampling_mode = MONO_PROFILER_STAT_MODE_REAL;
+ continue;
+ }
+ if ((opt = match_option (p, "sampling-process", NULL)) != p) {
+ sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
+ continue;
+ }
+ if ((opt = match_option (p, "heapshot", &val)) != p) {
+ events &= ~MONO_PROFILE_ALLOCATIONS;
+ events &= ~MONO_PROFILE_GC_MOVES;
+ events &= ~MONO_PROFILE_ENTER_LEAVE;
+ nocalls = 1;
+ do_heap_shot = 1;
+ set_hsmode (val, 1);
+ continue;
+ }
+ if ((opt = match_option (p, "sample", &val)) != p) {
+ events &= ~MONO_PROFILE_ALLOCATIONS;
+ events &= ~MONO_PROFILE_GC_MOVES;
+ events &= ~MONO_PROFILE_ENTER_LEAVE;
+ nocalls = 1;
+ set_sample_freq (val);
+ continue;
+ }
+ if ((opt = match_option (p, "zip", NULL)) != p) {
+ use_zip = 1;
+ continue;
+ }
+ if ((opt = match_option (p, "output", &val)) != p) {
+ filename = val;
+ continue;
+ }
+ if ((opt = match_option (p, "port", &val)) != p) {
+ char *end;
+ command_port = strtoul (val, &end, 10);
+ g_free (val);
+ continue;
+ }
+ if ((opt = match_option (p, "maxframes", &val)) != p) {
+ char *end;
+ num_frames = strtoul (val, &end, 10);
+ if (num_frames > MAX_FRAMES)
+ num_frames = MAX_FRAMES;
+ g_free (val);
+ notraces = num_frames == 0;
+ continue;
+ }
+ if ((opt = match_option (p, "maxsamples", &val)) != p) {
+ char *end;
+ max_allocated_sample_hits = strtoul (val, &end, 10);
+ if (!max_allocated_sample_hits)
+ max_allocated_sample_hits = G_MAXINT32;
+ g_free (val);
+ continue;
+ }
+ if ((opt = match_option (p, "calldepth", &val)) != p) {
+ char *end;
+ max_call_depth = strtoul (val, &end, 10);
+ g_free (val);
+ continue;
+ }
+ if ((opt = match_option (p, "counters", NULL)) != p) {
+ // For backwards compatibility.
+ continue;
+ }
+ if ((opt = match_option (p, "coverage", NULL)) != p) {
+ do_coverage = 1;
+ events |= MONO_PROFILE_ENTER_LEAVE;
+ debug_coverage = (g_getenv ("MONO_PROFILER_DEBUG_COVERAGE") != NULL);
+ continue;
+ }
+ if ((opt = match_option (p, "onlycoverage", NULL)) != p) {
+ only_coverage = TRUE;
+ continue;
+ }
+ if ((opt = match_option (p, "covfilter-file", &val)) != p) {
+ FILE *filter_file;
+ char *line, *content;
+
+ if (filters == NULL)
+ filters = g_ptr_array_new ();
+
+ filter_file = fopen (val, "r");
+ if (filter_file == NULL) {
+ fprintf (stderr, "Unable to open %s\n", val);
+ exit (0);
+ }
+
+ /* Don't need to free content as it is referred to by the lines stored in @filters */
+ content = get_file_content (filter_file);
+ if (content == NULL)
+ fprintf (stderr, "WARNING: %s is greater than 128kb - ignoring\n", val);
+
+ while ((line = get_next_line (content, &content)))
+ g_ptr_array_add (filters, g_strchug (g_strchomp (line)));
+
+ fclose (filter_file);
+ continue;
+ }
+ if ((opt = match_option (p, "covfilter", &val)) != p) {
+ if (filters == NULL)
+ filters = g_ptr_array_new ();
+
+ g_ptr_array_add (filters, val);
+ continue;
+ }
+ if (opt == p) {
+ usage (0);
+ exit (0);
+ }
+ }
+
+ if (calls_enabled) {
+ events |= MONO_PROFILE_ENTER_LEAVE;
+ nocalls = 0;
+ }
+
+ if (allocs_enabled) {
+ events |= MONO_PROFILE_ALLOCATIONS;
+ events |= MONO_PROFILE_GC_MOVES;
+ }
+
+ // Only activate the bare minimum events the profiler needs to function.
+ if (only_coverage) {
+ if (!do_coverage) {
+ fprintf (stderr, "The onlycoverage option is only valid when paired with the coverage option\n");
+ exit (1);
+ }
+
+ no_counters = TRUE;
+ events = MONO_PROFILE_GC | MONO_PROFILE_THREADS | MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_INS_COVERAGE;
+ }
+
+ init_time ();
+
+ PROF_TLS_INIT ();
+
+ prof = create_profiler (desc, filename, filters);
+ if (!prof) {
+ PROF_TLS_FREE ();
+ return;
+ }
+
+ mono_lls_init (&profiler_thread_list, NULL);
+
+ init_thread (prof, 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_install_class (NULL, class_loaded, class_unloaded, NULL);
+ mono_profiler_install_module (NULL, image_loaded, image_unloaded, NULL);
+ mono_profiler_install_assembly (NULL, assembly_loaded, assembly_unloaded, NULL);
+ mono_profiler_install_thread (thread_start, thread_end);
+ mono_profiler_install_thread_name (thread_name);
+ mono_profiler_install_enter_leave (method_enter, method_leave);
+ mono_profiler_install_jit_end (method_jitted);
+ mono_profiler_install_code_buffer_new (code_buffer_new);
+ mono_profiler_install_exception (throw_exc, method_exc_leave, clause_exc);
+ mono_profiler_install_monitor (monitor_event);
+ mono_profiler_install_runtime_initialized (runtime_initialized);
+ if (do_coverage)
+ mono_profiler_install_coverage_filter (coverage_filter);
+
+ if (do_mono_sample && sample_freq) {
+ events |= MONO_PROFILE_STATISTICAL;
+ mono_profiler_set_statistical_mode (sampling_mode, sample_freq);
+ mono_profiler_install_statistical (mono_sample_hit);
+ }
+
+ mono_profiler_set_events ((MonoProfileFlags)events);
+}
--- /dev/null
+#ifndef __MONO_PROFLOG_H__
+#define __MONO_PROFLOG_H__
+
+#define BUF_ID 0x4D504C01
+#define LOG_HEADER_ID 0x4D505A01
+#define LOG_VERSION_MAJOR 1
+#define LOG_VERSION_MINOR 1
+#define LOG_DATA_VERSION 13
+
+/*
+ * Changes in major/minor versions:
+ * version 1.0: removed sysid field from header
+ * added args, arch, os fields to header
+ *
+ * Changes in data versions:
+ * version 2: added offsets in heap walk
+ * version 3: added GC roots
+ * version 4: added sample/statistical profiling
+ * version 5: added counters sampling
+ * version 6: added optional backtrace in sampling info
+ * version 8: added TYPE_RUNTIME and JIT helpers/trampolines
+ * version 9: added MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
+ * version 10: added TYPE_COVERAGE
+ * version 11: added thread ID to TYPE_SAMPLE_HIT
+ added more load/unload events
+ unload for class
+ unload for image
+ load/unload for appdomain
+ load/unload for contexts
+ load/unload/name for assemblies
+ removed TYPE_LOAD_ERR flag (profiler never generated it, now removed from the format itself)
+ 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_ALLOC,
+ TYPE_GC,
+ TYPE_METADATA,
+ TYPE_METHOD,
+ TYPE_EXCEPTION,
+ TYPE_MONITOR,
+ TYPE_HEAP,
+ TYPE_SAMPLE,
+ TYPE_RUNTIME,
+ TYPE_COVERAGE,
+ TYPE_META,
+ /* extended type for TYPE_HEAP */
+ TYPE_HEAP_START = 0 << 4,
+ TYPE_HEAP_END = 1 << 4,
+ TYPE_HEAP_OBJECT = 2 << 4,
+ TYPE_HEAP_ROOT = 3 << 4,
+ /* extended type for TYPE_METADATA */
+ TYPE_END_LOAD = 2 << 4,
+ TYPE_END_UNLOAD = 4 << 4,
+ /* extended type for TYPE_GC */
+ TYPE_GC_EVENT = 1 << 4,
+ TYPE_GC_RESIZE = 2 << 4,
+ TYPE_GC_MOVE = 3 << 4,
+ TYPE_GC_HANDLE_CREATED = 4 << 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_EXC_LEAVE = 3 << 4,
+ TYPE_JIT = 4 << 4,
+ /* extended type for TYPE_EXCEPTION */
+ TYPE_THROW_NO_BT = 0 << 7,
+ TYPE_THROW_BT = 1 << 7,
+ TYPE_CLAUSE = 1 << 4,
+ /* extended type for TYPE_ALLOC */
+ TYPE_ALLOC_NO_BT = 0 << 4,
+ TYPE_ALLOC_BT = 1 << 4,
+ /* extended type for TYPE_MONITOR */
+ TYPE_MONITOR_NO_BT = 0 << 7,
+ TYPE_MONITOR_BT = 1 << 7,
+ /* extended type for TYPE_SAMPLE */
+ TYPE_SAMPLE_HIT = 0 << 4,
+ TYPE_SAMPLE_USYM = 1 << 4,
+ TYPE_SAMPLE_UBIN = 2 << 4,
+ TYPE_SAMPLE_COUNTERS_DESC = 3 << 4,
+ TYPE_SAMPLE_COUNTERS = 4 << 4,
+ /* extended type for TYPE_RUNTIME */
+ TYPE_JITHELPER = 1 << 4,
+ /* extended type for TYPE_COVERAGE */
+ TYPE_COVERAGE_ASSEMBLY = 0 << 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
+};
+
+enum {
+ /* metadata type byte for TYPE_METADATA */
+ TYPE_CLASS = 1,
+ TYPE_IMAGE = 2,
+ TYPE_ASSEMBLY = 3,
+ TYPE_DOMAIN = 4,
+ TYPE_THREAD = 5,
+ TYPE_CONTEXT = 6,
+};
+
+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 {
+ SAMPLE_CYCLES = 1,
+ SAMPLE_INSTRUCTIONS,
+ SAMPLE_CACHE_MISSES,
+ SAMPLE_CACHE_REFS,
+ SAMPLE_BRANCHES,
+ SAMPLE_BRANCH_MISSES,
+ SAMPLE_LAST
+};
+
+#endif /* __MONO_PROFLOG_H__ */
--- /dev/null
+/*
+ * mprof-report.c: mprof-report program source: decode and analyze the log profiler data
+ *
+ * Authors:
+ * Paolo Molaro (lupus@ximian.com)
+ * Alex Rønne Petersen (alexrp@xamarin.com)
+ *
+ * Copyright 2010 Novell, Inc (http://www.novell.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+ */
+
+/*
+ * The Coverage XML output schema
+ * <coverage>
+ * <assembly/>
+ * <class/>
+ * <method>
+ * <statement/>
+ * </method>
+ * </coverage>
+ *
+ * Elements:
+ * <coverage> - The root element of the documentation. It can contain any number of
+ * <assembly>, <class> or <method> elements.
+ * Attributes:
+ * - version: The version number for the file format - (eg: "0.3")
+ * <assembly> - Contains data about assemblies. Has no child elements
+ * Attributes:
+ * - name: The name of the assembly - (eg: "System.Xml")
+ * - guid: The GUID of the assembly
+ * - filename: The filename of the assembly
+ * - method-count: The number of methods in the assembly
+ * - full: The number of fully covered methods
+ * - partial: The number of partially covered methods
+ * <class> - Contains data about classes. Has no child elements
+ * Attributes:
+ * - name: The name of the class
+ * - method-count: The number of methods in the class
+ * - full: The number of fully covered methods
+ * - partial: The number of partially covered methods
+ * <method> - Contains data about methods. Can contain any number of <statement> elements
+ * Attributes:
+ * - assembly: The name of the parent assembly
+ * - class: The name of the parent class
+ * - name: The name of the method, with all it's parameters
+ * - filename: The name of the source file containing this method
+ * - token
+ * <statement> - Contains data about IL statements. Has no child elements
+ * Attributes:
+ * - offset: The offset of the statement in the IL code after the previous
+ * statement's offset
+ * - counter: 1 if the line was covered, 0 if it was not
+ * - line: The line number in the parent method's file
+ * - column: The column on the line
+ */
+#include <config.h>
+#include "mono-profiler-log.h"
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include <time.h>
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
+#include <malloc.h>
+#endif
+#include <unistd.h>
+#include <stdlib.h>
+#if defined (HAVE_SYS_ZLIB)
+#include <zlib.h>
+#endif
+#include <glib.h>
+#include <mono/metadata/profiler.h>
+#include <mono/metadata/object.h>
+#include <mono/metadata/debug-helpers.h>
+#include <mono/utils/mono-counters.h>
+
+#define HASH_SIZE 9371
+#define SMALL_HASH_SIZE 31
+
+#if defined(__native_client__) || defined(__native_client_codegen__)
+volatile int __nacl_thread_suspension_needed = 0;
+void __nacl_suspend_thread_if_needed() {}
+#endif
+
+static int debug = 0;
+static int collect_traces = 0;
+static int show_traces = 0;
+static int trace_max = 6;
+static int verbose = 0;
+static uintptr_t *tracked_objects = 0;
+static int num_tracked_objects = 0;
+static uintptr_t thread_filter = 0;
+static uint64_t find_size = 0;
+static const char* find_name = NULL;
+static uint64_t time_from = 0;
+static uint64_t time_to = 0xffffffffffffffffULL;
+static int use_time_filter = 0;
+static uint64_t startup_time = 0;
+static FILE* outfile = NULL;
+static FILE* coverage_outfile = NULL;
+
+static int32_t
+read_int16 (unsigned char *p)
+{
+ int32_t value = *p++;
+ value |= (*p++) << 8;
+ return value;
+}
+
+static int32_t
+read_int32 (unsigned char *p)
+{
+ int32_t value = *p++;
+ value |= (*p++) << 8;
+ value |= (*p++) << 16;
+ value |= (uint32_t)(*p++) << 24;
+ return value;
+}
+
+static int64_t
+read_int64 (unsigned char *p)
+{
+ uint64_t value = *p++;
+ value |= (*p++) << 8;
+ value |= (*p++) << 16;
+ value |= (uint64_t)(*p++) << 24;
+ value |= (uint64_t)(*p++) << 32;
+ value |= (uint64_t)(*p++) << 40;
+ value |= (uint64_t)(*p++) << 48;
+ value |= (uint64_t)(*p++) << 54;
+ return value;
+}
+
+static char*
+pstrdup (const char *s)
+{
+ int len = strlen (s) + 1;
+ char *p = (char *) g_malloc (len);
+ memcpy (p, s, len);
+ return p;
+}
+
+typedef struct _CounterValue CounterValue;
+struct _CounterValue {
+ uint64_t timestamp;
+ unsigned char *buffer;
+ CounterValue *next;
+};
+
+typedef struct _Counter Counter;
+struct _Counter {
+ int index;
+ const char *section;
+ const char *name;
+ int type;
+ int unit;
+ int variance;
+ CounterValue *values;
+ CounterValue *values_last;
+};
+
+typedef struct _CounterList CounterList;
+struct _CounterList {
+ Counter *counter;
+ CounterList *next;
+};
+
+typedef struct _CounterSection CounterSection;
+struct _CounterSection {
+ const char *value;
+ CounterList *counters;
+ CounterList *counters_last;
+ CounterSection *next;
+};
+
+typedef struct _CounterTimestamp CounterTimestamp;
+struct _CounterTimestamp {
+ uint64_t value;
+ CounterSection *sections;
+ CounterSection *sections_last;
+ CounterTimestamp *next;
+};
+
+static CounterList *counters = NULL;
+static CounterSection *counters_sections = NULL;
+static CounterTimestamp *counters_timestamps = NULL;
+
+enum {
+ COUNTERS_SORT_TIME,
+ COUNTERS_SORT_CATEGORY
+};
+
+static int counters_sort_mode = COUNTERS_SORT_TIME;
+
+static void
+add_counter_to_section (Counter *counter)
+{
+ CounterSection *csection, *s;
+ CounterList *clist;
+
+ clist = (CounterList *) g_calloc (1, sizeof (CounterList));
+ clist->counter = counter;
+
+ for (csection = counters_sections; csection; csection = csection->next) {
+ if (strcmp (csection->value, counter->section) == 0) {
+ /* If section exist */
+ if (!csection->counters)
+ csection->counters = clist;
+ else
+ csection->counters_last->next = clist;
+ csection->counters_last = clist;
+ return;
+ }
+ }
+
+ /* If section does not exist */
+ csection = (CounterSection *) g_calloc (1, sizeof (CounterSection));
+ csection->value = counter->section;
+ csection->counters = clist;
+ csection->counters_last = clist;
+
+ if (!counters_sections) {
+ counters_sections = csection;
+ } else {
+ s = counters_sections;
+ while (s->next)
+ s = s->next;
+ s->next = csection;
+ }
+}
+
+static void
+add_counter (const char *section, const char *name, int type, int unit, int variance, int index)
+{
+ CounterList *list, *l;
+ Counter *counter;
+
+ for (list = counters; list; list = list->next)
+ if (list->counter->index == index)
+ return;
+
+ counter = (Counter *) g_calloc (1, sizeof (Counter));
+ counter->section = section;
+ counter->name = name;
+ counter->type = type;
+ counter->unit = unit;
+ counter->variance = variance;
+ counter->index = index;
+
+ list = (CounterList *) g_calloc (1, sizeof (CounterList));
+ list->counter = counter;
+
+ if (!counters) {
+ counters = list;
+ } else {
+ l = counters;
+ while (l->next)
+ l = l->next;
+ l->next = list;
+ }
+
+ if (counters_sort_mode == COUNTERS_SORT_CATEGORY || !verbose)
+ add_counter_to_section (counter);
+}
+
+static void
+add_counter_to_timestamp (uint64_t timestamp, Counter *counter)
+{
+ CounterTimestamp *ctimestamp, *t;
+ CounterSection *csection;
+ CounterList *clist;
+
+ clist = (CounterList *) g_calloc (1, sizeof (CounterList));
+ clist->counter = counter;
+
+ for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
+ if (ctimestamp->value == timestamp) {
+ for (csection = ctimestamp->sections; csection; csection = csection->next) {
+ if (strcmp (csection->value, counter->section) == 0) {
+ /* if timestamp exist and section exist */
+ if (!csection->counters)
+ csection->counters = clist;
+ else
+ csection->counters_last->next = clist;
+ csection->counters_last = clist;
+ return;
+ }
+ }
+
+ /* if timestamp exist and section does not exist */
+ csection = (CounterSection *) g_calloc (1, sizeof (CounterSection));
+ csection->value = counter->section;
+ csection->counters = clist;
+ csection->counters_last = clist;
+
+ if (!ctimestamp->sections)
+ ctimestamp->sections = csection;
+ else
+ ctimestamp->sections_last->next = csection;
+ ctimestamp->sections_last = csection;
+ return;
+ }
+ }
+
+ /* If timestamp do not exist and section does not exist */
+ csection = (CounterSection *) g_calloc (1, sizeof (CounterSection));
+ csection->value = counter->section;
+ csection->counters = clist;
+ csection->counters_last = clist;
+
+ ctimestamp = (CounterTimestamp *) g_calloc (1, sizeof (CounterTimestamp));
+ ctimestamp->value = timestamp;
+ ctimestamp->sections = csection;
+ ctimestamp->sections_last = csection;
+
+ if (!counters_timestamps) {
+ counters_timestamps = ctimestamp;
+ } else {
+ t = counters_timestamps;
+ while (t->next)
+ t = t->next;
+ t->next = ctimestamp;
+ }
+}
+
+static void
+add_counter_value (int index, CounterValue *value)
+{
+ CounterList *list;
+
+ for (list = counters; list; list = list->next) {
+ if (list->counter->index == index) {
+ if (!list->counter->values)
+ list->counter->values = value;
+ else
+ list->counter->values_last->next = value;
+ list->counter->values_last = value;
+
+ if (counters_sort_mode == COUNTERS_SORT_TIME)
+ add_counter_to_timestamp (value->timestamp, list->counter);
+
+ return;
+ }
+ }
+}
+
+static const char*
+section_name (int section)
+{
+ switch (section) {
+ case MONO_COUNTER_JIT: return "Mono JIT";
+ case MONO_COUNTER_GC: return "Mono GC";
+ case MONO_COUNTER_METADATA: return "Mono Metadata";
+ case MONO_COUNTER_GENERICS: return "Mono Generics";
+ case MONO_COUNTER_SECURITY: return "Mono Security";
+ case MONO_COUNTER_RUNTIME: return "Mono Runtime";
+ case MONO_COUNTER_SYSTEM: return "Mono System";
+ case MONO_COUNTER_PROFILER: return "Mono Profiler";
+ default: return "<unknown>";
+ }
+}
+
+static const char*
+type_name (int type)
+{
+ switch (type) {
+ case MONO_COUNTER_INT: return "Int";
+ case MONO_COUNTER_UINT: return "UInt";
+ case MONO_COUNTER_WORD: return "Word";
+ case MONO_COUNTER_LONG: return "Long";
+ case MONO_COUNTER_ULONG: return "ULong";
+ case MONO_COUNTER_DOUBLE: return "Double";
+ case MONO_COUNTER_STRING: return "String";
+ case MONO_COUNTER_TIME_INTERVAL: return "Time Interval";
+ default: return "<unknown>";
+ }
+}
+
+static const char*
+unit_name (int unit)
+{
+ switch (unit) {
+ case MONO_COUNTER_RAW: return "Raw";
+ case MONO_COUNTER_BYTES: return "Bytes";
+ case MONO_COUNTER_TIME: return "Time";
+ case MONO_COUNTER_COUNT: return "Count";
+ case MONO_COUNTER_PERCENTAGE: return "Percentage";
+ default: return "<unknown>";
+ }
+}
+
+static const char*
+variance_name (int variance)
+{
+ switch (variance) {
+ case MONO_COUNTER_MONOTONIC: return "Monotonic";
+ case MONO_COUNTER_CONSTANT: return "Constant";
+ case MONO_COUNTER_VARIABLE: return "Variable";
+ default: return "<unknown>";
+ }
+}
+
+static void
+dump_counters_value (Counter *counter, const char *key_format, const char *key, void *value)
+{
+ char format[32];
+
+ if (value == NULL) {
+ snprintf (format, sizeof (format), "%s : %%s\n", key_format);
+ fprintf (outfile, format, key, "<null>");
+ } else {
+ switch (counter->type) {
+ case MONO_COUNTER_INT:
+#if SIZEOF_VOID_P == 4
+ case MONO_COUNTER_WORD:
+#endif
+ snprintf (format, sizeof (format), "%s : %%d\n", key_format);
+ fprintf (outfile, format, key, *(int32_t*)value);
+ break;
+ case MONO_COUNTER_UINT:
+ snprintf (format, sizeof (format), "%s : %%u\n", key_format);
+ fprintf (outfile, format, key, *(uint32_t*)value);
+ break;
+ case MONO_COUNTER_LONG:
+#if SIZEOF_VOID_P == 8
+ case MONO_COUNTER_WORD:
+#endif
+ case MONO_COUNTER_TIME_INTERVAL:
+ if (counter->type == MONO_COUNTER_LONG && counter->unit == MONO_COUNTER_TIME) {
+ snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
+ fprintf (outfile, format, key, (double)*(int64_t*)value / 10000.0);
+ } else if (counter->type == MONO_COUNTER_TIME_INTERVAL) {
+ snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
+ fprintf (outfile, format, key, (double)*(int64_t*)value / 1000.0);
+ } else {
+ snprintf (format, sizeof (format), "%s : %%u\n", key_format);
+ fprintf (outfile, format, key, *(int64_t*)value);
+ }
+ break;
+ case MONO_COUNTER_ULONG:
+ snprintf (format, sizeof (format), "%s : %%llu\n", key_format);
+ fprintf (outfile, format, key, *(uint64_t*)value);
+ break;
+ case MONO_COUNTER_DOUBLE:
+ snprintf (format, sizeof (format), "%s : %%f\n", key_format);
+ fprintf (outfile, format, key, *(double*)value);
+ break;
+ case MONO_COUNTER_STRING:
+ snprintf (format, sizeof (format), "%s : %%s\n", key_format);
+ fprintf (outfile, format, key, *(char*)value);
+ break;
+ }
+ }
+}
+
+static void
+dump_counters (void)
+{
+ Counter *counter;
+ CounterValue *cvalue;
+ CounterTimestamp *ctimestamp;
+ CounterSection *csection;
+ CounterList *clist;
+ char strtimestamp[17];
+ int i, section_printed;
+
+ fprintf (outfile, "\nCounters:\n");
+
+ if (!verbose) {
+ char counters_to_print[][64] = {
+ "Methods from AOT",
+ "Methods JITted using mono JIT",
+ "Methods JITted using LLVM",
+ "Total time spent JITting (sec)",
+ "User Time",
+ "System Time",
+ "Total Time",
+ "Working Set",
+ "Private Bytes",
+ "Virtual Bytes",
+ "Page Faults",
+ "CPU Load Average - 1min",
+ "CPU Load Average - 5min",
+ "CPU Load Average - 15min",
+ ""
+ };
+
+ for (csection = counters_sections; csection; csection = csection->next) {
+ section_printed = 0;
+
+ for (clist = csection->counters; clist; clist = clist->next) {
+ counter = clist->counter;
+ if (!counter->values_last)
+ continue;
+
+ for (i = 0; counters_to_print [i][0] != 0; i++) {
+ if (strcmp (counters_to_print [i], counter->name) == 0) {
+ if (!section_printed) {
+ fprintf (outfile, "\t%s:\n", csection->value);
+ section_printed = 1;
+ }
+
+ dump_counters_value (counter, "\t\t%-30s", counter->name, counter->values_last->buffer);
+ break;
+ }
+ }
+ }
+ }
+ } else if (counters_sort_mode == COUNTERS_SORT_TIME) {
+ for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
+ fprintf (outfile, "\t%llu:%02llu:%02llu:%02llu.%03llu:\n",
+ (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 / 24 % 1000),
+ (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 % 24),
+ (unsigned long long) (ctimestamp->value / 1000 / 60 % 60),
+ (unsigned long long) (ctimestamp->value / 1000 % 60),
+ (unsigned long long) (ctimestamp->value % 1000));
+
+ for (csection = ctimestamp->sections; csection; csection = csection->next) {
+ fprintf (outfile, "\t\t%s:\n", csection->value);
+
+ for (clist = csection->counters; clist; clist = clist->next) {
+ counter = clist->counter;
+ for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
+ if (cvalue->timestamp != ctimestamp->value)
+ continue;
+
+ dump_counters_value (counter, "\t\t\t%-30s", counter->name, cvalue->buffer);
+ }
+ }
+ }
+ }
+ } else if (counters_sort_mode == COUNTERS_SORT_CATEGORY) {
+ for (csection = counters_sections; csection; csection = csection->next) {
+ fprintf (outfile, "\t%s:\n", csection->value);
+
+ for (clist = csection->counters; clist; clist = clist->next) {
+ counter = clist->counter;
+ fprintf (outfile, "\t\t%s: [type: %s, unit: %s, variance: %s]\n",
+ counter->name, type_name (counter->type), unit_name (counter->unit), variance_name (counter->variance));
+
+ for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
+ snprintf (strtimestamp, sizeof (strtimestamp), "%llu:%02llu:%02llu:%02llu.%03llu",
+ (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 / 24 % 1000),
+ (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 % 24),
+ (unsigned long long) (cvalue->timestamp / 1000 / 60 % 60),
+ (unsigned long long) (cvalue->timestamp / 1000 % 60),
+ (unsigned long long) (cvalue->timestamp % 1000));
+
+ dump_counters_value (counter, "\t\t\t%s", strtimestamp, cvalue->buffer);
+ }
+ }
+ }
+ }
+}
+
+static int num_images;
+typedef struct _ImageDesc ImageDesc;
+struct _ImageDesc {
+ ImageDesc *next;
+ intptr_t image;
+ char *filename;
+};
+
+static ImageDesc* image_hash [SMALL_HASH_SIZE] = {0};
+
+static void
+add_image (intptr_t image, char *name)
+{
+ int slot = ((image >> 2) & 0xffff) % SMALL_HASH_SIZE;
+ ImageDesc *cd = (ImageDesc *) g_malloc (sizeof (ImageDesc));
+ cd->image = image;
+ cd->filename = pstrdup (name);
+ cd->next = image_hash [slot];
+ image_hash [slot] = cd;
+ num_images++;
+}
+
+static int num_assemblies;
+
+typedef struct _AssemblyDesc AssemblyDesc;
+struct _AssemblyDesc {
+ AssemblyDesc *next;
+ intptr_t assembly;
+ char *asmname;
+};
+
+static AssemblyDesc* assembly_hash [SMALL_HASH_SIZE] = {0};
+
+static void
+add_assembly (intptr_t assembly, char *name)
+{
+ int slot = ((assembly >> 2) & 0xffff) % SMALL_HASH_SIZE;
+ AssemblyDesc *cd = (AssemblyDesc *) g_malloc (sizeof (AssemblyDesc));
+ cd->assembly = assembly;
+ cd->asmname = pstrdup (name);
+ cd->next = assembly_hash [slot];
+ assembly_hash [slot] = cd;
+ num_assemblies++;
+}
+
+typedef struct _BackTrace BackTrace;
+typedef struct {
+ uint64_t count;
+ BackTrace *bt;
+} CallContext;
+
+typedef struct {
+ int count;
+ int size;
+ CallContext *traces;
+} TraceDesc;
+
+typedef struct _ClassDesc ClassDesc;
+struct _ClassDesc {
+ ClassDesc *next;
+ intptr_t klass;
+ char *name;
+ intptr_t allocs;
+ uint64_t alloc_size;
+ TraceDesc traces;
+};
+
+static ClassDesc* class_hash [HASH_SIZE] = {0};
+static int num_classes = 0;
+
+static ClassDesc*
+add_class (intptr_t klass, const char *name)
+{
+ int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
+ ClassDesc *cd;
+ cd = class_hash [slot];
+ while (cd && cd->klass != klass)
+ cd = cd->next;
+ /* we resolved an unknown class (unless we had the code unloaded) */
+ if (cd) {
+ /*printf ("resolved unknown: %s\n", name);*/
+ g_free (cd->name);
+ cd->name = pstrdup (name);
+ return cd;
+ }
+ cd = (ClassDesc *) g_calloc (sizeof (ClassDesc), 1);
+ cd->klass = klass;
+ cd->name = pstrdup (name);
+ cd->next = class_hash [slot];
+ cd->allocs = 0;
+ cd->alloc_size = 0;
+ cd->traces.count = 0;
+ cd->traces.size = 0;
+ cd->traces.traces = NULL;
+ class_hash [slot] = cd;
+ num_classes++;
+ return cd;
+}
+
+static ClassDesc *
+lookup_class (intptr_t klass)
+{
+ int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
+ ClassDesc *cd = class_hash [slot];
+ while (cd && cd->klass != klass)
+ cd = cd->next;
+ if (!cd) {
+ char buf [128];
+ snprintf (buf, sizeof (buf), "unresolved class %p", (void*)klass);
+ return add_class (klass, buf);
+ }
+ return cd;
+}
+
+typedef struct _MethodDesc MethodDesc;
+struct _MethodDesc {
+ MethodDesc *next;
+ intptr_t method;
+ char *name;
+ intptr_t code;
+ int len;
+ int recurse_count;
+ int sample_hits;
+ int ignore_jit; /* when this is set, we collect the metadata but don't count this method fot jit time and code size, when filtering events */
+ uint64_t calls;
+ uint64_t total_time;
+ uint64_t callee_time;
+ uint64_t self_time;
+ TraceDesc traces;
+};
+
+static MethodDesc* method_hash [HASH_SIZE] = {0};
+static int num_methods = 0;
+
+static MethodDesc*
+add_method (intptr_t method, const char *name, intptr_t code, int len)
+{
+ int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
+ MethodDesc *cd;
+ cd = method_hash [slot];
+ while (cd && cd->method != method)
+ cd = cd->next;
+ /* we resolved an unknown method (unless we had the code unloaded) */
+ if (cd) {
+ cd->code = code;
+ cd->len = len;
+ /*printf ("resolved unknown: %s\n", name);*/
+ g_free (cd->name);
+ cd->name = pstrdup (name);
+ return cd;
+ }
+ cd = (MethodDesc *) g_calloc (sizeof (MethodDesc), 1);
+ cd->method = method;
+ cd->name = pstrdup (name);
+ cd->code = code;
+ cd->len = len;
+ cd->calls = 0;
+ cd->total_time = 0;
+ cd->traces.count = 0;
+ cd->traces.size = 0;
+ cd->traces.traces = NULL;
+ cd->next = method_hash [slot];
+ method_hash [slot] = cd;
+ num_methods++;
+ return cd;
+}
+
+static MethodDesc *
+lookup_method (intptr_t method)
+{
+ int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
+ MethodDesc *cd = method_hash [slot];
+ while (cd && cd->method != method)
+ cd = cd->next;
+ if (!cd) {
+ char buf [128];
+ snprintf (buf, sizeof (buf), "unknown method %p", (void*)method);
+ return add_method (method, buf, 0, 0);
+ }
+ return cd;
+}
+
+static int num_stat_samples = 0;
+static int size_stat_samples = 0;
+uintptr_t *stat_samples = NULL;
+int *stat_sample_desc = NULL;
+
+static void
+add_stat_sample (int type, uintptr_t ip) {
+ if (num_stat_samples == size_stat_samples) {
+ size_stat_samples *= 2;
+ if (!size_stat_samples)
+ size_stat_samples = 32;
+ stat_samples = (uintptr_t *) g_realloc (stat_samples, size_stat_samples * sizeof (uintptr_t));
+ stat_sample_desc = (int *) g_realloc (stat_sample_desc, size_stat_samples * sizeof (int));
+ }
+ stat_samples [num_stat_samples] = ip;
+ stat_sample_desc [num_stat_samples++] = type;
+}
+
+static MethodDesc*
+lookup_method_by_ip (uintptr_t ip)
+{
+ int i;
+ MethodDesc* m;
+ /* dumb */
+ for (i = 0; i < HASH_SIZE; ++i) {
+ m = method_hash [i];
+ while (m) {
+ //printf ("checking %p against %p-%p\n", (void*)ip, (void*)(m->code), (void*)(m->code + m->len));
+ if (ip >= (uintptr_t)m->code && ip < (uintptr_t)m->code + m->len) {
+ return m;
+ }
+ m = m->next;
+ }
+ }
+ return NULL;
+}
+
+static int
+compare_method_samples (const void *a, const void *b)
+{
+ MethodDesc *const *A = (MethodDesc *const *)a;
+ MethodDesc *const *B = (MethodDesc *const *)b;
+ if ((*A)->sample_hits == (*B)->sample_hits)
+ return 0;
+ if ((*B)->sample_hits < (*A)->sample_hits)
+ return -1;
+ return 1;
+}
+
+typedef struct _UnmanagedSymbol UnmanagedSymbol;
+struct _UnmanagedSymbol {
+ UnmanagedSymbol *parent;
+ char *name;
+ int is_binary;
+ uintptr_t addr;
+ uintptr_t size;
+ uintptr_t sample_hits;
+};
+
+static UnmanagedSymbol **usymbols = NULL;
+static int usymbols_size = 0;
+static int usymbols_num = 0;
+
+static int
+compare_usymbol_addr (const void *a, const void *b)
+{
+ UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
+ UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
+ if ((*B)->addr == (*A)->addr)
+ return 0;
+ if ((*B)->addr > (*A)->addr)
+ return -1;
+ return 1;
+}
+
+static int
+compare_usymbol_samples (const void *a, const void *b)
+{
+ UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
+ UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
+ if ((*B)->sample_hits == (*A)->sample_hits)
+ return 0;
+ if ((*B)->sample_hits < (*A)->sample_hits)
+ return -1;
+ return 1;
+}
+
+static void
+add_unmanaged_symbol (uintptr_t addr, char *name, uintptr_t size)
+{
+ UnmanagedSymbol *sym;
+ if (usymbols_num == usymbols_size) {
+ int new_size = usymbols_size * 2;
+ if (!new_size)
+ new_size = 16;
+ usymbols = (UnmanagedSymbol **) g_realloc (usymbols, sizeof (void*) * new_size);
+ usymbols_size = new_size;
+ }
+ sym = (UnmanagedSymbol *) g_calloc (sizeof (UnmanagedSymbol), 1);
+ sym->addr = addr;
+ sym->name = name;
+ sym->size = size;
+ usymbols [usymbols_num++] = sym;
+}
+
+/* only valid after the symbols are sorted */
+static UnmanagedSymbol*
+lookup_unmanaged_symbol (uintptr_t addr)
+{
+ int r = usymbols_num - 1;
+ int l = 0;
+ UnmanagedSymbol *sym;
+ int last_best = -1;
+ while (r >= l) {
+ int m = (l + r) / 2;
+ sym = usymbols [m];
+ if (addr == sym->addr)
+ return sym;
+ if (addr < sym->addr) {
+ r = m - 1;
+ } else if (addr > sym->addr) {
+ l = m + 1;
+ last_best = m;
+ }
+ }
+ if (last_best >= 0 && (addr - usymbols [last_best]->addr) < 4096)
+ return usymbols [last_best];
+ return NULL;
+}
+
+/* we use the same structure for binaries */
+static UnmanagedSymbol **ubinaries = NULL;
+static int ubinaries_size = 0;
+static int ubinaries_num = 0;
+
+static void
+add_unmanaged_binary (uintptr_t addr, char *name, uintptr_t size)
+{
+ UnmanagedSymbol *sym;
+ if (ubinaries_num == ubinaries_size) {
+ int new_size = ubinaries_size * 2;
+ if (!new_size)
+ new_size = 16;
+ ubinaries = (UnmanagedSymbol **) g_realloc (ubinaries, sizeof (void*) * new_size);
+ ubinaries_size = new_size;
+ }
+ sym = (UnmanagedSymbol *) g_calloc (sizeof (UnmanagedSymbol), 1);
+ sym->addr = addr;
+ sym->name = name;
+ sym->size = size;
+ sym->is_binary = 1;
+ ubinaries [ubinaries_num++] = sym;
+}
+
+static UnmanagedSymbol*
+lookup_unmanaged_binary (uintptr_t addr)
+{
+ int i;
+ for (i = 0; i < ubinaries_num; ++i) {
+ UnmanagedSymbol *ubin = ubinaries [i];
+ if (addr >= ubin->addr && addr < ubin->addr + ubin->size) {
+ return ubin;
+ }
+ }
+ return NULL;
+}
+
+static const char*
+sample_type_name (int type)
+{
+ switch (type) {
+ case SAMPLE_CYCLES: return "cycles";
+ case SAMPLE_INSTRUCTIONS: return "instructions retired";
+ case SAMPLE_CACHE_MISSES: return "cache misses";
+ case SAMPLE_CACHE_REFS: return "cache references";
+ case SAMPLE_BRANCHES: return "executed branches";
+ case SAMPLE_BRANCH_MISSES: return "unpredicted branches";
+ }
+ return "unknown";
+}
+
+static void
+set_usym_parent (UnmanagedSymbol** cachedus, int count)
+{
+ int i;
+ for (i = 0; i < count; ++i) {
+ UnmanagedSymbol *ubin = lookup_unmanaged_binary (cachedus [i]->addr);
+ if (ubin == cachedus [i])
+ continue;
+ cachedus [i]->parent = ubin;
+ }
+}
+
+static void
+print_usym (UnmanagedSymbol* um)
+{
+ if (um->parent)
+ fprintf (outfile, "\t%6zd %6.2f %-36s in %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name, um->parent->name);
+ else
+ fprintf (outfile, "\t%6zd %6.2f %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name);
+}
+
+static int
+sym_percent (uintptr_t sample_hits)
+{
+ double pc;
+ if (verbose)
+ return 1;
+ pc = sample_hits*100.0/num_stat_samples;
+ return pc >= 0.1;
+}
+
+static void
+dump_samples (void)
+{
+ int i, u;
+ int count = 0, msize = 0;
+ int unmanaged_hits = 0;
+ int unresolved_hits = 0;
+ MethodDesc** cachedm = NULL;
+ int ucount = 0, usize = 0;
+ UnmanagedSymbol** cachedus = NULL;
+ if (!num_stat_samples)
+ return;
+ qsort (usymbols, usymbols_num, sizeof (UnmanagedSymbol*), compare_usymbol_addr);
+ for (i = 0; i < num_stat_samples; ++i) {
+ MethodDesc *m = lookup_method_by_ip (stat_samples [i]);
+ if (m) {
+ if (!m->sample_hits) {
+ if (count == msize) {
+ msize *= 2;
+ if (!msize)
+ msize = 4;
+ cachedm = (MethodDesc **) g_realloc (cachedm, sizeof (void*) * msize);
+ }
+ cachedm [count++] = m;
+ }
+ m->sample_hits++;
+ } else {
+ UnmanagedSymbol *usym = lookup_unmanaged_symbol (stat_samples [i]);
+ if (!usym) {
+ unresolved_hits++;
+ //printf ("unmanaged hit at %p\n", (void*)stat_samples [i]);
+ usym = lookup_unmanaged_binary (stat_samples [i]);
+ }
+ if (usym) {
+ if (!usym->sample_hits) {
+ if (ucount == usize) {
+ usize *= 2;
+ if (!usize)
+ usize = 4;
+ cachedus = (UnmanagedSymbol **) g_realloc (cachedus, sizeof (void*) * usize);
+ }
+ cachedus [ucount++] = usym;
+ }
+ usym->sample_hits++;
+ }
+ unmanaged_hits++;
+ }
+ }
+ qsort (cachedm, count, sizeof (MethodDesc*), compare_method_samples);
+ qsort (cachedus, ucount, sizeof (UnmanagedSymbol*), compare_usymbol_samples);
+ set_usym_parent (cachedus, ucount);
+ fprintf (outfile, "\nStatistical samples summary\n");
+ fprintf (outfile, "\tSample type: %s\n", sample_type_name (stat_sample_desc [0]));
+ fprintf (outfile, "\tUnmanaged hits: %6d (%4.1f%%)\n", unmanaged_hits, (100.0*unmanaged_hits)/num_stat_samples);
+ fprintf (outfile, "\tManaged hits: %6d (%4.1f%%)\n", num_stat_samples - unmanaged_hits, (100.0*(num_stat_samples-unmanaged_hits))/num_stat_samples);
+ fprintf (outfile, "\tUnresolved hits: %6d (%4.1f%%)\n", unresolved_hits, (100.0*unresolved_hits)/num_stat_samples);
+ fprintf (outfile, "\t%6s %6s %s\n", "Hits", "%", "Method name");
+ i = 0;
+ u = 0;
+ while (i < count || u < ucount) {
+ if (i < count) {
+ MethodDesc *m = cachedm [i];
+ if (u < ucount) {
+ UnmanagedSymbol *um = cachedus [u];
+ if (um->sample_hits > m->sample_hits) {
+ if (!sym_percent (um->sample_hits))
+ break;
+ print_usym (um);
+ u++;
+ continue;
+ }
+ }
+ if (!sym_percent (m->sample_hits))
+ break;
+ fprintf (outfile, "\t%6d %6.2f %s\n", m->sample_hits, m->sample_hits*100.0/num_stat_samples, m->name);
+ i++;
+ continue;
+ }
+ if (u < ucount) {
+ UnmanagedSymbol *um = cachedus [u];
+ if (!sym_percent (um->sample_hits))
+ break;
+ print_usym (um);
+ u++;
+ continue;
+ }
+ }
+}
+
+typedef struct _HeapClassDesc HeapClassDesc;
+typedef struct {
+ HeapClassDesc *klass;
+ uint64_t count;
+} HeapClassRevRef;
+
+struct _HeapClassDesc {
+ ClassDesc *klass;
+ int64_t count;
+ int64_t total_size;
+ HeapClassRevRef *rev_hash;
+ int rev_hash_size;
+ int rev_count;
+ uintptr_t pinned_references;
+ uintptr_t root_references;
+};
+
+static int
+add_rev_class_hashed (HeapClassRevRef *rev_hash, uintptr_t size, HeapClassDesc *hklass, uint64_t value)
+{
+ uintptr_t i;
+ uintptr_t start_pos;
+ start_pos = (hklass->klass->klass >> 2) % size;
+ assert (start_pos < size);
+ i = start_pos;
+ do {
+ if (rev_hash [i].klass == hklass) {
+ rev_hash [i].count += value;
+ return 0;
+ } else if (!rev_hash [i].klass) {
+ rev_hash [i].klass = hklass;
+ rev_hash [i].count += value;
+ start_pos = 0;
+ for (i = 0; i < size; ++i)
+ if (rev_hash [i].klass && rev_hash [i].klass->klass == hklass->klass)
+ start_pos ++;
+ assert (start_pos == 1);
+ return 1;
+ }
+ /* wrap around */
+ if (++i == size)
+ i = 0;
+ } while (i != start_pos);
+ /* should not happen */
+ printf ("failed revref store\n");
+ return 0;
+}
+
+static void
+add_heap_class_rev (HeapClassDesc *from, HeapClassDesc *to)
+{
+ uintptr_t i;
+ if (to->rev_count * 2 >= to->rev_hash_size) {
+ HeapClassRevRef *n;
+ uintptr_t old_size = to->rev_hash_size;
+ to->rev_hash_size *= 2;
+ if (to->rev_hash_size == 0)
+ to->rev_hash_size = 4;
+ n = (HeapClassRevRef *) g_calloc (sizeof (HeapClassRevRef) * to->rev_hash_size, 1);
+ for (i = 0; i < old_size; ++i) {
+ if (to->rev_hash [i].klass)
+ add_rev_class_hashed (n, to->rev_hash_size, to->rev_hash [i].klass, to->rev_hash [i].count);
+ }
+ if (to->rev_hash)
+ g_free (to->rev_hash);
+ to->rev_hash = n;
+ }
+ to->rev_count += add_rev_class_hashed (to->rev_hash, to->rev_hash_size, from, 1);
+}
+
+typedef struct {
+ uintptr_t objaddr;
+ HeapClassDesc *hklass;
+ uintptr_t num_refs;
+ uintptr_t refs [0];
+} HeapObjectDesc;
+
+typedef struct _HeapShot HeapShot;
+struct _HeapShot {
+ HeapShot *next;
+ uint64_t timestamp;
+ int class_count;
+ int hash_size;
+ HeapClassDesc **class_hash;
+ HeapClassDesc **sorted;
+ HeapObjectDesc **objects_hash;
+ uintptr_t objects_count;
+ uintptr_t objects_hash_size;
+ uintptr_t num_roots;
+ uintptr_t *roots;
+ uintptr_t *roots_extra;
+ int *roots_types;
+};
+
+static HeapShot *heap_shots = NULL;
+static int num_heap_shots = 0;
+
+static HeapShot*
+new_heap_shot (uint64_t timestamp)
+{
+ HeapShot *hs = (HeapShot *) g_calloc (sizeof (HeapShot), 1);
+ hs->hash_size = 4;
+ hs->class_hash = (HeapClassDesc **) g_calloc (sizeof (void*), hs->hash_size);
+ hs->timestamp = timestamp;
+ num_heap_shots++;
+ hs->next = heap_shots;
+ heap_shots = hs;
+ return hs;
+}
+
+static HeapClassDesc*
+heap_class_lookup (HeapShot *hs, ClassDesc *klass)
+{
+ int i;
+ unsigned int start_pos;
+ start_pos = ((uintptr_t)klass->klass >> 2) % hs->hash_size;
+ i = start_pos;
+ do {
+ HeapClassDesc* cd = hs->class_hash [i];
+ if (!cd)
+ return NULL;
+ if (cd->klass == klass)
+ return cd;
+ /* wrap around */
+ if (++i == hs->hash_size)
+ i = 0;
+ } while (i != start_pos);
+ return NULL;
+}
+
+static int
+add_heap_hashed (HeapClassDesc **hash, HeapClassDesc **retv, uintptr_t hsize, ClassDesc *klass, uint64_t size, uint64_t count)
+{
+ uintptr_t i;
+ uintptr_t start_pos;
+ start_pos = ((uintptr_t)klass->klass >> 2) % hsize;
+ i = start_pos;
+ do {
+ if (hash [i] && hash [i]->klass == klass) {
+ hash [i]->total_size += size;
+ hash [i]->count += count;
+ *retv = hash [i];
+ return 0;
+ } else if (!hash [i]) {
+ if (*retv) {
+ hash [i] = *retv;
+ return 1;
+ }
+ hash [i] = (HeapClassDesc *) g_calloc (sizeof (HeapClassDesc), 1);
+ hash [i]->klass = klass;
+ hash [i]->total_size += size;
+ hash [i]->count += count;
+ *retv = hash [i];
+ return 1;
+ }
+ /* wrap around */
+ if (++i == hsize)
+ i = 0;
+ } while (i != start_pos);
+ /* should not happen */
+ printf ("failed heap class store\n");
+ return 0;
+}
+
+static HeapClassDesc*
+add_heap_shot_class (HeapShot *hs, ClassDesc *klass, uint64_t size)
+{
+ HeapClassDesc *res;
+ int i;
+ if (hs->class_count * 2 >= hs->hash_size) {
+ HeapClassDesc **n;
+ int old_size = hs->hash_size;
+ hs->hash_size *= 2;
+ if (hs->hash_size == 0)
+ hs->hash_size = 4;
+ n = (HeapClassDesc **) g_calloc (sizeof (void*) * hs->hash_size, 1);
+ for (i = 0; i < old_size; ++i) {
+ res = hs->class_hash [i];
+ if (hs->class_hash [i])
+ add_heap_hashed (n, &res, hs->hash_size, hs->class_hash [i]->klass, hs->class_hash [i]->total_size, hs->class_hash [i]->count);
+ }
+ if (hs->class_hash)
+ g_free (hs->class_hash);
+ hs->class_hash = n;
+ }
+ res = NULL;
+ hs->class_count += add_heap_hashed (hs->class_hash, &res, hs->hash_size, klass, size, 1);
+ //if (res->count == 1)
+ // printf ("added heap class: %s\n", res->klass->name);
+ return res;
+}
+
+static HeapObjectDesc*
+alloc_heap_obj (uintptr_t objaddr, HeapClassDesc *hklass, uintptr_t num_refs)
+{
+ HeapObjectDesc* ho = (HeapObjectDesc *) g_calloc (sizeof (HeapObjectDesc) + num_refs * sizeof (uintptr_t), 1);
+ ho->objaddr = objaddr;
+ ho->hklass = hklass;
+ ho->num_refs = num_refs;
+ return ho;
+}
+
+static uintptr_t
+heap_shot_find_obj_slot (HeapShot *hs, uintptr_t objaddr)
+{
+ uintptr_t i;
+ uintptr_t start_pos;
+ HeapObjectDesc **hash = hs->objects_hash;
+ start_pos = ((uintptr_t)objaddr >> 3) % hs->objects_hash_size;
+ i = start_pos;
+ do {
+ if (hash [i] && hash [i]->objaddr == objaddr) {
+ return i;
+ } else if (!hash [i]) {
+ break; /* fail */
+ }
+ /* wrap around */
+ if (++i == hs->objects_hash_size)
+ i = 0;
+ } while (i != start_pos);
+ /* should not happen */
+ //printf ("failed heap obj slot\n");
+ return -1;
+}
+
+static HeapObjectDesc*
+heap_shot_obj_add_refs (HeapShot *hs, uintptr_t objaddr, uintptr_t num, uintptr_t *ref_offset)
+{
+ HeapObjectDesc **hash = hs->objects_hash;
+ uintptr_t i = heap_shot_find_obj_slot (hs, objaddr);
+ if (i >= 0) {
+ HeapObjectDesc* ho = alloc_heap_obj (objaddr, hash [i]->hklass, hash [i]->num_refs + num);
+ *ref_offset = hash [i]->num_refs;
+ memcpy (ho->refs, hash [i]->refs, hash [i]->num_refs * sizeof (uintptr_t));
+ g_free (hash [i]);
+ hash [i] = ho;
+ return ho;
+ }
+ /* should not happen */
+ printf ("failed heap obj update\n");
+ return NULL;
+
+}
+
+static uintptr_t
+add_heap_hashed_obj (HeapObjectDesc **hash, uintptr_t hsize, HeapObjectDesc *obj)
+{
+ uintptr_t i;
+ uintptr_t start_pos;
+ start_pos = ((uintptr_t)obj->objaddr >> 3) % hsize;
+ i = start_pos;
+ do {
+ if (hash [i] && hash [i]->objaddr == obj->objaddr) {
+ printf ("duplicate object!\n");
+ return 0;
+ } else if (!hash [i]) {
+ hash [i] = obj;
+ return 1;
+ }
+ /* wrap around */
+ if (++i == hsize)
+ i = 0;
+ } while (i != start_pos);
+ /* should not happen */
+ printf ("failed heap obj store\n");
+ return 0;
+}
+
+static void
+add_heap_shot_obj (HeapShot *hs, HeapObjectDesc *obj)
+{
+ uintptr_t i;
+ if (hs->objects_count * 2 >= hs->objects_hash_size) {
+ HeapObjectDesc **n;
+ uintptr_t old_size = hs->objects_hash_size;
+ hs->objects_hash_size *= 2;
+ if (hs->objects_hash_size == 0)
+ hs->objects_hash_size = 4;
+ n = (HeapObjectDesc **) g_calloc (sizeof (void*) * hs->objects_hash_size, 1);
+ for (i = 0; i < old_size; ++i) {
+ if (hs->objects_hash [i])
+ add_heap_hashed_obj (n, hs->objects_hash_size, hs->objects_hash [i]);
+ }
+ if (hs->objects_hash)
+ g_free (hs->objects_hash);
+ hs->objects_hash = n;
+ }
+ hs->objects_count += add_heap_hashed_obj (hs->objects_hash, hs->objects_hash_size, obj);
+}
+
+static void
+heap_shot_resolve_reverse_refs (HeapShot *hs)
+{
+ uintptr_t i;
+ for (i = 0; i < hs->objects_hash_size; ++i) {
+ uintptr_t r;
+ HeapObjectDesc *ho = hs->objects_hash [i];
+ if (!ho)
+ continue;
+ for (r = 0; r < ho->num_refs; ++r) {
+ uintptr_t oi = heap_shot_find_obj_slot (hs, ho->refs [r]);
+ add_heap_class_rev (ho->hklass, hs->objects_hash [oi]->hklass);
+ }
+ }
+}
+
+#define MARK_GRAY 1
+#define MARK_BLACK 2
+
+static void
+heap_shot_mark_objects (HeapShot *hs)
+{
+ uintptr_t i, oi, r;
+ unsigned char *marks;
+ HeapObjectDesc *obj, *ref;
+ int marked_some;
+ uintptr_t num_marked = 0, num_unmarked;
+ for (i = 0; i < hs->num_roots; ++i) {
+ HeapClassDesc *cd;
+ oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
+ if (oi == -1) {
+ continue;
+ }
+ obj = hs->objects_hash [oi];
+ cd = obj->hklass;
+ if (hs->roots_types [i] & MONO_PROFILE_GC_ROOT_PINNING)
+ cd->pinned_references++;
+ cd->root_references++;
+ }
+ if (!debug)
+ return;
+ /* consistency checks: it seems not all the objects are walked in the heap in some cases */
+ marks = (unsigned char *) g_calloc (hs->objects_hash_size, 1);
+ if (!marks)
+ return;
+ for (i = 0; i < hs->num_roots; ++i) {
+ oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
+ if (oi == -1) {
+ fprintf (outfile, "root type 0x%x for obj %p (%s) not found in heap\n", hs->roots_types [i], (void*)hs->roots [i], lookup_class (hs->roots_extra [i])->name);
+ continue;
+ }
+ obj = hs->objects_hash [oi];
+ if (!marks [oi]) {
+ marks [oi] = obj->num_refs? MARK_GRAY: MARK_BLACK;
+ num_marked++;
+ }
+ }
+ marked_some = 1;
+ while (marked_some) {
+ marked_some = 0;
+ for (i = 0; i < hs->objects_hash_size; ++i) {
+ if (marks [i] != MARK_GRAY)
+ continue;
+ marks [i] = MARK_BLACK;
+ obj = hs->objects_hash [i];
+ for (r = 0; r < obj->num_refs; ++r) {
+ oi = heap_shot_find_obj_slot (hs, obj->refs [r]);
+ if (oi == -1) {
+ fprintf (outfile, "referenced obj %p not found in heap\n", (void*)obj->refs [r]);
+ continue;
+ }
+ ref = hs->objects_hash [oi];
+ if (!marks [oi]) {
+ marks [oi] = ref->num_refs? MARK_GRAY: MARK_BLACK;
+ }
+ }
+ marked_some++;
+ }
+ }
+
+ num_unmarked = 0;
+ for (i = 0; i < hs->objects_hash_size; ++i) {
+ if (hs->objects_hash [i] && !marks [i]) {
+ num_unmarked++;
+ fprintf (outfile, "object %p (%s) unmarked\n", (void*)hs->objects_hash [i], hs->objects_hash [i]->hklass->klass->name);
+ }
+ }
+ fprintf (outfile, "Total unmarked: %zd/%zd\n", num_unmarked, hs->objects_count);
+ g_free (marks);
+}
+
+static void
+heap_shot_free_objects (HeapShot *hs)
+{
+ uintptr_t i;
+ for (i = 0; i < hs->objects_hash_size; ++i) {
+ HeapObjectDesc *ho = hs->objects_hash [i];
+ if (ho)
+ g_free (ho);
+ }
+ if (hs->objects_hash)
+ g_free (hs->objects_hash);
+ hs->objects_hash = NULL;
+ hs->objects_hash_size = 0;
+ hs->objects_count = 0;
+}
+
+
+struct _BackTrace {
+ BackTrace *next;
+ unsigned int hash;
+ int count;
+ int id;
+ MethodDesc *methods [1];
+};
+
+static BackTrace *backtrace_hash [HASH_SIZE];
+static BackTrace **backtraces = NULL;
+static int num_backtraces = 0;
+static int next_backtrace = 0;
+
+static int
+hash_backtrace (int count, MethodDesc **methods)
+{
+ int hash = count;
+ int i;
+ for (i = 0; i < count; ++i) {
+ hash = (hash << 5) - hash + methods [i]->method;
+ }
+ return hash;
+}
+
+static int
+compare_backtrace (BackTrace *bt, int count, MethodDesc **methods)
+{
+ int i;
+ if (bt->count != count)
+ return 0;
+ for (i = 0; i < count; ++i)
+ if (methods [i] != bt->methods [i])
+ return 0;
+ return 1;
+}
+
+static BackTrace*
+add_backtrace (int count, MethodDesc **methods)
+{
+ int hash = hash_backtrace (count, methods);
+ int slot = (hash & 0xffff) % HASH_SIZE;
+ BackTrace *bt = backtrace_hash [slot];
+ while (bt) {
+ if (bt->hash == hash && compare_backtrace (bt, count, methods))
+ return bt;
+ bt = bt->next;
+ }
+ bt = (BackTrace *) g_malloc (sizeof (BackTrace) + ((count - 1) * sizeof (void*)));
+ bt->next = backtrace_hash [slot];
+ backtrace_hash [slot] = bt;
+ if (next_backtrace == num_backtraces) {
+ num_backtraces *= 2;
+ if (!num_backtraces)
+ num_backtraces = 16;
+ backtraces = (BackTrace **) g_realloc (backtraces, sizeof (void*) * num_backtraces);
+ }
+ bt->id = next_backtrace++;
+ backtraces [bt->id] = bt;
+ bt->count = count;
+ bt->hash = hash;
+ for (slot = 0; slot < count; ++slot)
+ bt->methods [slot] = methods [slot];
+
+ return bt;
+}
+
+typedef struct _MonitorDesc MonitorDesc;
+typedef struct _ThreadContext ThreadContext;
+typedef struct _DomainContext DomainContext;
+typedef struct _RemCtxContext RemCtxContext;
+
+typedef struct {
+ FILE *file;
+#if defined (HAVE_SYS_ZLIB)
+ gzFile gzfile;
+#endif
+ unsigned char *buf;
+ int size;
+ int data_version;
+ int version_major;
+ int version_minor;
+ int timer_overhead;
+ int pid;
+ int port;
+ char *args;
+ char *arch;
+ char *os;
+ uint64_t startup_time;
+ ThreadContext *threads;
+ ThreadContext *current_thread;
+ DomainContext *domains;
+ DomainContext *current_domain;
+ RemCtxContext *remctxs;
+ RemCtxContext *current_remctx;
+} ProfContext;
+
+struct _ThreadContext {
+ ThreadContext *next;
+ intptr_t thread_id;
+ char *name;
+ /* emulated stack */
+ MethodDesc **stack;
+ uint64_t *time_stack;
+ uint64_t *callee_time_stack;
+ uint64_t last_time;
+ uint64_t contention_start;
+ MonitorDesc *monitor;
+ int stack_size;
+ int stack_id;
+ HeapShot *current_heap_shot;
+ uintptr_t num_roots;
+ uintptr_t size_roots;
+ uintptr_t *roots;
+ uintptr_t *roots_extra;
+ int *roots_types;
+ uint64_t gc_start_times [3];
+};
+
+struct _DomainContext {
+ DomainContext *next;
+ intptr_t domain_id;
+ const char *friendly_name;
+};
+
+struct _RemCtxContext {
+ RemCtxContext *next;
+ intptr_t remctx_id;
+ intptr_t domain_id;
+};
+
+static void
+ensure_buffer (ProfContext *ctx, int size)
+{
+ if (ctx->size < size) {
+ ctx->buf = (unsigned char *) g_realloc (ctx->buf, size);
+ ctx->size = size;
+ }
+}
+
+static int
+load_data (ProfContext *ctx, int size)
+{
+ ensure_buffer (ctx, size);
+#if defined (HAVE_SYS_ZLIB)
+ if (ctx->gzfile) {
+ int r = gzread (ctx->gzfile, ctx->buf, size);
+ if (r == 0)
+ return size == 0? 1: 0;
+ return r == size;
+ } else
+#endif
+ {
+ int r = fread (ctx->buf, size, 1, ctx->file);
+ if (r == 0)
+ return size == 0? 1: 0;
+ return r;
+ }
+}
+
+static ThreadContext*
+get_thread (ProfContext *ctx, intptr_t thread_id)
+{
+ ThreadContext *thread;
+ if (ctx->current_thread && ctx->current_thread->thread_id == thread_id)
+ return ctx->current_thread;
+ thread = ctx->threads;
+ while (thread) {
+ if (thread->thread_id == thread_id) {
+ return thread;
+ }
+ thread = thread->next;
+ }
+ thread = (ThreadContext *) g_calloc (sizeof (ThreadContext), 1);
+ thread->next = ctx->threads;
+ ctx->threads = thread;
+ thread->thread_id = thread_id;
+ thread->last_time = 0;
+ thread->stack_id = 0;
+ thread->stack_size = 32;
+ thread->stack = (MethodDesc **) g_malloc (thread->stack_size * sizeof (void*));
+ thread->time_stack = (uint64_t *) g_malloc (thread->stack_size * sizeof (uint64_t));
+ thread->callee_time_stack = (uint64_t *) g_malloc (thread->stack_size * sizeof (uint64_t));
+ return thread;
+}
+
+static DomainContext *
+get_domain (ProfContext *ctx, intptr_t domain_id)
+{
+ if (ctx->current_domain && ctx->current_domain->domain_id == domain_id)
+ return ctx->current_domain;
+
+ DomainContext *domain = ctx->domains;
+
+ while (domain) {
+ if (domain->domain_id == domain_id)
+ return domain;
+
+ domain = domain->next;
+ }
+
+ domain = (DomainContext *) g_calloc (sizeof (DomainContext), 1);
+ domain->next = ctx->domains;
+ ctx->domains = domain;
+ domain->domain_id = domain_id;
+
+ return domain;
+}
+
+static RemCtxContext *
+get_remctx (ProfContext *ctx, intptr_t remctx_id)
+{
+ if (ctx->current_remctx && ctx->current_remctx->remctx_id == remctx_id)
+ return ctx->current_remctx;
+
+ RemCtxContext *remctx = ctx->remctxs;
+
+ while (remctx) {
+ if (remctx->remctx_id == remctx_id)
+ return remctx;
+
+ remctx = remctx->next;
+ }
+
+ remctx = (RemCtxContext *) g_calloc (sizeof (RemCtxContext), 1);
+ remctx->next = ctx->remctxs;
+ ctx->remctxs = remctx;
+ remctx->remctx_id = remctx_id;
+
+ return remctx;
+}
+
+static ThreadContext*
+load_thread (ProfContext *ctx, intptr_t thread_id)
+{
+ ThreadContext *thread = get_thread (ctx, thread_id);
+ ctx->current_thread = thread;
+ return thread;
+}
+
+static void
+ensure_thread_stack (ThreadContext *thread)
+{
+ if (thread->stack_id == thread->stack_size) {
+ thread->stack_size *= 2;
+ thread->stack = (MethodDesc **) g_realloc (thread->stack, thread->stack_size * sizeof (void*));
+ thread->time_stack = (uint64_t *) g_realloc (thread->time_stack, thread->stack_size * sizeof (uint64_t));
+ thread->callee_time_stack = (uint64_t *) g_realloc (thread->callee_time_stack, thread->stack_size * sizeof (uint64_t));
+ }
+}
+
+static int
+add_trace_hashed (CallContext *traces, int size, BackTrace *bt, uint64_t value)
+{
+ int i;
+ unsigned int start_pos;
+ start_pos = bt->hash % size;
+ i = start_pos;
+ do {
+ if (traces [i].bt == bt) {
+ traces [i].count += value;
+ return 0;
+ } else if (!traces [i].bt) {
+ traces [i].bt = bt;
+ traces [i].count += value;
+ return 1;
+ }
+ /* wrap around */
+ if (++i == size)
+ i = 0;
+ } while (i != start_pos);
+ /* should not happen */
+ printf ("failed trace store\n");
+ return 0;
+}
+
+static void
+add_trace_bt (BackTrace *bt, TraceDesc *trace, uint64_t value)
+{
+ int i;
+ if (!collect_traces)
+ return;
+ if (trace->count * 2 >= trace->size) {
+ CallContext *n;
+ int old_size = trace->size;
+ trace->size *= 2;
+ if (trace->size == 0)
+ trace->size = 4;
+ n = (CallContext *) g_calloc (sizeof (CallContext) * trace->size, 1);
+ for (i = 0; i < old_size; ++i) {
+ if (trace->traces [i].bt)
+ add_trace_hashed (n, trace->size, trace->traces [i].bt, trace->traces [i].count);
+ }
+ if (trace->traces)
+ g_free (trace->traces);
+ trace->traces = n;
+ }
+ trace->count += add_trace_hashed (trace->traces, trace->size, bt, value);
+}
+
+static BackTrace*
+add_trace_thread (ThreadContext *thread, TraceDesc *trace, uint64_t value)
+{
+ BackTrace *bt;
+ int count = thread->stack_id;
+ if (!collect_traces)
+ return NULL;
+ if (count > trace_max)
+ count = trace_max;
+ bt = add_backtrace (count, thread->stack + thread->stack_id - count);
+ add_trace_bt (bt, trace, value);
+ return bt;
+}
+
+static BackTrace*
+add_trace_methods (MethodDesc **methods, int count, TraceDesc *trace, uint64_t value)
+{
+ BackTrace *bt;
+ if (!collect_traces)
+ return NULL;
+ if (count > trace_max)
+ count = trace_max;
+ bt = add_backtrace (count, methods);
+ add_trace_bt (bt, trace, value);
+ return bt;
+}
+
+static void
+thread_add_root (ThreadContext *ctx, uintptr_t obj, int root_type, uintptr_t extra_info)
+{
+ if (ctx->num_roots == ctx->size_roots) {
+ int new_size = ctx->size_roots * 2;
+ if (!new_size)
+ new_size = 4;
+ ctx->roots = (uintptr_t *) g_realloc (ctx->roots, new_size * sizeof (uintptr_t));
+ ctx->roots_extra = (uintptr_t *) g_realloc (ctx->roots_extra, new_size * sizeof (uintptr_t));
+ ctx->roots_types = (int *) g_realloc (ctx->roots_types, new_size * sizeof (int));
+ ctx->size_roots = new_size;
+ }
+ ctx->roots_types [ctx->num_roots] = root_type;
+ ctx->roots_extra [ctx->num_roots] = extra_info;
+ ctx->roots [ctx->num_roots++] = obj;
+}
+
+static int
+compare_callc (const void *a, const void *b)
+{
+ const CallContext *A = (const CallContext *)a;
+ const CallContext *B = (const CallContext *)b;
+ if (B->count == A->count)
+ return 0;
+ if (B->count < A->count)
+ return -1;
+ return 1;
+}
+
+static void
+sort_context_array (TraceDesc* traces)
+{
+ int i, j;
+ for (i = 0, j = 0; i < traces->size; ++i) {
+ if (traces->traces [i].bt) {
+ traces->traces [j].bt = traces->traces [i].bt;
+ traces->traces [j].count = traces->traces [i].count;
+ j++;
+ }
+ }
+ qsort (traces->traces, traces->count, sizeof (CallContext), compare_callc);
+}
+
+static void
+push_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
+{
+ ensure_thread_stack (thread);
+ thread->time_stack [thread->stack_id] = timestamp;
+ thread->callee_time_stack [thread->stack_id] = 0;
+ thread->stack [thread->stack_id++] = method;
+ method->recurse_count++;
+}
+
+static void
+pop_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
+{
+ method->recurse_count--;
+ if (thread->stack_id > 0 && thread->stack [thread->stack_id - 1] == method) {
+ uint64_t tdiff;
+ thread->stack_id--;
+ method->calls++;
+ if (timestamp < thread->time_stack [thread->stack_id])
+ fprintf (outfile, "time went backwards for %s\n", method->name);
+ tdiff = timestamp - thread->time_stack [thread->stack_id];
+ if (thread->callee_time_stack [thread->stack_id] > tdiff)
+ fprintf (outfile, "callee time bigger for %s\n", method->name);
+ method->self_time += tdiff - thread->callee_time_stack [thread->stack_id];
+ method->callee_time += thread->callee_time_stack [thread->stack_id];
+ if (thread->stack_id)
+ thread->callee_time_stack [thread->stack_id - 1] += tdiff;
+ //fprintf (outfile, "method %s took %d\n", method->name, (int)(tdiff/1000));
+ } else {
+ fprintf (outfile, "unmatched leave at stack pos: %d for method %s\n", thread->stack_id, method->name);
+ }
+}
+
+typedef struct {
+ uint64_t total_time;
+ uint64_t max_time;
+ int count;
+} GCDesc;
+static GCDesc gc_info [3];
+static uint64_t max_heap_size;
+static uint64_t gc_object_moves;
+static int gc_resizes;
+typedef struct {
+ uint64_t created;
+ uint64_t destroyed;
+ uint64_t live;
+ uint64_t max_live;
+ TraceDesc traces;
+ TraceDesc destroy_traces;
+} HandleInfo;
+static HandleInfo handle_info [4];
+
+static const char*
+gc_event_name (int ev)
+{
+ switch (ev) {
+ case MONO_GC_EVENT_START: return "start";
+ case MONO_GC_EVENT_MARK_START: return "mark start";
+ case MONO_GC_EVENT_MARK_END: return "mark end";
+ case MONO_GC_EVENT_RECLAIM_START: return "reclaim start";
+ 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 uint64_t clause_summary [MONO_EXCEPTION_CLAUSE_FAULT + 1];
+static uint64_t throw_count = 0;
+static TraceDesc exc_traces;
+
+static const char*
+clause_name (int type)
+{
+ switch (type) {
+ case MONO_EXCEPTION_CLAUSE_NONE: return "catch";
+ case MONO_EXCEPTION_CLAUSE_FILTER: return "filter";
+ case MONO_EXCEPTION_CLAUSE_FINALLY: return "finally";
+ case MONO_EXCEPTION_CLAUSE_FAULT: return "fault";
+ default: return "invalid";
+ }
+}
+
+static uint64_t monitor_contention;
+static uint64_t monitor_failed;
+static uint64_t monitor_acquired;
+
+struct _MonitorDesc {
+ MonitorDesc *next;
+ uintptr_t objid;
+ uintptr_t contentions;
+ uint64_t wait_time;
+ uint64_t max_wait_time;
+ TraceDesc traces;
+};
+
+static MonitorDesc* monitor_hash [SMALL_HASH_SIZE] = {0};
+static int num_monitors = 0;
+
+static MonitorDesc*
+lookup_monitor (uintptr_t objid)
+{
+ int slot = ((objid >> 3) & 0xffff) % SMALL_HASH_SIZE;
+ MonitorDesc *cd = monitor_hash [slot];
+ while (cd && cd->objid != objid)
+ cd = cd->next;
+ if (!cd) {
+ cd = (MonitorDesc *) g_calloc (sizeof (MonitorDesc), 1);
+ cd->objid = objid;
+ cd->next = monitor_hash [slot];
+ monitor_hash [slot] = cd;
+ num_monitors++;
+ }
+ return cd;
+}
+
+static const char*
+monitor_ev_name (int ev)
+{
+ switch (ev) {
+ case MONO_PROFILER_MONITOR_CONTENTION: return "contended";
+ case MONO_PROFILER_MONITOR_DONE: return "acquired";
+ case MONO_PROFILER_MONITOR_FAIL: return "not taken";
+ default: return "invalid";
+ }
+}
+
+static const char*
+get_handle_name (int htype)
+{
+ switch (htype) {
+ case 0: return "weak";
+ case 1: return "weaktrack";
+ case 2: return "normal";
+ case 3: return "pinned";
+ default: return "unknown";
+ }
+}
+
+static const char*
+get_root_name (int rtype)
+{
+ switch (rtype & MONO_PROFILE_GC_ROOT_TYPEMASK) {
+ case MONO_PROFILE_GC_ROOT_STACK: return "stack";
+ case MONO_PROFILE_GC_ROOT_FINALIZER: return "finalizer";
+ case MONO_PROFILE_GC_ROOT_HANDLE: return "handle";
+ case MONO_PROFILE_GC_ROOT_OTHER: return "other";
+ case MONO_PROFILE_GC_ROOT_MISC: return "misc";
+ default: return "unknown";
+ }
+}
+
+static uint64_t
+decode_uleb128 (uint8_t *buf, uint8_t **endbuf)
+{
+ uint64_t res = 0;
+ int shift = 0;
+
+ while (1) {
+ uint8_t b = *buf++;
+ res |= (((uint64_t) (b & 0x7f)) << shift);
+
+ if (!(b & 0x80))
+ break;
+
+ shift += 7;
+ }
+
+ *endbuf = buf;
+
+ return res;
+}
+
+static intptr_t
+decode_sleb128 (uint8_t *buf, uint8_t **endbuf)
+{
+ uint8_t *p = buf;
+ intptr_t res = 0;
+ int shift = 0;
+
+ while (1) {
+ uint8_t b = *p;
+ p++;
+
+ res = res | (((intptr_t) (b & 0x7f)) << shift);
+ shift += 7;
+
+ if (!(b & 0x80)) {
+ if (shift < sizeof (intptr_t) * 8 && (b & 0x40))
+ res |= - ((intptr_t) 1 << shift);
+
+ break;
+ }
+ }
+
+ *endbuf = p;
+
+ return res;
+}
+
+static MethodDesc**
+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;
+ if (ctx->data_version < 13)
+ decode_uleb128 (p, &p); /* flags */
+ int count = decode_uleb128 (p, &p);
+ if (count > *size)
+ frames = (MethodDesc **) g_malloc (count * sizeof (void*));
+ else
+ frames = sframes;
+ for (i = 0; i < count; ++i) {
+ intptr_t ptrdiff = decode_sleb128 (p, &p);
+ 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;
+ return frames;
+}
+
+static void
+tracked_creation (uintptr_t obj, ClassDesc *cd, uint64_t size, BackTrace *bt, uint64_t timestamp)
+{
+ int i;
+ for (i = 0; i < num_tracked_objects; ++i) {
+ if (tracked_objects [i] != obj)
+ continue;
+ fprintf (outfile, "Object %p created (%s, %llu bytes) at %.3f secs.\n", (void*)obj, cd->name, (unsigned long long) size, (timestamp - startup_time)/1000000000.0);
+ if (bt && bt->count) {
+ int k;
+ for (k = 0; k < bt->count; ++k)
+ fprintf (outfile, "\t%s\n", bt->methods [k]->name);
+ }
+ }
+}
+
+static void
+track_handle (uintptr_t obj, int htype, uint32_t handle, BackTrace *bt, uint64_t timestamp)
+{
+ int i;
+ for (i = 0; i < num_tracked_objects; ++i) {
+ if (tracked_objects [i] != obj)
+ continue;
+ fprintf (outfile, "Object %p referenced from handle %u at %.3f secs.\n", (void*)obj, handle, (timestamp - startup_time) / 1000000000.0);
+ if (bt && bt->count) {
+ int k;
+ for (k = 0; k < bt->count; ++k)
+ fprintf (outfile, "\t%s\n", bt->methods [k]->name);
+ }
+ }
+}
+
+static void
+track_move (uintptr_t src, uintptr_t dst)
+{
+ int i;
+ for (i = 0; i < num_tracked_objects; ++i) {
+ if (tracked_objects [i] == src)
+ fprintf (outfile, "Object %p moved to %p\n", (void*)src, (void*)dst);
+ else if (tracked_objects [i] == dst)
+ fprintf (outfile, "Object %p moved from %p\n", (void*)dst, (void*)src);
+ }
+}
+
+static void
+track_obj_reference (uintptr_t obj, uintptr_t parent, ClassDesc *cd)
+{
+ int i;
+ for (i = 0; i < num_tracked_objects; ++i) {
+ if (tracked_objects [i] == obj)
+ fprintf (outfile, "Object %p referenced from %p (%s).\n", (void*)obj, (void*)parent, cd->name);
+ }
+}
+
+static void
+found_object (uintptr_t obj)
+{
+ num_tracked_objects ++;
+ tracked_objects = (uintptr_t *) g_realloc (tracked_objects, num_tracked_objects * sizeof (tracked_objects [0]));
+ tracked_objects [num_tracked_objects - 1] = obj;
+}
+
+static int num_jit_helpers = 0;
+static int jit_helpers_code_size = 0;
+
+static const char*
+code_buffer_desc (int type)
+{
+ switch (type) {
+ case MONO_PROFILER_CODE_BUFFER_METHOD:
+ return "method";
+ case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE:
+ return "method trampoline";
+ case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE:
+ return "unbox trampoline";
+ case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE:
+ return "imt trampoline";
+ case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE:
+ return "generics trampoline";
+ case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE:
+ return "specific trampoline";
+ case MONO_PROFILER_CODE_BUFFER_HELPER:
+ return "misc helper";
+ case MONO_PROFILER_CODE_BUFFER_MONITOR:
+ return "monitor/lock";
+ case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE:
+ return "delegate invoke";
+ case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING:
+ return "exception handling";
+ default:
+ return "unspecified";
+ }
+}
+
+typedef struct _CoverageAssembly CoverageAssembly;
+struct _CoverageAssembly {
+ char *name;
+ char *guid;
+ char *filename;
+ int number_of_methods;
+ int fully_covered;
+ int partially_covered;
+};
+
+typedef struct _CoverageClass CoverageClass;
+struct _CoverageClass {
+ char *assembly_name;
+ char *class_name;
+ int number_of_methods;
+ int fully_covered;
+ int partially_covered;
+};
+
+typedef struct _CoverageCoverage CoverageCoverage;
+struct _CoverageCoverage {
+ int method_id;
+ int offset;
+ int count;
+ int line;
+ int column;
+};
+
+typedef struct _CoverageMethod CoverageMethod;
+struct _CoverageMethod {
+ char *assembly_name;
+ char *class_name;
+ char *method_name;
+ char *method_signature;
+ char *filename;
+ int token;
+ int n_statements;
+ int method_id;
+ GPtrArray *coverage;
+};
+static GPtrArray *coverage_assemblies = NULL;
+static GPtrArray *coverage_methods = NULL;
+static GPtrArray *coverage_statements = NULL;
+static GHashTable *coverage_methods_hash = NULL;
+static GPtrArray *coverage_classes = NULL;
+static GHashTable *coverage_assembly_classes = NULL;
+
+static void
+gather_coverage_statements (void)
+{
+ for (guint i = 0; i < coverage_statements->len; i++) {
+ CoverageCoverage *coverage = (CoverageCoverage *)coverage_statements->pdata[i];
+ CoverageMethod *method = (CoverageMethod *)g_hash_table_lookup (coverage_methods_hash, GINT_TO_POINTER (coverage->method_id));
+ if (method == NULL) {
+ fprintf (outfile, "Cannot find method with ID: %d\n", coverage->method_id);
+ continue;
+ }
+
+ g_ptr_array_add (method->coverage, coverage);
+ }
+}
+
+static void
+coverage_add_assembly (CoverageAssembly *assembly)
+{
+ if (coverage_assemblies == NULL)
+ coverage_assemblies = g_ptr_array_new ();
+
+ g_ptr_array_add (coverage_assemblies, assembly);
+}
+
+static void
+coverage_add_method (CoverageMethod *method)
+{
+ if (coverage_methods == NULL) {
+ coverage_methods = g_ptr_array_new ();
+ coverage_methods_hash = g_hash_table_new (NULL, NULL);
+ }
+
+ g_ptr_array_add (coverage_methods, method);
+ g_hash_table_insert (coverage_methods_hash, GINT_TO_POINTER (method->method_id), method);
+}
+
+static void
+coverage_add_class (CoverageClass *klass)
+{
+ GPtrArray *classes = NULL;
+
+ if (coverage_classes == NULL) {
+ coverage_classes = g_ptr_array_new ();
+ coverage_assembly_classes = g_hash_table_new (g_str_hash, g_str_equal);
+ }
+
+ g_ptr_array_add (coverage_classes, klass);
+ classes = (GPtrArray *)g_hash_table_lookup (coverage_assembly_classes, klass->assembly_name);
+ if (classes == NULL) {
+ classes = g_ptr_array_new ();
+ g_hash_table_insert (coverage_assembly_classes, klass->assembly_name, classes);
+ }
+ g_ptr_array_add (classes, klass);
+}
+
+static void
+coverage_add_coverage (CoverageCoverage *coverage)
+{
+ if (coverage_statements == NULL)
+ coverage_statements = g_ptr_array_new ();
+
+ g_ptr_array_add (coverage_statements, coverage);
+}
+
+#define OBJ_ADDR(diff) ((obj_base + diff) << 3)
+#define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
+
+
+/* Stats */
+#define BUFFER_HEADER_SIZE 48
+
+typedef struct {
+ int count, min_size, max_size, bytes;
+} EventStat;
+
+static int buffer_count;
+static EventStat stats [256];
+
+static void
+record_event_stats (int type, int size)
+{
+ ++stats [type].count;
+ if (!stats [type].min_size)
+ stats [type].min_size = size;
+ stats [type].min_size = MIN (stats [type].min_size, size);
+ stats [type].max_size = MAX (stats [type].max_size, size);
+ stats [type].bytes += size;
+}
+
+static int
+decode_buffer (ProfContext *ctx)
+{
+ unsigned char *p;
+ unsigned char *end;
+ intptr_t thread_id;
+ intptr_t ptr_base;
+ intptr_t obj_base;
+ intptr_t method_base;
+ uint64_t time_base;
+ uint64_t file_offset;
+ int len, i;
+ ThreadContext *thread;
+
+#ifdef HAVE_SYS_ZLIB
+ if (ctx->gzfile)
+ file_offset = gztell (ctx->gzfile);
+ else
+#endif
+ file_offset = ftell (ctx->file);
+ if (!load_data (ctx, 48))
+ return 0;
+ p = ctx->buf;
+ if (read_int32 (p) != BUF_ID) {
+ fprintf (outfile, "Incorrect buffer id: 0x%x\n", read_int32 (p));
+ for (i = 0; i < 48; ++i) {
+ fprintf (outfile, "0x%x%s", p [i], i % 8?" ":"\n");
+ }
+ return 0;
+ }
+ len = read_int32 (p + 4);
+ time_base = read_int64 (p + 8);
+ ptr_base = read_int64 (p + 16);
+ obj_base = read_int64 (p + 24);
+ thread_id = read_int64 (p + 32);
+ method_base = read_int64 (p + 40);
+ if (debug)
+ fprintf (outfile, "buf: thread:%zx, len: %d, time: %llu, file offset: %llu\n", thread_id, len, (unsigned long long) time_base, (unsigned long long) file_offset);
+ thread = load_thread (ctx, thread_id);
+ if (!load_data (ctx, len))
+ return 0;
+
+ ++buffer_count;
+
+ if (!startup_time) {
+ startup_time = time_base;
+ if (use_time_filter) {
+ time_from += startup_time;
+ time_to += startup_time;
+ }
+ }
+ for (i = 0; i < thread->stack_id; ++i)
+ thread->stack [i]->recurse_count++;
+ p = ctx->buf;
+ end = p + len;
+ while (p < end) {
+ unsigned char *start = p;
+ unsigned char event = *p;
+ switch (*p & 0xf) {
+ case TYPE_GC: {
+ int subtype = *p & 0xf0;
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ if (subtype == TYPE_GC_RESIZE) {
+ uint64_t new_size = decode_uleb128 (p, &p);
+ if (debug)
+ fprintf (outfile, "gc heap resized to %llu\n", (unsigned long long) new_size);
+ gc_resizes++;
+ if (new_size > max_heap_size)
+ max_heap_size = new_size;
+ } else if (subtype == TYPE_GC_EVENT) {
+ 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) {
+ fprintf (outfile, "incorrect gc gen: %d\n", gen);
+ break;
+ }
+ if (ev == MONO_GC_EVENT_START) {
+ thread->gc_start_times [gen] = time_base;
+ gc_info [gen].count++;
+ } else if (ev == MONO_GC_EVENT_END) {
+ tdiff = time_base - thread->gc_start_times [gen];
+ gc_info [gen].total_time += tdiff;
+ if (tdiff > gc_info [gen].max_time)
+ gc_info [gen].max_time = tdiff;
+ }
+ } else if (subtype == TYPE_GC_MOVE) {
+ int j, num = decode_uleb128 (p, &p);
+ gc_object_moves += num / 2;
+ for (j = 0; j < num; j += 2) {
+ intptr_t obj1diff = decode_sleb128 (p, &p);
+ intptr_t obj2diff = decode_sleb128 (p, &p);
+ if (num_tracked_objects)
+ track_move (OBJ_ADDR (obj1diff), OBJ_ADDR (obj2diff));
+ if (debug) {
+ fprintf (outfile, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff), (void*)OBJ_ADDR (obj2diff));
+ }
+ }
+ } else if (subtype == TYPE_GC_HANDLE_CREATED || subtype == TYPE_GC_HANDLE_CREATED_BT) {
+ int has_bt = subtype == TYPE_GC_HANDLE_CREATED_BT;
+ int num_bt = 0;
+ MethodDesc *sframes [8];
+ MethodDesc **frames = sframes;
+ int htype = decode_uleb128 (p, &p);
+ uint32_t handle = decode_uleb128 (p, &p);
+ intptr_t objdiff = decode_sleb128 (p, &p);
+ if (has_bt) {
+ num_bt = 8;
+ frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
+ if (!frames) {
+ fprintf (outfile, "Cannot load backtrace\n");
+ return 0;
+ }
+ }
+ if (htype > 3)
+ return 0;
+ if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
+ handle_info [htype].created++;
+ handle_info [htype].live++;
+ if (handle_info [htype].live > handle_info [htype].max_live)
+ handle_info [htype].max_live = handle_info [htype].live;
+ BackTrace *bt;
+ if (has_bt)
+ bt = add_trace_methods (frames, num_bt, &handle_info [htype].traces, 1);
+ else
+ bt = add_trace_thread (thread, &handle_info [htype].traces, 1);
+ if (num_tracked_objects)
+ track_handle (OBJ_ADDR (objdiff), htype, handle, bt, time_base);
+ }
+ if (debug)
+ fprintf (outfile, "handle (%s) %u created for object %p\n", get_handle_name (htype), handle, (void*)OBJ_ADDR (objdiff));
+ if (frames != sframes)
+ g_free (frames);
+ } else if (subtype == TYPE_GC_HANDLE_DESTROYED || subtype == TYPE_GC_HANDLE_DESTROYED_BT) {
+ int has_bt = subtype == TYPE_GC_HANDLE_DESTROYED_BT;
+ int num_bt = 0;
+ MethodDesc *sframes [8];
+ MethodDesc **frames = sframes;
+ int htype = decode_uleb128 (p, &p);
+ uint32_t handle = decode_uleb128 (p, &p);
+ if (has_bt) {
+ num_bt = 8;
+ frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
+ if (!frames) {
+ fprintf (outfile, "Cannot load backtrace\n");
+ return 0;
+ }
+ }
+ if (htype > 3)
+ return 0;
+ if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
+ handle_info [htype].destroyed ++;
+ handle_info [htype].live--;
+ BackTrace *bt;
+ if (has_bt)
+ bt = add_trace_methods (frames, num_bt, &handle_info [htype].destroy_traces, 1);
+ else
+ bt = add_trace_thread (thread, &handle_info [htype].destroy_traces, 1);
+ /* TODO: track_handle_free () - would need to record and keep track of the associated object address... */
+ }
+ if (debug)
+ fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
+ if (frames != sframes)
+ g_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;
+ }
+ case TYPE_METADATA: {
+ int subtype = *p & 0xf0;
+ const char *load_str = subtype == TYPE_END_LOAD ? "loaded" : "unloaded";
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ int mtype = *p++;
+ intptr_t ptrdiff = decode_sleb128 (p, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ if (mtype == TYPE_CLASS) {
+ intptr_t imptrdiff = decode_sleb128 (p, &p);
+ 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)
+ add_class (ptr_base + ptrdiff, (char*)p);
+ while (*p) p++;
+ p++;
+ } else if (mtype == TYPE_IMAGE) {
+ 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)
+ add_image (ptr_base + ptrdiff, (char*)p);
+ while (*p) p++;
+ p++;
+ } else if (mtype == TYPE_ASSEMBLY) {
+ 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)
+ add_assembly (ptr_base + ptrdiff, (char*)p);
+ while (*p) p++;
+ p++;
+ } else if (mtype == TYPE_DOMAIN) {
+ 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)
+ nd->friendly_name = pstrdup ((char *) p);
+ if (debug) {
+ if (subtype == 0)
+ fprintf (outfile, "domain %p named at %llu: %s\n", (void *) (ptr_base + ptrdiff), (unsigned long long) time_base, p);
+ else
+ fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
+ }
+ if (subtype == 0) {
+ while (*p) p++;
+ p++;
+ }
+ } else if (mtype == TYPE_CONTEXT) {
+ 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) {
+ 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)
+ nt->name = pstrdup ((char*)p);
+ if (debug) {
+ if (subtype == 0)
+ fprintf (outfile, "thread %p named at %llu: %s\n", (void*)(ptr_base + ptrdiff), (unsigned long long) time_base, p);
+ else
+ fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
+ }
+ if (subtype == 0) {
+ while (*p) p++;
+ p++;
+ }
+ }
+ break;
+ }
+ case TYPE_ALLOC: {
+ int has_bt = *p & TYPE_ALLOC_BT;
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ intptr_t ptrdiff = decode_sleb128 (p, &p);
+ intptr_t objdiff = decode_sleb128 (p, &p);
+ uint64_t len;
+ int num_bt = 0;
+ MethodDesc* sframes [8];
+ MethodDesc** frames = sframes;
+ ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
+ len = decode_uleb128 (p, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ if (debug)
+ 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 (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
+ if (!frames) {
+ fprintf (outfile, "Cannot load backtrace\n");
+ return 0;
+ }
+ }
+ if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
+ BackTrace *bt;
+ cd->allocs++;
+ cd->alloc_size += len;
+ if (has_bt)
+ bt = add_trace_methods (frames, num_bt, &cd->traces, len);
+ else
+ bt = add_trace_thread (thread, &cd->traces, len);
+ if (find_size && len >= find_size) {
+ if (!find_name || strstr (cd->name, find_name))
+ found_object (OBJ_ADDR (objdiff));
+ } else if (!find_size && find_name && strstr (cd->name, find_name)) {
+ found_object (OBJ_ADDR (objdiff));
+ }
+ if (num_tracked_objects)
+ tracked_creation (OBJ_ADDR (objdiff), cd, len, bt, time_base);
+ }
+ if (frames != sframes)
+ g_free (frames);
+ break;
+ }
+ case TYPE_METHOD: {
+ int subtype = *p & 0xf0;
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ int64_t ptrdiff = decode_sleb128 (p, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ method_base += ptrdiff;
+ if (subtype == TYPE_JIT) {
+ intptr_t codediff = decode_sleb128 (p, &p);
+ int codelen = decode_uleb128 (p, &p);
+ MethodDesc *jitted_method;
+ if (debug)
+ fprintf (outfile, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base), p, codelen, (void*)(ptr_base + codediff));
+ jitted_method = add_method (method_base, (char*)p, ptr_base + codediff, codelen);
+ if (!(time_base >= time_from && time_base < time_to))
+ jitted_method->ignore_jit = 1;
+ while (*p) p++;
+ p++;
+ } else {
+ MethodDesc *method;
+ if ((thread_filter && thread_filter != thread->thread_id))
+ break;
+ if (!(time_base >= time_from && time_base < time_to))
+ break;
+ method = lookup_method (method_base);
+ if (subtype == TYPE_ENTER) {
+ add_trace_thread (thread, &method->traces, 1);
+ push_method (thread, method, time_base);
+ } else {
+ pop_method (thread, method, time_base);
+ }
+ if (debug)
+ fprintf (outfile, "%s method %s\n", subtype == TYPE_ENTER? "enter": subtype == TYPE_EXC_LEAVE? "exleave": "leave", method->name);
+ }
+ break;
+ }
+ case TYPE_HEAP: {
+ int subtype = *p & 0xf0;
+ if (subtype == TYPE_HEAP_OBJECT) {
+ HeapObjectDesc *ho = NULL;
+ int i;
+ 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);
+ uintptr_t ref_offset = 0;
+ uintptr_t last_obj_offset = 0;
+ ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
+ if (size) {
+ HeapClassDesc *hcd = add_heap_shot_class (thread->current_heap_shot, cd, size);
+ if (collect_traces) {
+ ho = alloc_heap_obj (OBJ_ADDR (objdiff), hcd, num);
+ add_heap_shot_obj (thread->current_heap_shot, ho);
+ ref_offset = 0;
+ }
+ } else {
+ if (collect_traces)
+ ho = heap_shot_obj_add_refs (thread->current_heap_shot, OBJ_ADDR (objdiff), num, &ref_offset);
+ }
+ for (i = 0; i < num; ++i) {
+ /* FIXME: use object distance to measure how good
+ * the GC is at keeping related objects close
+ */
+ uintptr_t offset = ctx->data_version > 1? last_obj_offset + decode_uleb128 (p, &p): -1;
+ intptr_t obj1diff = decode_sleb128 (p, &p);
+ last_obj_offset = offset;
+ if (collect_traces)
+ ho->refs [ref_offset + i] = OBJ_ADDR (obj1diff);
+ if (num_tracked_objects)
+ track_obj_reference (OBJ_ADDR (obj1diff), OBJ_ADDR (objdiff), cd);
+ }
+ 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;
+ 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;
+ 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)
+ fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type));
+ if (collect_traces)
+ thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info);
+ }
+ } else if (subtype == TYPE_HEAP_END) {
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ if (debug)
+ fprintf (outfile, "heap shot end\n");
+ if (collect_traces) {
+ HeapShot *hs = thread->current_heap_shot;
+ if (hs && thread->num_roots) {
+ /* transfer the root ownershipt to the heapshot */
+ hs->num_roots = thread->num_roots;
+ hs->roots = thread->roots;
+ hs->roots_extra = thread->roots_extra;
+ hs->roots_types = thread->roots_types;
+ } else {
+ g_free (thread->roots);
+ g_free (thread->roots_extra);
+ g_free (thread->roots_types);
+ }
+ thread->num_roots = 0;
+ thread->size_roots = 0;
+ thread->roots = NULL;
+ thread->roots_extra = NULL;
+ thread->roots_types = NULL;
+ heap_shot_resolve_reverse_refs (hs);
+ heap_shot_mark_objects (hs);
+ heap_shot_free_objects (hs);
+ }
+ thread->current_heap_shot = NULL;
+ } else if (subtype == TYPE_HEAP_START) {
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ if (debug)
+ fprintf (outfile, "heap shot start\n");
+ thread->current_heap_shot = new_heap_shot (time_base);
+ }
+ break;
+ }
+ case TYPE_MONITOR: {
+ int event = (*p >> 4) & 0x3;
+ int has_bt = *p & TYPE_MONITOR_BT;
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ intptr_t objdiff = decode_sleb128 (p, &p);
+ MethodDesc* sframes [8];
+ MethodDesc** frames = sframes;
+ int record;
+ int num_bt = 0;
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ record = (!thread_filter || thread_filter == thread->thread_id);
+ if (!(time_base >= time_from && time_base < time_to))
+ record = 0;
+ if (event == MONO_PROFILER_MONITOR_CONTENTION) {
+ MonitorDesc *mdesc = lookup_monitor (OBJ_ADDR (objdiff));
+ if (record) {
+ monitor_contention++;
+ mdesc->contentions++;
+ thread->monitor = mdesc;
+ thread->contention_start = time_base;
+ }
+ if (has_bt) {
+ num_bt = 8;
+ frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
+ if (!frames) {
+ fprintf (outfile, "Cannot load backtrace\n");
+ return 0;
+ }
+ if (record)
+ add_trace_methods (frames, num_bt, &mdesc->traces, 1);
+ } else {
+ if (record)
+ add_trace_thread (thread, &mdesc->traces, 1);
+ }
+ } else if (event == MONO_PROFILER_MONITOR_FAIL) {
+ if (record) {
+ monitor_failed++;
+ if (thread->monitor && thread->contention_start) {
+ uint64_t wait_time = time_base - thread->contention_start;
+ if (wait_time > thread->monitor->max_wait_time)
+ thread->monitor->max_wait_time = wait_time;
+ thread->monitor->wait_time += wait_time;
+ thread->monitor = NULL;
+ thread->contention_start = 0;
+ }
+ }
+ } else if (event == MONO_PROFILER_MONITOR_DONE) {
+ if (record) {
+ monitor_acquired++;
+ if (thread->monitor && thread->contention_start) {
+ uint64_t wait_time = time_base - thread->contention_start;
+ if (wait_time > thread->monitor->max_wait_time)
+ thread->monitor->max_wait_time = wait_time;
+ thread->monitor->wait_time += wait_time;
+ thread->monitor = NULL;
+ thread->contention_start = 0;
+ }
+ }
+ }
+ if (debug)
+ fprintf (outfile, "monitor %s for object %p\n", monitor_ev_name (event), (void*)OBJ_ADDR (objdiff));
+ if (frames != sframes)
+ g_free (frames);
+ break;
+ }
+ case TYPE_EXCEPTION: {
+ int subtype = *p & 0x70;
+ int has_bt = *p & TYPE_THROW_BT;
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ MethodDesc* sframes [8];
+ MethodDesc** frames = sframes;
+ int record;
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ record = (!thread_filter || thread_filter == thread->thread_id);
+ if (!(time_base >= time_from && time_base < time_to))
+ record = 0;
+ if (subtype == TYPE_CLAUSE) {
+ 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;
+ if (record)
+ clause_summary [clause_type]++;
+ if (debug)
+ fprintf (outfile, "clause %s (%d) in method %s\n", clause_name (clause_type), clause_num, lookup_method (method_base)->name);
+ } else {
+ intptr_t objdiff = decode_sleb128 (p, &p);
+ if (record)
+ throw_count++;
+ if (has_bt) {
+ has_bt = 8;
+ frames = decode_bt (ctx, sframes, &has_bt, p, &p, ptr_base, &method_base);
+ if (!frames) {
+ fprintf (outfile, "Cannot load backtrace\n");
+ return 0;
+ }
+ if (record)
+ add_trace_methods (frames, has_bt, &exc_traces, 1);
+ } else {
+ if (record)
+ add_trace_thread (thread, &exc_traces, 1);
+ }
+ if (frames != sframes)
+ g_free (frames);
+ if (debug)
+ fprintf (outfile, "throw %p\n", (void*)OBJ_ADDR (objdiff));
+ }
+ break;
+ }
+ case TYPE_RUNTIME: {
+ int subtype = *p & 0xf0;
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ if (subtype == TYPE_JITHELPER) {
+ 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;
+ if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
+ name = (const char *)p;
+ while (*p) p++;
+ p++;
+ } else {
+ name = code_buffer_desc (type);
+ }
+ num_jit_helpers++;
+ jit_helpers_code_size += codelen;
+ if (debug)
+ fprintf (outfile, "jit helper %s, size: %d, code: %p\n", name, codelen, (void*)(ptr_base + codediff));
+ }
+ break;
+ }
+ case TYPE_SAMPLE: {
+ int subtype = *p & 0xf0;
+ if (subtype == TYPE_SAMPLE_HIT) {
+ int i;
+ 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));
+ int count = decode_uleb128 (p, &p);
+ for (i = 0; i < count; ++i) {
+ uintptr_t ip = ptr_base + decode_sleb128 (p, &p);
+ if ((tstamp >= time_from && tstamp < time_to))
+ add_stat_sample (sample_type, ip);
+ if (debug)
+ fprintf (outfile, "sample hit, type: %d at %p for thread %p\n", sample_type, (void*)ip, tid);
+ }
+ if (ctx->data_version > 5) {
+ count = decode_uleb128 (p, &p);
+ for (i = 0; i < count; ++i) {
+ MethodDesc *method;
+ int64_t ptrdiff = decode_sleb128 (p, &p);
+ method_base += ptrdiff;
+ method = lookup_method (method_base);
+ if (debug)
+ 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;
+ 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);
+ add_unmanaged_symbol (addr, name, size);
+ if (debug)
+ fprintf (outfile, "unmanaged symbol %s at %p\n", name, (void*)addr);
+ while (*p) p++;
+ p++;
+ } else if (subtype == TYPE_SAMPLE_UBIN) {
+ /* un unmanaged binary loaded in memory */
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ uintptr_t addr = decode_sleb128 (p, &p);
+ uint64_t offset G_GNUC_UNUSED = decode_uleb128 (p, &p);
+ uintptr_t size = decode_uleb128 (p, &p);
+ char *name;
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ name = pstrdup ((char*)p);
+ add_unmanaged_binary (addr, name, size);
+ if (debug)
+ fprintf (outfile, "unmanaged binary %s at %p\n", name, (void*)addr);
+ while (*p) p++;
+ p++;
+ } else if (subtype == TYPE_SAMPLE_COUNTERS_DESC) {
+ 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);
+ char *section_str, *name;
+ if (section != MONO_COUNTER_PERFCOUNTERS) {
+ section_str = (char*) section_name (section);
+ } else {
+ section_str = pstrdup ((char*)p);
+ while (*p++);
+ }
+ name = pstrdup ((char*)p);
+ while (*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);
+ }
+ } else if (subtype == TYPE_SAMPLE_COUNTERS) {
+ int i;
+ CounterValue *value, *previous = NULL;
+ CounterList *list;
+ 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);
+ if (index == 0)
+ break;
+
+ for (list = counters; list; list = list->next) {
+ if (list->counter->index == (int)index) {
+ previous = list->counter->values_last;
+ break;
+ }
+ }
+
+ if (ctx->data_version > 12)
+ type = *p++;
+ else
+ type = decode_uleb128 (p, &p);
+
+ value = (CounterValue *) g_calloc (1, sizeof (CounterValue));
+ value->timestamp = timestamp;
+
+ switch (type) {
+ case MONO_COUNTER_INT:
+#if SIZEOF_VOID_P == 4
+ case MONO_COUNTER_WORD:
+#endif
+ value->buffer = (unsigned char *)g_malloc (sizeof (int32_t));
+ *(int32_t*)value->buffer = (int32_t)decode_sleb128 (p, &p) + (previous ? (*(int32_t*)previous->buffer) : 0);
+ break;
+ case MONO_COUNTER_UINT:
+ value->buffer = (unsigned char *) g_malloc (sizeof (uint32_t));
+ *(uint32_t*)value->buffer = (uint32_t)decode_uleb128 (p, &p) + (previous ? (*(uint32_t*)previous->buffer) : 0);
+ break;
+ case MONO_COUNTER_LONG:
+#if SIZEOF_VOID_P == 8
+ case MONO_COUNTER_WORD:
+#endif
+ case MONO_COUNTER_TIME_INTERVAL:
+ value->buffer = (unsigned char *) g_malloc (sizeof (int64_t));
+ *(int64_t*)value->buffer = (int64_t)decode_sleb128 (p, &p) + (previous ? (*(int64_t*)previous->buffer) : 0);
+ break;
+ case MONO_COUNTER_ULONG:
+ value->buffer = (unsigned char *) g_malloc (sizeof (uint64_t));
+ *(uint64_t*)value->buffer = (uint64_t)decode_uleb128 (p, &p) + (previous ? (*(uint64_t*)previous->buffer) : 0);
+ break;
+ case MONO_COUNTER_DOUBLE:
+ value->buffer = (unsigned char *) g_malloc (sizeof (double));
+#if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
+ for (i = 0; i < sizeof (double); i++)
+#else
+ for (i = sizeof (double) - 1; i >= 0; i--)
+#endif
+ value->buffer[i] = *p++;
+ break;
+ case MONO_COUNTER_STRING:
+ if (*p++ == 0) {
+ value->buffer = NULL;
+ } else {
+ value->buffer = (unsigned char*) pstrdup ((char*)p);
+ while (*p++);
+ }
+ break;
+ }
+ if (time_between >= time_from && time_between <= time_to)
+ add_counter_value (index, value);
+ }
+ } else {
+ return 0;
+ }
+ break;
+ }
+ case TYPE_COVERAGE:{
+ int subtype = *p & 0xf0;
+ switch (subtype) {
+ case TYPE_COVERAGE_METHOD: {
+ CoverageMethod *method = g_new0 (CoverageMethod, 1);
+ const char *assembly, *klass, *name, *sig, *filename;
+ 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++;
+ sig = (const char *)p; while (*p) p++; p++;
+ filename = (const char *)p; while (*p) p++; p++;
+
+ token = decode_uleb128 (p, &p);
+ method_id = decode_uleb128 (p, &p);
+ n_offsets = decode_uleb128 (p, &p);
+
+ method->assembly_name = g_strdup (assembly);
+ method->class_name = g_strdup (klass);
+ method->method_name = g_strdup (name);
+ method->method_signature = g_strdup (sig);
+ method->filename = g_strdup (filename);
+ method->token = token;
+ method->n_statements = n_offsets;
+ method->coverage = g_ptr_array_new ();
+ method->method_id = method_id;
+
+ coverage_add_method (method);
+
+ break;
+ }
+ case TYPE_COVERAGE_STATEMENT: {
+ CoverageCoverage *coverage = g_new0 (CoverageCoverage, 1);
+ 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);
+ line = decode_uleb128 (p, &p);
+ column = decode_uleb128 (p, &p);
+
+ coverage->method_id = method_id;
+ coverage->offset = offset;
+ coverage->count = count;
+ coverage->line = line;
+ coverage->column = column;
+
+ coverage_add_coverage (coverage);
+ break;
+ }
+ case TYPE_COVERAGE_ASSEMBLY: {
+ CoverageAssembly *assembly = g_new0 (CoverageAssembly, 1);
+ char *name, *guid, *filename;
+ 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++;
+ number_of_methods = decode_uleb128 (p, &p);
+ fully_covered = decode_uleb128 (p, &p);
+ partially_covered = decode_uleb128 (p, &p);
+
+ assembly->name = g_strdup (name);
+ assembly->guid = g_strdup (guid);
+ assembly->filename = g_strdup (filename);
+ assembly->number_of_methods = number_of_methods;
+ assembly->fully_covered = fully_covered;
+ assembly->partially_covered = partially_covered;
+
+ coverage_add_assembly (assembly);
+ break;
+ }
+ case TYPE_COVERAGE_CLASS: {
+ CoverageClass *klass = g_new0 (CoverageClass, 1);
+ char *assembly_name, *class_name;
+ 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);
+ fully_covered = decode_uleb128 (p, &p);
+ partially_covered = decode_uleb128 (p, &p);
+
+ klass->assembly_name = g_strdup (assembly_name);
+ klass->class_name = g_strdup (class_name);
+ klass->number_of_methods = number_of_methods;
+ klass->fully_covered = fully_covered;
+ klass->partially_covered = partially_covered;
+
+ coverage_add_class (klass);
+ break;
+ }
+
+ default:
+ break;
+ }
+ 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);
+ }
+ record_event_stats (event, p - start);
+ }
+ thread->last_time = time_base;
+ for (i = 0; i < thread->stack_id; ++i)
+ thread->stack [i]->recurse_count = 0;
+ return 1;
+}
+
+static int
+read_header_string (ProfContext *ctx, char **field)
+{
+ if (!load_data (ctx, 4))
+ return 0;
+
+ if (!load_data (ctx, read_int32 (ctx->buf)))
+ return 0;
+
+ *field = pstrdup ((const char *) ctx->buf);
+
+ return 1;
+}
+
+static ProfContext*
+load_file (char *name)
+{
+ unsigned char *p;
+ ProfContext *ctx = (ProfContext *) g_calloc (sizeof (ProfContext), 1);
+ if (strcmp (name, "-") == 0)
+ ctx->file = stdin;
+ else
+ ctx->file = fopen (name, "rb");
+ if (!ctx->file) {
+ printf ("Cannot open file: %s\n", name);
+ exit (1);
+ }
+#if defined (HAVE_SYS_ZLIB)
+ if (ctx->file != stdin)
+ ctx->gzfile = gzdopen (fileno (ctx->file), "rb");
+#endif
+ if (!load_data (ctx, 30))
+ return NULL;
+ p = ctx->buf;
+ if (read_int32 (p) != LOG_HEADER_ID || p [6] > LOG_DATA_VERSION)
+ return NULL;
+ ctx->version_major = p [4];
+ ctx->version_minor = p [5];
+ ctx->data_version = p [6];
+ /* reading 64 bit files on 32 bit systems not supported yet */
+ if (p [7] > sizeof (void*))
+ return NULL;
+ if (read_int32 (p + 20)) /* flags must be 0 */
+ return NULL;
+ ctx->startup_time = read_int64 (p + 8);
+ ctx->timer_overhead = read_int32 (p + 16);
+ ctx->pid = read_int32 (p + 24);
+ ctx->port = read_int16 (p + 28);
+ if (ctx->version_major >= 1) {
+ if (!read_header_string (ctx, &ctx->args))
+ return NULL;
+ if (!read_header_string (ctx, &ctx->arch))
+ return NULL;
+ if (!read_header_string (ctx, &ctx->os))
+ return NULL;
+ } else {
+ if (!load_data (ctx, 2)) /* old opsys field, was never used */
+ return NULL;
+ }
+ return ctx;
+}
+
+enum {
+ ALLOC_SORT_BYTES,
+ ALLOC_SORT_COUNT
+};
+static int alloc_sort_mode = ALLOC_SORT_BYTES;
+
+static int
+compare_class (const void *a, const void *b)
+{
+ ClassDesc *const *A = (ClassDesc *const *)a;
+ ClassDesc *const *B = (ClassDesc *const *)b;
+ uint64_t vala, valb;
+ if (alloc_sort_mode == ALLOC_SORT_BYTES) {
+ vala = (*A)->alloc_size;
+ valb = (*B)->alloc_size;
+ } else {
+ vala = (*A)->allocs;
+ valb = (*B)->allocs;
+ }
+ if (valb == vala)
+ return 0;
+ if (valb < vala)
+ return -1;
+ return 1;
+}
+
+static void
+dump_header (ProfContext *ctx)
+{
+ time_t st = ctx->startup_time / 1000;
+ char *t = ctime (&st);
+ fprintf (outfile, "\nMono log profiler data\n");
+ fprintf (outfile, "\tProfiler version: %d.%d\n", ctx->version_major, ctx->version_minor);
+ fprintf (outfile, "\tData version: %d\n", ctx->data_version);
+ if (ctx->version_major >= 1) {
+ fprintf (outfile, "\tArguments: %s\n", ctx->args);
+ fprintf (outfile, "\tArchitecture: %s\n", ctx->arch);
+ fprintf (outfile, "\tOperating system: %s\n", ctx->os);
+ }
+ fprintf (outfile, "\tMean timer overhead: %d nanoseconds\n", ctx->timer_overhead);
+ fprintf (outfile, "\tProgram startup: %s", t);
+ if (ctx->pid)
+ fprintf (outfile, "\tProgram ID: %d\n", ctx->pid);
+ if (ctx->port)
+ fprintf (outfile, "\tServer listening on: %d\n", ctx->port);
+}
+
+static void
+dump_traces (TraceDesc *traces, const char *desc)
+{
+ int j;
+ if (!show_traces)
+ return;
+ if (!traces->count)
+ return;
+ sort_context_array (traces);
+ for (j = 0; j < traces->count; ++j) {
+ int k;
+ BackTrace *bt;
+ bt = traces->traces [j].bt;
+ if (!bt->count)
+ continue;
+ fprintf (outfile, "\t%llu %s from:\n", (unsigned long long) traces->traces [j].count, desc);
+ for (k = 0; k < bt->count; ++k)
+ fprintf (outfile, "\t\t%s\n", bt->methods [k]->name);
+ }
+}
+
+static void
+dump_threads (ProfContext *ctx)
+{
+ ThreadContext *thread;
+ fprintf (outfile, "\nThread summary\n");
+ for (thread = ctx->threads; thread; thread = thread->next) {
+ if (thread->thread_id) {
+ fprintf (outfile, "\tThread: %p, name: \"%s\"\n", (void*)thread->thread_id, thread->name? thread->name: "");
+ }
+ }
+}
+
+static void
+dump_domains (ProfContext *ctx)
+{
+ fprintf (outfile, "\nDomain summary\n");
+
+ for (DomainContext *domain = ctx->domains; domain; domain = domain->next)
+ fprintf (outfile, "\tDomain: %p, friendly name: \"%s\"\n", (void *) domain->domain_id, domain->friendly_name);
+}
+
+static void
+dump_remctxs (ProfContext *ctx)
+{
+ fprintf (outfile, "\nContext summary\n");
+
+ for (RemCtxContext *remctx = ctx->remctxs; remctx; remctx = remctx->next)
+ fprintf (outfile, "\tContext: %p, domain: %p\n", (void *) remctx->remctx_id, (void *) remctx->domain_id);
+}
+
+static void
+dump_exceptions (void)
+{
+ int i;
+ fprintf (outfile, "\nException summary\n");
+ fprintf (outfile, "\tThrows: %llu\n", (unsigned long long) throw_count);
+ dump_traces (&exc_traces, "throws");
+ for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
+ if (!clause_summary [i])
+ continue;
+ fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), (unsigned long long) clause_summary [i]);
+ }
+}
+
+static int
+compare_monitor (const void *a, const void *b)
+{
+ MonitorDesc *const *A = (MonitorDesc *const *)a;
+ MonitorDesc *const *B = (MonitorDesc *const *)b;
+ if ((*B)->wait_time == (*A)->wait_time)
+ return 0;
+ if ((*B)->wait_time < (*A)->wait_time)
+ return -1;
+ return 1;
+}
+
+static void
+dump_monitors (void)
+{
+ MonitorDesc **monitors;
+ int i, j;
+ if (!num_monitors)
+ return;
+ monitors = (MonitorDesc **) g_malloc (sizeof (void*) * num_monitors);
+ for (i = 0, j = 0; i < SMALL_HASH_SIZE; ++i) {
+ MonitorDesc *mdesc = monitor_hash [i];
+ while (mdesc) {
+ monitors [j++] = mdesc;
+ mdesc = mdesc->next;
+ }
+ }
+ qsort (monitors, num_monitors, sizeof (void*), compare_monitor);
+ fprintf (outfile, "\nMonitor lock summary\n");
+ for (i = 0; i < num_monitors; ++i) {
+ MonitorDesc *mdesc = monitors [i];
+ fprintf (outfile, "\tLock object %p: %d contentions\n", (void*)mdesc->objid, (int)mdesc->contentions);
+ fprintf (outfile, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
+ mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
+ dump_traces (&mdesc->traces, "contentions");
+ }
+ fprintf (outfile, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention);
+ fprintf (outfile, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired);
+ fprintf (outfile, "\tLock failures: %llu\n", (unsigned long long) monitor_failed);
+}
+
+static void
+dump_gcs (void)
+{
+ int i;
+ fprintf (outfile, "\nGC summary\n");
+ fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
+ fprintf (outfile, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size);
+ fprintf (outfile, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves);
+ for (i = 0; i < 3; ++i) {
+ if (!gc_info [i].count)
+ continue;
+ fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
+ i, gc_info [i].count,
+ (unsigned long long) (gc_info [i].max_time / 1000),
+ (unsigned long long) (gc_info [i].total_time / 1000),
+ (unsigned long long) (gc_info [i].total_time / gc_info [i].count / 1000));
+ }
+ for (i = 0; i < 3; ++i) {
+ if (!handle_info [i].max_live)
+ continue;
+ fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
+ get_handle_name (i),
+ (unsigned long long) (handle_info [i].created),
+ (unsigned long long) (handle_info [i].destroyed),
+ (unsigned long long) (handle_info [i].max_live));
+ dump_traces (&handle_info [i].traces, "created");
+ dump_traces (&handle_info [i].destroy_traces, "destroyed");
+ }
+}
+
+static void
+dump_jit (void)
+{
+ int i;
+ int code_size = 0;
+ int compiled_methods = 0;
+ MethodDesc* m;
+ fprintf (outfile, "\nJIT summary\n");
+ for (i = 0; i < HASH_SIZE; ++i) {
+ m = method_hash [i];
+ for (m = method_hash [i]; m; m = m->next) {
+ if (!m->code || m->ignore_jit)
+ continue;
+ compiled_methods++;
+ code_size += m->len;
+ }
+ }
+ fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
+ fprintf (outfile, "\tGenerated code size: %d\n", code_size);
+ fprintf (outfile, "\tJIT helpers: %d\n", num_jit_helpers);
+ fprintf (outfile, "\tJIT helpers code size: %d\n", jit_helpers_code_size);
+}
+
+static void
+dump_allocations (void)
+{
+ int i, c;
+ intptr_t allocs = 0;
+ uint64_t size = 0;
+ int header_done = 0;
+ ClassDesc **classes = (ClassDesc **) g_malloc (num_classes * sizeof (void*));
+ ClassDesc *cd;
+ c = 0;
+ for (i = 0; i < HASH_SIZE; ++i) {
+ cd = class_hash [i];
+ while (cd) {
+ classes [c++] = cd;
+ cd = cd->next;
+ }
+ }
+ qsort (classes, num_classes, sizeof (void*), compare_class);
+ for (i = 0; i < num_classes; ++i) {
+ cd = classes [i];
+ if (!cd->allocs)
+ continue;
+ allocs += cd->allocs;
+ size += cd->alloc_size;
+ if (!header_done++) {
+ fprintf (outfile, "\nAllocation summary\n");
+ fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
+ }
+ fprintf (outfile, "%10llu %10zd %8llu %s\n",
+ (unsigned long long) (cd->alloc_size),
+ cd->allocs,
+ (unsigned long long) (cd->alloc_size / cd->allocs),
+ cd->name);
+ dump_traces (&cd->traces, "bytes");
+ }
+ if (allocs)
+ fprintf (outfile, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size, allocs);
+}
+
+enum {
+ METHOD_SORT_TOTAL,
+ METHOD_SORT_SELF,
+ METHOD_SORT_CALLS
+};
+
+static int method_sort_mode = METHOD_SORT_TOTAL;
+
+static int
+compare_method (const void *a, const void *b)
+{
+ MethodDesc *const *A = (MethodDesc *const *)a;
+ MethodDesc *const *B = (MethodDesc *const *)b;
+ uint64_t vala, valb;
+ if (method_sort_mode == METHOD_SORT_SELF) {
+ vala = (*A)->self_time;
+ valb = (*B)->self_time;
+ } else if (method_sort_mode == METHOD_SORT_CALLS) {
+ vala = (*A)->calls;
+ valb = (*B)->calls;
+ } else {
+ vala = (*A)->total_time;
+ valb = (*B)->total_time;
+ }
+ if (vala == valb)
+ return 0;
+ if (valb < vala)
+ return -1;
+ return 1;
+}
+
+static void
+dump_metadata (void)
+{
+ fprintf (outfile, "\nMetadata summary\n");
+ fprintf (outfile, "\tLoaded images: %d\n", num_images);
+ if (verbose) {
+ ImageDesc *image;
+ int i;
+ for (i = 0; i < SMALL_HASH_SIZE; ++i) {
+ image = image_hash [i];
+ while (image) {
+ fprintf (outfile, "\t\t%s\n", image->filename);
+ image = image->next;
+ }
+ }
+ }
+ fprintf (outfile, "\tLoaded assemblies: %d\n", num_assemblies);
+ if (verbose) {
+ AssemblyDesc *assembly;
+ int i;
+ for (i = 0; i < SMALL_HASH_SIZE; ++i) {
+ assembly = assembly_hash [i];
+ while (assembly) {
+ fprintf (outfile, "\t\t%s\n", assembly->asmname);
+ assembly = assembly->next;
+ }
+ }
+ }
+}
+
+static void
+dump_methods (void)
+{
+ int i, c;
+ uint64_t calls = 0;
+ int header_done = 0;
+ MethodDesc **methods = (MethodDesc **) g_malloc (num_methods * sizeof (void*));
+ MethodDesc *cd;
+ c = 0;
+ for (i = 0; i < HASH_SIZE; ++i) {
+ cd = method_hash [i];
+ while (cd) {
+ cd->total_time = cd->self_time + cd->callee_time;
+ methods [c++] = cd;
+ cd = cd->next;
+ }
+ }
+ qsort (methods, num_methods, sizeof (void*), compare_method);
+ for (i = 0; i < num_methods; ++i) {
+ uint64_t msecs;
+ uint64_t smsecs;
+ cd = methods [i];
+ if (!cd->calls)
+ continue;
+ calls += cd->calls;
+ msecs = cd->total_time / 1000000;
+ smsecs = (cd->total_time - cd->callee_time) / 1000000;
+ if (!msecs && !verbose)
+ continue;
+ if (!header_done++) {
+ fprintf (outfile, "\nMethod call summary\n");
+ fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
+ }
+ fprintf (outfile, "%8llu %8llu %10llu %s\n",
+ (unsigned long long) (msecs),
+ (unsigned long long) (smsecs),
+ (unsigned long long) (cd->calls),
+ cd->name);
+ dump_traces (&cd->traces, "calls");
+ }
+ if (calls)
+ fprintf (outfile, "Total calls: %llu\n", (unsigned long long) calls);
+}
+
+static int
+compare_heap_class (const void *a, const void *b)
+{
+ HeapClassDesc *const *A = (HeapClassDesc *const *)a;
+ HeapClassDesc *const *B = (HeapClassDesc *const *)b;
+ uint64_t vala, valb;
+ if (alloc_sort_mode == ALLOC_SORT_BYTES) {
+ vala = (*A)->total_size;
+ valb = (*B)->total_size;
+ } else {
+ vala = (*A)->count;
+ valb = (*B)->count;
+ }
+ if (valb == vala)
+ return 0;
+ if (valb < vala)
+ return -1;
+ return 1;
+}
+
+static int
+compare_rev_class (const void *a, const void *b)
+{
+ const HeapClassRevRef *A = (const HeapClassRevRef *)a;
+ const HeapClassRevRef *B = (const HeapClassRevRef *)b;
+ if (B->count == A->count)
+ return 0;
+ if (B->count < A->count)
+ return -1;
+ return 1;
+}
+
+static void
+dump_rev_claases (HeapClassRevRef *revs, int count)
+{
+ int j;
+ if (!show_traces)
+ return;
+ if (!count)
+ return;
+ for (j = 0; j < count; ++j) {
+ HeapClassDesc *cd = revs [j].klass;
+ fprintf (outfile, "\t\t%llu references from: %s\n",
+ (unsigned long long) (revs [j].count),
+ cd->klass->name);
+ }
+}
+
+static void
+heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
+{
+ uint64_t size = 0;
+ uint64_t count = 0;
+ int ccount = 0;
+ int i;
+ HeapClassDesc *cd;
+ HeapClassDesc **sorted;
+ sorted = (HeapClassDesc **) g_malloc (sizeof (void*) * hs->class_count);
+ for (i = 0; i < hs->hash_size; ++i) {
+ cd = hs->class_hash [i];
+ if (!cd)
+ continue;
+ count += cd->count;
+ size += cd->total_size;
+ sorted [ccount++] = cd;
+ }
+ hs->sorted = sorted;
+ qsort (sorted, ccount, sizeof (void*), compare_heap_class);
+ fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
+ hs_num,
+ (hs->timestamp - startup_time)/1000000000.0,
+ (unsigned long long) (size),
+ (unsigned long long) (count),
+ ccount, hs->num_roots);
+ if (!verbose && ccount > 30)
+ ccount = 30;
+ fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
+ for (i = 0; i < ccount; ++i) {
+ HeapClassRevRef *rev_sorted;
+ int j, k;
+ HeapClassDesc *ocd = NULL;
+ cd = sorted [i];
+ if (last_hs)
+ ocd = heap_class_lookup (last_hs, cd->klass);
+ fprintf (outfile, "\t%10llu %10llu %8llu %s",
+ (unsigned long long) (cd->total_size),
+ (unsigned long long) (cd->count),
+ (unsigned long long) (cd->total_size / cd->count),
+ cd->klass->name);
+ if (ocd) {
+ int64_t bdiff = cd->total_size - ocd->total_size;
+ int64_t cdiff = cd->count - ocd->count;
+ fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff, (long long) cdiff);
+ } else {
+ fprintf (outfile, "\n");
+ }
+ if (!collect_traces)
+ continue;
+ rev_sorted = (HeapClassRevRef *) g_malloc (cd->rev_count * sizeof (HeapClassRevRef));
+ k = 0;
+ for (j = 0; j < cd->rev_hash_size; ++j) {
+ if (cd->rev_hash [j].klass)
+ rev_sorted [k++] = cd->rev_hash [j];
+ }
+ assert (cd->rev_count == k);
+ qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
+ if (cd->root_references)
+ fprintf (outfile, "\t\t%zd root references (%zd pinning)\n", cd->root_references, cd->pinned_references);
+ dump_rev_claases (rev_sorted, cd->rev_count);
+ g_free (rev_sorted);
+ }
+ g_free (sorted);
+}
+
+static int
+compare_heap_shots (const void *a, const void *b)
+{
+ HeapShot *const *A = (HeapShot *const *)a;
+ HeapShot *const *B = (HeapShot *const *)b;
+ if ((*B)->timestamp == (*A)->timestamp)
+ return 0;
+ if ((*B)->timestamp > (*A)->timestamp)
+ return -1;
+ return 1;
+}
+
+static void
+dump_heap_shots (void)
+{
+ HeapShot **hs_sorted;
+ HeapShot *hs;
+ HeapShot *last_hs = NULL;
+ int i;
+ if (!heap_shots)
+ return;
+ hs_sorted = (HeapShot **) g_malloc (num_heap_shots * sizeof (void*));
+ fprintf (outfile, "\nHeap shot summary\n");
+ i = 0;
+ for (hs = heap_shots; hs; hs = hs->next)
+ hs_sorted [i++] = hs;
+ qsort (hs_sorted, num_heap_shots, sizeof (void*), compare_heap_shots);
+ for (i = 0; i < num_heap_shots; ++i) {
+ hs = hs_sorted [i];
+ heap_shot_summary (hs, i, last_hs);
+ last_hs = hs;
+ }
+}
+
+/* This is a very basic escape function that escapes < > and &
+ Ideally we'd use g_markup_escape_string but that function isn't
+ available in Mono's eglib. This was written without looking at the
+ source of that function in glib. */
+static char *
+escape_string_for_xml (const char *string)
+{
+ GString *string_builder = g_string_new (NULL);
+ const char *start, *p;
+
+ start = p = string;
+ while (*p) {
+ while (*p && *p != '&' && *p != '<' && *p != '>')
+ p++;
+
+ g_string_append_len (string_builder, start, p - start);
+
+ if (*p == '\0')
+ break;
+
+ switch (*p) {
+ case '<':
+ g_string_append (string_builder, "<");
+ break;
+
+ case '>':
+ g_string_append (string_builder, ">");
+ break;
+
+ case '&':
+ g_string_append (string_builder, "&");
+ break;
+
+ default:
+ break;
+ }
+
+ p++;
+ start = p;
+ }
+
+ return g_string_free (string_builder, FALSE);
+}
+
+static int
+sort_assemblies (gconstpointer a, gconstpointer b)
+{
+ CoverageAssembly *assembly_a = *(CoverageAssembly **)a;
+ CoverageAssembly *assembly_b = *(CoverageAssembly **)b;
+
+ if (assembly_a->name == NULL && assembly_b->name == NULL)
+ return 0;
+ else if (assembly_a->name == NULL)
+ return -1;
+ else if (assembly_b->name == NULL)
+ return 1;
+
+ return strcmp (assembly_a->name, assembly_b->name);
+}
+
+static void
+dump_coverage (void)
+{
+ if (!coverage_methods && !coverage_assemblies)
+ return;
+
+ gather_coverage_statements ();
+ fprintf (outfile, "\nCoverage Summary:\n");
+
+ if (coverage_outfile) {
+ fprintf (coverage_outfile, "<?xml version=\"1.0\"?>\n");
+ fprintf (coverage_outfile, "<coverage version=\"0.3\">\n");
+ }
+
+ g_ptr_array_sort (coverage_assemblies, sort_assemblies);
+
+ for (guint i = 0; i < coverage_assemblies->len; i++) {
+ CoverageAssembly *assembly = (CoverageAssembly *)coverage_assemblies->pdata[i];
+ GPtrArray *classes;
+
+ if (assembly->number_of_methods != 0) {
+ int percentage = ((assembly->fully_covered + assembly->partially_covered) * 100) / assembly->number_of_methods;
+ fprintf (outfile, "\t%s (%s) %d%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, percentage, assembly->number_of_methods, assembly->fully_covered);
+ } else
+ fprintf (outfile, "\t%s (%s) ?%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, assembly->number_of_methods, assembly->fully_covered);
+
+ if (coverage_outfile) {
+ char *escaped_name, *escaped_filename;
+ escaped_name = escape_string_for_xml (assembly->name);
+ escaped_filename = escape_string_for_xml (assembly->filename);
+
+ fprintf (coverage_outfile, "\t<assembly name=\"%s\" guid=\"%s\" filename=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n", escaped_name, assembly->guid, escaped_filename, assembly->number_of_methods, assembly->fully_covered, assembly->partially_covered);
+
+ g_free (escaped_name);
+ g_free (escaped_filename);
+ }
+
+ classes = (GPtrArray *)g_hash_table_lookup (coverage_assembly_classes, assembly->name);
+ if (classes) {
+ for (guint j = 0; j < classes->len; j++) {
+ CoverageClass *klass = (CoverageClass *)classes->pdata [j];
+
+ if (klass->number_of_methods > 0) {
+ int percentage = ((klass->fully_covered + klass->partially_covered) * 100) / klass->number_of_methods;
+ fprintf (outfile, "\t\t%s %d%% covered (%d methods - %d covered)\n", klass->class_name, percentage, klass->number_of_methods, klass->fully_covered);
+ } else
+ fprintf (outfile, "\t\t%s ?%% covered (%d methods - %d covered)\n", klass->class_name, klass->number_of_methods, klass->fully_covered);
+
+ if (coverage_outfile) {
+ char *escaped_name;
+ escaped_name = escape_string_for_xml (klass->class_name);
+
+ fprintf (coverage_outfile, "\t\t<class name=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n", escaped_name, klass->number_of_methods, klass->fully_covered, klass->partially_covered);
+ g_free (escaped_name);
+ }
+ }
+ }
+ }
+
+ for (guint i = 0; i < coverage_methods->len; i++) {
+ CoverageMethod *method = (CoverageMethod *)coverage_methods->pdata [i];
+
+ if (coverage_outfile) {
+ char *escaped_assembly, *escaped_class, *escaped_method, *escaped_sig, *escaped_filename;
+
+ escaped_assembly = escape_string_for_xml (method->assembly_name);
+ escaped_class = escape_string_for_xml (method->class_name);
+ escaped_method = escape_string_for_xml (method->method_name);
+ escaped_sig = escape_string_for_xml (method->method_signature);
+ escaped_filename = escape_string_for_xml (method->filename);
+
+ fprintf (coverage_outfile, "\t<method assembly=\"%s\" class=\"%s\" name=\"%s (%s)\" filename=\"%s\" token=\"%d\">\n", escaped_assembly, escaped_class, escaped_method, escaped_sig, escaped_filename, method->token);
+
+ g_free (escaped_assembly);
+ g_free (escaped_class);
+ g_free (escaped_method);
+ g_free (escaped_sig);
+ g_free (escaped_filename);
+
+ for (guint j = 0; j < method->coverage->len; j++) {
+ CoverageCoverage *coverage = (CoverageCoverage *)method->coverage->pdata [j];
+ fprintf (coverage_outfile, "\t\t<statement offset=\"%d\" counter=\"%d\" line=\"%d\" column=\"%d\"/>\n", coverage->offset, coverage->count, coverage->line, coverage->column);
+ }
+ fprintf (coverage_outfile, "\t</method>\n");
+ }
+ }
+
+ if (coverage_outfile) {
+ fprintf (coverage_outfile, "</coverage>\n");
+ fclose (coverage_outfile);
+ coverage_outfile = NULL;
+ }
+}
+
+#define DUMP_EVENT_STAT(EVENT,SUBTYPE) dump_event (#EVENT, #SUBTYPE, EVENT, SUBTYPE);
+
+static void
+dump_event (const char *event_name, const char *subtype_name, int event, int subtype)
+{
+ int idx = event | subtype;
+ EventStat evt = stats [idx];
+ if (!evt.count)
+ return;
+
+ fprintf (outfile, "\t%16s\t%26s\tcount %6d\tmin %3d\tmax %6d\tbytes %d\n", event_name, subtype_name, evt.count, evt.min_size, evt.max_size, evt.bytes);
+}
+
+static void
+dump_stats (void)
+{
+ fprintf (outfile, "\nMlpd statistics\n");
+ fprintf (outfile, "\tBuffer count %d\toverhead %d (%d bytes per header)\n", buffer_count, buffer_count * BUFFER_HEADER_SIZE, BUFFER_HEADER_SIZE);
+ fprintf (outfile, "\nEvent details:\n");
+
+ DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_NO_BT);
+ DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_BT);
+
+ DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_EVENT);
+ DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_RESIZE);
+ DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_MOVE);
+ DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED);
+ DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED);
+ DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED_BT);
+ DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED_BT);
+
+ DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_LOAD);
+ DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_UNLOAD);
+
+ DUMP_EVENT_STAT (TYPE_METHOD, TYPE_LEAVE);
+ DUMP_EVENT_STAT (TYPE_METHOD, TYPE_ENTER);
+ DUMP_EVENT_STAT (TYPE_METHOD, TYPE_EXC_LEAVE);
+ DUMP_EVENT_STAT (TYPE_METHOD, TYPE_JIT);
+
+ DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW_NO_BT);
+ DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW_BT);
+ DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_CLAUSE);
+
+ DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_NO_BT);
+ DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_BT);
+
+ DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_START);
+ DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_END);
+ DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_OBJECT);
+ DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_ROOT);
+
+ DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_HIT);
+ DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_USYM);
+ DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_UBIN);
+ DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS_DESC);
+ DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS);
+
+ DUMP_EVENT_STAT (TYPE_RUNTIME, TYPE_JITHELPER);
+
+ DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_ASSEMBLY);
+ 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);
+}
+
+
+
+static void
+flush_context (ProfContext *ctx)
+{
+ ThreadContext *thread;
+ /* FIXME: sometimes there are leftovers: indagate */
+ for (thread = ctx->threads; thread; thread = thread->next) {
+ while (thread->stack_id) {
+ if (debug)
+ fprintf (outfile, "thread %p has %d items on stack\n", (void*)thread->thread_id, thread->stack_id);
+ pop_method (thread, thread->stack [thread->stack_id - 1], thread->last_time);
+ }
+ }
+}
+
+static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot,counters,coverage";
+
+static const char*
+match_option (const char *p, const char *opt)
+{
+ int len = strlen (opt);
+ if (strncmp (p, opt, len) == 0) {
+ if (p [len] == ',')
+ len++;
+ return p + len;
+ }
+ return p;
+}
+
+static int
+print_reports (ProfContext *ctx, const char *reps, int parse_only)
+{
+ const char *opt;
+ const char *p;
+ for (p = reps; *p; p = opt) {
+ if ((opt = match_option (p, "header")) != p) {
+ if (!parse_only)
+ dump_header (ctx);
+ continue;
+ }
+ if ((opt = match_option (p, "thread")) != p) {
+ if (!parse_only)
+ dump_threads (ctx);
+ continue;
+ }
+ if ((opt = match_option (p, "domain")) != p) {
+ if (!parse_only)
+ dump_domains (ctx);
+ continue;
+ }
+ if ((opt = match_option (p, "context")) != p) {
+ if (!parse_only)
+ dump_remctxs (ctx);
+ continue;
+ }
+ if ((opt = match_option (p, "gc")) != p) {
+ if (!parse_only)
+ dump_gcs ();
+ continue;
+ }
+ if ((opt = match_option (p, "jit")) != p) {
+ if (!parse_only)
+ dump_jit ();
+ continue;
+ }
+ if ((opt = match_option (p, "alloc")) != p) {
+ if (!parse_only)
+ dump_allocations ();
+ continue;
+ }
+ if ((opt = match_option (p, "call")) != p) {
+ if (!parse_only)
+ dump_methods ();
+ continue;
+ }
+ if ((opt = match_option (p, "metadata")) != p) {
+ if (!parse_only)
+ dump_metadata ();
+ continue;
+ }
+ if ((opt = match_option (p, "exception")) != p) {
+ if (!parse_only)
+ dump_exceptions ();
+ continue;
+ }
+ if ((opt = match_option (p, "monitor")) != p) {
+ if (!parse_only)
+ dump_monitors ();
+ continue;
+ }
+ if ((opt = match_option (p, "heapshot")) != p) {
+ if (!parse_only)
+ dump_heap_shots ();
+ continue;
+ }
+ if ((opt = match_option (p, "sample")) != p) {
+ if (!parse_only)
+ dump_samples ();
+ continue;
+ }
+ if ((opt = match_option (p, "counters")) != p) {
+ if (!parse_only)
+ dump_counters ();
+ continue;
+ }
+ if ((opt = match_option (p, "coverage")) != p) {
+ if (!parse_only)
+ dump_coverage ();
+ continue;
+ }
+ if ((opt = match_option (p, "stats")) != p) {
+ if (!parse_only)
+ dump_stats ();
+ continue;
+ }
+ return 0;
+ }
+ return 1;
+}
+
+static int
+add_find_spec (const char *p)
+{
+ if (p [0] == 'S' && p [1] == ':') {
+ char *vale;
+ find_size = strtoul (p + 2, &vale, 10);
+ return 1;
+ } else if (p [0] == 'T' && p [1] == ':') {
+ find_name = p + 2;
+ return 1;
+ }
+ return 0;
+}
+
+static void
+usage (void)
+{
+ printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR);
+ printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
+ printf ("FILENAME can be '-' to read from standard input.\n");
+ printf ("Options:\n");
+ printf ("\t--help display this help\n");
+ printf ("\t--out=FILE write to FILE instead of stdout\n");
+ printf ("\t--traces collect and show backtraces\n");
+ printf ("\t--maxframes=NUM limit backtraces to NUM entries\n");
+ printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
+ printf ("\t %s\n", reports);
+ printf ("\t--method-sort=MODE sort methods according to MODE: total, self, calls\n");
+ printf ("\t--alloc-sort=MODE sort allocations according to MODE: bytes, count\n");
+ printf ("\t--counters-sort=MODE sort counters according to MODE: time, category\n");
+ printf ("\t only accessible in verbose mode\n");
+ printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
+ printf ("\t--find=FINDSPEC find and track objects matching FINFSPEC, where FINDSPEC is:\n");
+ printf ("\t S:minimum_size or T:partial_name\n");
+ printf ("\t--thread=THREADID consider just the data for thread THREADID\n");
+ printf ("\t--time=FROM-TO consider data FROM seconds from startup up to TO seconds\n");
+ printf ("\t--verbose increase verbosity level\n");
+ printf ("\t--debug display decoding debug info for mprof-report devs\n");
+ printf ("\t--coverage-out=FILE write the coverage info to FILE as XML\n");
+}
+
+int
+main (int argc, char *argv[])
+{
+ ProfContext *ctx;
+ int i;
+ outfile = stdout;
+ for (i = 1; i < argc; ++i) {
+ if (strcmp ("--debug", argv [i]) == 0) {
+ debug++;
+ } else if (strcmp ("--help", argv [i]) == 0) {
+ usage ();
+ return 0;
+ } else if (strncmp ("--alloc-sort=", argv [i], 13) == 0) {
+ const char *val = argv [i] + 13;
+ if (strcmp (val, "bytes") == 0) {
+ alloc_sort_mode = ALLOC_SORT_BYTES;
+ } else if (strcmp (val, "count") == 0) {
+ alloc_sort_mode = ALLOC_SORT_COUNT;
+ } else {
+ usage ();
+ return 1;
+ }
+ } else if (strncmp ("--method-sort=", argv [i], 14) == 0) {
+ const char *val = argv [i] + 14;
+ if (strcmp (val, "total") == 0) {
+ method_sort_mode = METHOD_SORT_TOTAL;
+ } else if (strcmp (val, "self") == 0) {
+ method_sort_mode = METHOD_SORT_SELF;
+ } else if (strcmp (val, "calls") == 0) {
+ method_sort_mode = METHOD_SORT_CALLS;
+ } else {
+ usage ();
+ return 1;
+ }
+ } else if (strncmp ("--counters-sort=", argv [i], 16) == 0) {
+ const char *val = argv [i] + 16;
+ if (strcmp (val, "time") == 0) {
+ counters_sort_mode = COUNTERS_SORT_TIME;
+ } else if (strcmp (val, "category") == 0) {
+ counters_sort_mode = COUNTERS_SORT_CATEGORY;
+ } else {
+ usage ();
+ return 1;
+ }
+ } else if (strncmp ("--reports=", argv [i], 10) == 0) {
+ const char *val = argv [i] + 10;
+ if (!print_reports (NULL, val, 1)) {
+ usage ();
+ return 1;
+ }
+ reports = val;
+ } else if (strncmp ("--out=", argv [i], 6) == 0) {
+ const char *val = argv [i] + 6;
+ outfile = fopen (val, "w");
+ if (!outfile) {
+ printf ("Cannot open output file: %s\n", val);
+ return 1;
+ }
+ } else if (strncmp ("--maxframes=", argv [i], 12) == 0) {
+ const char *val = argv [i] + 12;
+ char *vale;
+ trace_max = strtoul (val, &vale, 10);
+ } else if (strncmp ("--find=", argv [i], 7) == 0) {
+ const char *val = argv [i] + 7;
+ if (!add_find_spec (val)) {
+ usage ();
+ return 1;
+ }
+ } else if (strncmp ("--track=", argv [i], 8) == 0) {
+ const char *val = argv [i] + 8;
+ char *vale;
+ while (*val) {
+ uintptr_t tracked_obj;
+ if (*val == ',') {
+ val++;
+ continue;
+ }
+ tracked_obj = strtoul (val, &vale, 0);
+ found_object (tracked_obj);
+ val = vale;
+ }
+ } else if (strncmp ("--thread=", argv [i], 9) == 0) {
+ const char *val = argv [i] + 9;
+ char *vale;
+ thread_filter = strtoul (val, &vale, 0);
+ } else if (strncmp ("--time=", argv [i], 7) == 0) {
+ char *val = pstrdup (argv [i] + 7);
+ double from_secs, to_secs;
+ char *top = strchr (val, '-');
+ if (!top) {
+ usage ();
+ return 1;
+ }
+ *top++ = 0;
+ from_secs = atof (val);
+ to_secs = atof (top);
+ g_free (val);
+ if (from_secs > to_secs) {
+ usage ();
+ return 1;
+ }
+ time_from = from_secs * 1000000000;
+ time_to = to_secs * 1000000000;
+ use_time_filter = 1;
+ } else if (strcmp ("--verbose", argv [i]) == 0) {
+ verbose++;
+ } else if (strcmp ("--traces", argv [i]) == 0) {
+ show_traces = 1;
+ collect_traces = 1;
+ } else if (strncmp ("--coverage-out=", argv [i], 15) == 0) {
+ const char *val = argv [i] + 15;
+ coverage_outfile = fopen (val, "w");
+ if (!coverage_outfile) {
+ printf ("Cannot open output file: %s\n", val);
+ return 1;
+ }
+ } else {
+ break;
+ }
+ }
+ if (i >= argc) {
+ usage ();
+ return 2;
+ }
+ ctx = load_file (argv [i]);
+ if (!ctx) {
+ printf ("Not a log profiler data file (or unsupported version).\n");
+ return 1;
+ }
+ while (decode_buffer (ctx));
+ flush_context (ctx);
+ if (num_tracked_objects)
+ return 0;
+ print_reports (ctx, reports, 0);
+ return 0;
+}
+++ /dev/null
-/*
- * proflog.c: mono log profiler
- *
- * Authors:
- * Paolo Molaro (lupus@ximian.com)
- * Alex Rønne Petersen (alexrp@xamarin.com)
- *
- * Copyright 2010 Novell, Inc (http://www.novell.com)
- * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
- * Licensed under the MIT license. See LICENSE file in the project root for full license information.
- */
-
-#include <config.h>
-#include "../mini/jit.h"
-#include "../metadata/metadata-internals.h"
-#include <mono/metadata/profiler.h>
-#include <mono/metadata/threads.h>
-#include <mono/metadata/debug-helpers.h>
-#include <mono/metadata/mono-config.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/metadata/tokentype.h>
-#include <mono/metadata/tabledefs.h>
-#include <mono/utils/atomic.h>
-#include <mono/utils/mono-membar.h>
-#include <mono/utils/mono-mmap.h>
-#include <mono/utils/mono-counters.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>
-#include <glib.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef HAVE_SCHED_GETAFFINITY
-#include <sched.h>
-#endif
-#include <fcntl.h>
-#include <errno.h>
-#if defined(HOST_WIN32) || defined(DISABLE_SOCKETS)
-#define DISABLE_HELPER_THREAD 1
-#endif
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-#ifdef HAVE_DLFCN_H
-#include <dlfcn.h>
-#endif
-#ifdef HAVE_EXECINFO_H
-#include <execinfo.h>
-#endif
-#ifdef HAVE_LINK_H
-#include <link.h>
-#endif
-
-#ifndef DISABLE_HELPER_THREAD
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <sys/select.h>
-#endif
-
-#ifdef HOST_WIN32
-#include <windows.h>
-#else
-#include <pthread.h>
-#endif
-
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-
-#include "utils.c"
-#include "proflog.h"
-
-#if defined (HAVE_SYS_ZLIB)
-#include <zlib.h>
-#endif
-
-#if defined(__linux__)
-
-#include <unistd.h>
-#include <sys/syscall.h>
-
-#ifdef ENABLE_PERF_EVENTS
-#include <linux/perf_event.h>
-
-#define USE_PERF_EVENTS 1
-
-static int read_perf_mmap (MonoProfiler* prof, int cpu);
-#endif
-
-#endif
-
-#define BUFFER_SIZE (4096 * 16)
-
-/* Worst-case size in bytes of a 64-bit value encoded with LEB128. */
-#define LEB128_SIZE 10
-/* 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 int use_zip = 0;
-static int do_report = 0;
-static int do_heap_shot = 0;
-static int max_call_depth = 100;
-static volatile int runtime_inited = 0;
-static int need_helper_thread = 0;
-static int command_port = 0;
-static int heapshot_requested = 0;
-static int sample_type = 0;
-static int sample_freq = 0;
-static int do_mono_sample = 0;
-static int in_shutdown = 0;
-static int do_debug = 0;
-static int do_counters = 0;
-static int do_coverage = 0;
-static gboolean debug_coverage = FALSE;
-static MonoProfileSamplingMode sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
-static int max_allocated_sample_hits;
-
-static gint32 sample_hits;
-static gint32 sample_flushes;
-static gint32 sample_allocations;
-static gint32 buffer_allocations;
-static gint32 thread_starts;
-static gint32 thread_ends;
-static gint32 domain_loads;
-static gint32 domain_unloads;
-static gint32 context_loads;
-static gint32 context_unloads;
-static gint32 assembly_loads;
-static gint32 assembly_unloads;
-static gint32 image_loads;
-static gint32 image_unloads;
-static gint32 class_loads;
-static gint32 class_unloads;
-
-static MonoLinkedListSet profiler_thread_list;
-
-/*
- * file format:
- * [header] [buffer]*
- *
- * The file is composed by a header followed by 0 or more buffers.
- * Each buffer contains events that happened on a thread: for a given thread
- * buffers that appear later in the file are guaranteed to contain events
- * that happened later in time. Buffers from separate threads could be interleaved,
- * though.
- * Buffers are not required to be aligned.
- *
- * header format:
- * [id: 4 bytes] constant value: LOG_HEADER_ID
- * [major: 1 byte] [minor: 1 byte] major and minor version of the log profiler
- * [format: 1 byte] version of the data format for the rest of the file
- * [ptrsize: 1 byte] size in bytes of a pointer in the profiled program
- * [startup time: 8 bytes] time in milliseconds since the unix epoch when the program started
- * [timer overhead: 4 bytes] approximate overhead in nanoseconds of the timer
- * [flags: 4 bytes] file format flags, should be 0 for now
- * [pid: 4 bytes] pid of the profiled process
- * [port: 2 bytes] tcp port for server if != 0
- * [args size: 4 bytes] size of args
- * [args: string] arguments passed to the profiler
- * [arch size: 4 bytes] size of arch
- * [arch: string] architecture the profiler is running on
- * [os size: 4 bytes] size of os
- * [os: string] operating system the profiler is running on
- *
- * The multiple byte integers are in little-endian format.
- *
- * buffer format:
- * [buffer header] [event]*
- * Buffers have a fixed-size header followed by 0 or more bytes of event data.
- * Timing information and other values in the event data are usually stored
- * as uleb128 or sleb128 integers. To save space, as noted for each item below,
- * some data is represented as a difference between the actual value and
- * either the last value of the same type (like for timing information) or
- * as the difference from a value stored in a buffer header.
- *
- * For timing information the data is stored as uleb128, since timing
- * increases in a monotonic way in each thread: the value is the number of
- * nanoseconds to add to the last seen timing data in a buffer. The first value
- * in a buffer will be calculated from the time_base field in the buffer head.
- *
- * Object or heap sizes are stored as uleb128.
- * Pointer differences are stored as sleb128, instead.
- *
- * If an unexpected value is found, the rest of the buffer should be ignored,
- * as generally the later values need the former to be interpreted correctly.
- *
- * buffer header format:
- * [bufid: 4 bytes] constant value: BUF_ID
- * [len: 4 bytes] size of the data following the buffer header
- * [time_base: 8 bytes] time base in nanoseconds since an unspecified epoch
- * [ptr_base: 8 bytes] base value for pointers
- * [obj_base: 8 bytes] base value for object addresses
- * [thread id: 8 bytes] system-specific thread ID (pthread_t for example)
- * [method_base: 8 bytes] base value for MonoMethod pointers
- *
- * event format:
- * [extended info: upper 4 bits] [type: lower 4 bits]
- * [time diff: uleb128] nanoseconds since last timing
- * [data]*
- * The data that follows depends on type and the extended info.
- * Type is one of the enum values in proflog.h: TYPE_ALLOC, TYPE_GC,
- * TYPE_METADATA, TYPE_METHOD, TYPE_EXCEPTION, TYPE_MONITOR, TYPE_HEAP.
- * The extended info bits are interpreted based on type, see
- * each individual event description below.
- * strings are represented as a 0-terminated utf8 sequence.
- *
- * backtrace format:
- * [num: uleb128] number of frames following
- * [frame: sleb128]* mum MonoMethod* as a pointer difference from the last such
- * pointer or the buffer method_base
- *
- * type alloc format:
- * type: TYPE_ALLOC
- * exinfo: flags: TYPE_ALLOC_BT
- * [ptr: sleb128] class as a byte difference from ptr_base
- * [obj: sleb128] object address as a byte difference from obj_base
- * [size: uleb128] size of the object in the heap
- * If the TYPE_ALLOC_BT flag is set, a backtrace follows.
- *
- * 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_FINALIZE_START, TYPE_GC_FINALIZE_END,
- * TYPE_GC_FINALIZE_OBJECT_START, TYPE_GC_FINALIZE_OBJECT_END
- * if exinfo == TYPE_GC_RESIZE
- * [heap_size: uleb128] new heap size
- * if exinfo == TYPE_GC_EVENT
- * [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
- * num is always an even number: the even items are the old
- * addresses, the odd numbers are the respective new object addresses
- * if exinfo == TYPE_GC_HANDLE_CREATED[_BT]
- * [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
- * upper bits reserved as flags
- * [handle: uleb128] GC handle value
- * [objaddr: sleb128] object pointer differences from obj_base
- * If exinfo == TYPE_GC_HANDLE_CREATED_BT, a backtrace follows.
- * if exinfo == TYPE_GC_HANDLE_DESTROYED[_BT]
- * [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
- * 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
- * exinfo: one of: TYPE_END_LOAD, TYPE_END_UNLOAD (optional for TYPE_THREAD and TYPE_DOMAIN)
- * [mtype: byte] metadata type, one of: TYPE_CLASS, TYPE_IMAGE, TYPE_ASSEMBLY, TYPE_DOMAIN,
- * TYPE_THREAD, TYPE_CONTEXT
- * [pointer: sleb128] pointer of the metadata type depending on mtype
- * if mtype == TYPE_CLASS
- * [image: sleb128] MonoImage* as a pointer difference from ptr_base
- * [name: string] full class name
- * if mtype == TYPE_IMAGE
- * [name: string] image file name
- * if mtype == TYPE_ASSEMBLY
- * [name: string] assembly name
- * if mtype == TYPE_DOMAIN && exinfo == 0
- * [name: string] domain friendly name
- * if mtype == TYPE_CONTEXT
- * [domain: sleb128] domain id as pointer
- * if mtype == TYPE_THREAD && exinfo == 0
- * [name: string] thread name
- *
- * type method format:
- * type: TYPE_METHOD
- * exinfo: one of: TYPE_LEAVE, TYPE_ENTER, TYPE_EXC_LEAVE, TYPE_JIT
- * [method: sleb128] MonoMethod* as a pointer difference from the last such
- * pointer or the buffer method_base
- * if exinfo == TYPE_JIT
- * [code address: sleb128] pointer to the native code as a diff from ptr_base
- * [code size: uleb128] size of the generated code
- * [name: string] full method name
- *
- * type exception format:
- * type: TYPE_EXCEPTION
- * exinfo: TYPE_THROW_BT flag or one of: TYPE_CLAUSE
- * if exinfo == TYPE_CLAUSE
- * [clause type: byte] MonoExceptionEnum enum value
- * [clause index: uleb128] index of the current clause
- * [method: sleb128] MonoMethod* as a pointer difference from the last such
- * pointer or the buffer method_base
- * else
- * [object: sleb128] the exception object as a difference from obj_base
- * if exinfo has TYPE_THROW_BT set, a backtrace follows.
- *
- * type runtime format:
- * type: TYPE_RUNTIME
- * exinfo: one of: TYPE_JITHELPER
- * if exinfo == TYPE_JITHELPER
- * [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
- * [name: string] buffer description name
- *
- * type monitor format:
- * type: TYPE_MONITOR
- * exinfo: TYPE_MONITOR_BT flag and one of: MONO_PROFILER_MONITOR_(CONTENTION|FAIL|DONE)
- * [object: sleb128] the lock object as a difference from obj_base
- * if exinfo.low3bits == MONO_PROFILER_MONITOR_CONTENTION
- * If the TYPE_MONITOR_BT flag is set, a backtrace follows.
- *
- * type heap format
- * type: TYPE_HEAP
- * exinfo: one of TYPE_HEAP_START, TYPE_HEAP_END, TYPE_HEAP_OBJECT, TYPE_HEAP_ROOT
- * if exinfo == TYPE_HEAP_OBJECT
- * [object: sleb128] the object as a difference from obj_base
- * [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
- * 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
- * provide additional referenced objects.
- * if exinfo == TYPE_HEAP_ROOT
- * [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: 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 sample format
- * 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: byte] type of sample (SAMPLE_*)
- * [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
- * [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)
- * [name: string] symbol name
- * if exinfo == TYPE_SAMPLE_UBIN
- * [address: sleb128] address where binary has been loaded
- * [offset: uleb128] file offset of mapping (the same file can be mapped multiple times)
- * [size: uleb128] memory size
- * [name: string] binary name
- * if exinfo == TYPE_SAMPLE_COUNTERS_DESC
- * [len: uleb128] number of counters
- * for i = 0 to len
- * [section: uleb128] section of counter
- * if section == MONO_COUNTER_PERFCOUNTERS:
- * [section_name: string] section name of counter
- * [name: string] name 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
- * while true:
- * [index: uleb128] unique index of counter
- * if index == 0:
- * break
- * [type: byte] type of counter value
- * if type == string:
- * if value == null:
- * [0: uleb128] 0 -> value is null
- * else:
- * [1: uleb128] 1 -> value is not null
- * [value: string] counter value
- * else:
- * [value: uleb128/sleb128/double] counter value, can be sleb128, uleb128 or double (determined by using type)
- *
- * type coverage format
- * type: TYPE_COVERAGE
- * exinfo: one of TYPE_COVERAGE_METHOD, TYPE_COVERAGE_STATEMENT, TYPE_COVERAGE_ASSEMBLY, TYPE_COVERAGE_CLASS
- * if exinfo == TYPE_COVERAGE_METHOD
- * [assembly: string] name of assembly
- * [class: string] name of the class
- * [name: string] name of the method
- * [signature: string] the signature of the method
- * [filename: string] the file path of the file that contains this method
- * [token: uleb128] the method token
- * [method_id: uleb128] an ID for this data to associate with the buffers of TYPE_COVERAGE_STATEMENTS
- * [len: uleb128] the number of TYPE_COVERAGE_BUFFERS associated with this method
- * if exinfo == TYPE_COVERAGE_STATEMENTS
- * [method_id: uleb128] an the TYPE_COVERAGE_METHOD buffer to associate this with
- * [offset: uleb128] the il offset relative to the previous offset
- * [counter: uleb128] the counter for this instruction
- * [line: uleb128] the line of filename containing this instruction
- * [column: uleb128] the column containing this instruction
- * if exinfo == TYPE_COVERAGE_ASSEMBLY
- * [name: string] assembly name
- * [guid: string] assembly GUID
- * [filename: string] assembly filename
- * [number_of_methods: uleb128] the number of methods in this assembly
- * [fully_covered: uleb128] the number of fully covered methods
- * [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.
- * if exinfo == TYPE_COVERAGE_CLASS
- * [name: string] assembly name
- * [class: string] class name
- * [number_of_methods: uleb128] the number of methods in this class
- * [fully_covered: uleb128] the number of fully covered methods
- * [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.
- *
- * type meta format:
- * type: TYPE_META
- * exinfo: one of: TYPE_SYNC_POINT
- * 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;
-
- uint64_t time_base;
- uint64_t last_time;
- uintptr_t ptr_base;
- uintptr_t method_base;
- uintptr_t last_method;
- uintptr_t obj_base;
- uintptr_t thread_id;
-
- // Bytes allocated for this LogBuffer
- int size;
-
- // Start of currently unused space in buffer
- unsigned char* cursor;
-
- // Pointer to start-of-structure-plus-size (for convenience)
- unsigned char* buf_end;
-
- // Start of data in buffer. Contents follow "buffer format" described above.
- 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, ...)
-{
-}
-
-/*
- * 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?")
-
-#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;
- char *name;
-};
-
-struct _MonoProfiler {
- FILE* file;
-#if defined (HAVE_SYS_ZLIB)
- gzFile gzfile;
-#endif
- char *args;
- uint64_t startup_time;
- int pipe_output;
- int last_gc_gen_started;
- int command_port;
- int server_socket;
- int pipes [2];
-#ifndef HOST_WIN32
- pthread_t helper_thread;
- pthread_t writer_thread;
- 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;
- mono_mutex_t method_table_mutex;
- volatile gint32 run_dumper_thread;
- MonoLockFreeQueue dumper_queue;
- MonoSemType dumper_queue_sem;
- MonoLockFreeAllocSizeClass sample_size_class;
- MonoLockFreeAllocator sample_allocator;
- MonoLockFreeQueue sample_reuse_queue;
- BinaryObject *binary_objects;
- GPtrArray *coverage_filters;
-};
-
-typedef struct {
- MonoLockFreeQueueNode node;
- GPtrArray *methods;
- LogBuffer *buffer;
-} WriterQueueEntry;
-
-#define WRITER_ENTRY_BLOCK_SIZE (mono_pagesize ())
-
-typedef struct {
- MonoMethod *method;
- MonoJitInfo *ji;
- uint64_t time;
-} MethodInfo;
-
-#ifdef HOST_WIN32
-
-#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 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 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)
-{
- int len = strlen (s) + 1;
- char *p = (char *)malloc (len);
- memcpy (p, s, len);
- return p;
-}
-
-static LogBuffer*
-create_buffer (void)
-{
- LogBuffer* buf = (LogBuffer *)alloc_buffer (BUFFER_SIZE);
-
- InterlockedIncrement (&buffer_allocations);
-
- buf->size = BUFFER_SIZE;
- buf->time_base = current_time ();
- buf->last_time = buf->time_base;
- buf->buf_end = (unsigned char*)buf + buf->size;
- buf->cursor = buf->buf;
- return buf;
-}
-
-static void
-init_buffer_state (MonoProfilerThread *thread)
-{
- 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);
- }
-
- 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 *
-ensure_logbuf_inner (LogBuffer *old, int bytes)
-{
- if (old && old->cursor + bytes + 100 < old->buf_end)
- return old;
-
- LogBuffer *new_ = create_buffer ();
- new_->next = old;
-
- return new_;
-}
-
-// Only valid if init_thread () was called with add_to_lls = FALSE.
-static LogBuffer *
-ensure_logbuf_unsafe (int bytes)
-{
- MonoProfilerThread *thread = PROF_TLS_GET ();
- LogBuffer *old = thread->buffer;
- LogBuffer *new_ = ensure_logbuf_inner (old, bytes);
-
- if (new_ == old)
- return old; // Still enough space.
-
- 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)
-{
- logbuffer->cursor [0] = value;
- logbuffer->cursor++;
- assert (logbuffer->cursor <= logbuffer->buf_end);
-}
-
-static void
-emit_value (LogBuffer *logbuffer, int value)
-{
- encode_uleb128 (value, logbuffer->cursor, &logbuffer->cursor);
- assert (logbuffer->cursor <= logbuffer->buf_end);
-}
-
-static void
-emit_time (LogBuffer *logbuffer, uint64_t value)
-{
- uint64_t tdiff = value - logbuffer->last_time;
- //if (value < logbuffer->last_time)
- // printf ("time went backwards\n");
- //if (tdiff > 1000000)
- // printf ("large time offset: %llu\n", tdiff);
- encode_uleb128 (tdiff, logbuffer->cursor, &logbuffer->cursor);
- /*if (tdiff != decode_uleb128 (p, &p))
- printf ("incorrect encoding: %llu\n", tdiff);*/
- logbuffer->last_time = 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)
-{
- encode_sleb128 (value, logbuffer->cursor, &logbuffer->cursor);
- assert (logbuffer->cursor <= logbuffer->buf_end);
-}
-
-static void
-emit_uvalue (LogBuffer *logbuffer, uint64_t value)
-{
- encode_uleb128 (value, logbuffer->cursor, &logbuffer->cursor);
- assert (logbuffer->cursor <= logbuffer->buf_end);
-}
-
-static void
-emit_ptr (LogBuffer *logbuffer, void *ptr)
-{
- if (!logbuffer->ptr_base)
- logbuffer->ptr_base = (uintptr_t)ptr;
- emit_svalue (logbuffer, (intptr_t)ptr - logbuffer->ptr_base);
- assert (logbuffer->cursor <= logbuffer->buf_end);
-}
-
-static void
-emit_method_inner (LogBuffer *logbuffer, void *method)
-{
- if (!logbuffer->method_base) {
- logbuffer->method_base = (intptr_t)method;
- logbuffer->last_method = (intptr_t)method;
- }
- encode_sleb128 ((intptr_t)((char*)method - (char*)logbuffer->last_method), logbuffer->cursor, &logbuffer->cursor);
- logbuffer->last_method = (intptr_t)method;
- assert (logbuffer->cursor <= logbuffer->buf_end);
-}
-
-/*
-typedef struct {
- MonoMethod *method;
- MonoJitInfo *found;
-} MethodSearch;
-
-static void
-find_method (MonoDomain *domain, void *user_data)
-{
- MethodSearch *search = user_data;
-
- if (search->found)
- return;
-
- MonoJitInfo *ji = mono_get_jit_info_from_method (domain, search->method);
-
- // It could be AOT'd, so we need to get it from the AOT runtime's cache.
- if (!ji) {
- void *ip = mono_aot_get_method (domain, search->method);
-
- // Avoid a slow path in mono_jit_info_table_find ().
- if (ip)
- ji = mono_jit_info_table_find (domain, ip);
- }
-
- if (ji)
- search->found = ji;
-}
-*/
-
-static void
-register_method_local (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *ji)
-{
- if (!mono_conc_hashtable_lookup (prof->method_table, method)) {
- /*
- * FIXME: In some cases, we crash while looking up JIT info for AOT'd methods.
- * This usually happens for static constructors. This code is disabled for now
- * as we don't need this info for anything critical.
- *
- * https://bugzilla.xamarin.com/show_bug.cgi?id=35171
- */
- /*
- if (!ji) {
- MethodSearch search = { method, NULL };
-
- mono_domain_foreach (find_method, &search);
-
- ji = search.found;
- }
- */
-
- /*
- * FIXME: We can't always find JIT info for a generic shared method, especially
- * if we obtained the MonoMethod during an async stack walk. For now, we deal
- * with this by giving the generic shared method name and dummy code start/size
- * information (i.e. zeroes).
- */
- //g_assert (ji);
-
- MethodInfo *info = (MethodInfo *) malloc (sizeof (MethodInfo));
-
- info->method = method;
- info->ji = ji;
- info->time = current_time ();
-
- MonoProfilerThread *thread = PROF_TLS_GET ();
- GPtrArray *arr = thread->methods ? thread->methods : (thread->methods = g_ptr_array_new ());
- g_ptr_array_add (arr, info);
- }
-}
-
-static void
-emit_method (MonoProfiler *prof, LogBuffer *logbuffer, MonoMethod *method)
-{
- register_method_local (prof, method, NULL);
- emit_method_inner (logbuffer, method);
-}
-
-static void
-emit_obj (LogBuffer *logbuffer, void *ptr)
-{
- if (!logbuffer->obj_base)
- logbuffer->obj_base = (uintptr_t)ptr >> 3;
- emit_svalue (logbuffer, ((uintptr_t)ptr >> 3) - logbuffer->obj_base);
- assert (logbuffer->cursor <= logbuffer->buf_end);
-}
-
-static void
-emit_string (LogBuffer *logbuffer, const char *str, size_t size)
-{
- size_t i = 0;
- if (str) {
- for (; i < size; i++) {
- if (str[i] == '\0')
- break;
- emit_byte (logbuffer, str [i]);
- }
- }
- emit_byte (logbuffer, '\0');
-}
-
-static void
-emit_double (LogBuffer *logbuffer, double value)
-{
- int i;
- unsigned char buffer[8];
- memcpy (buffer, &value, 8);
-#if G_BYTE_ORDER == G_BIG_ENDIAN
- for (i = 7; i >= 0; i--)
-#else
- for (i = 0; i < 8; i++)
-#endif
- emit_byte (logbuffer, buffer[i]);
-}
-
-static char*
-write_int16 (char *buf, int32_t value)
-{
- int i;
- for (i = 0; i < 2; ++i) {
- buf [i] = value;
- value >>= 8;
- }
- return buf + 2;
-}
-
-static char*
-write_int32 (char *buf, int32_t value)
-{
- int i;
- for (i = 0; i < 4; ++i) {
- buf [i] = value;
- value >>= 8;
- }
- return buf + 4;
-}
-
-static char*
-write_int64 (char *buf, int64_t value)
-{
- int i;
- for (i = 0; i < 8; ++i) {
- buf [i] = value;
- value >>= 8;
- }
- return buf + 8;
-}
-
-static char *
-write_header_string (char *p, const char *str)
-{
- size_t len = strlen (str) + 1;
-
- p = write_int32 (p, len);
- strcpy (p, str);
-
- return p + len;
-}
-
-static void
-dump_header (MonoProfiler *profiler)
-{
- const char *args = profiler->args;
- const char *arch = mono_config_get_cpu ();
- const char *os = mono_config_get_os ();
-
- char *hbuf = malloc (
- sizeof (gint32) /* header id */ +
- sizeof (gint8) /* major version */ +
- sizeof (gint8) /* minor version */ +
- sizeof (gint8) /* data version */ +
- sizeof (gint8) /* word size */ +
- sizeof (gint64) /* startup time */ +
- sizeof (gint32) /* timer overhead */ +
- sizeof (gint32) /* flags */ +
- sizeof (gint32) /* process id */ +
- sizeof (gint16) /* command port */ +
- sizeof (gint32) + strlen (args) + 1 /* arguments */ +
- sizeof (gint32) + strlen (arch) + 1 /* architecture */ +
- sizeof (gint32) + strlen (os) + 1 /* operating system */
- );
- char *p = hbuf;
-
- p = write_int32 (p, LOG_HEADER_ID);
- *p++ = LOG_VERSION_MAJOR;
- *p++ = LOG_VERSION_MINOR;
- *p++ = LOG_DATA_VERSION;
- *p++ = sizeof (void *);
- p = write_int64 (p, ((uint64_t) time (NULL)) * 1000);
- p = write_int32 (p, get_timer_overhead ());
- p = write_int32 (p, 0); /* flags */
- p = write_int32 (p, process_id ());
- p = write_int16 (p, profiler->command_port);
- p = write_header_string (p, args);
- p = write_header_string (p, arch);
- p = write_header_string (p, os);
-
-#if defined (HAVE_SYS_ZLIB)
- if (profiler->gzfile) {
- gzwrite (profiler->gzfile, hbuf, p - hbuf);
- } else
-#endif
- {
- fwrite (hbuf, p - hbuf, 1, profiler->file);
- fflush (profiler->file);
- }
-
- free (hbuf);
-}
-
-static void
-send_buffer (MonoProfiler *prof, MonoProfilerThread *thread)
-{
- 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);
-
- 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)
-{
- char hbuf [128];
- char *p = hbuf;
-
- if (buf->next)
- dump_buffer (profiler, buf->next);
-
- p = write_int32 (p, BUF_ID);
- p = write_int32 (p, buf->cursor - buf->buf);
- p = write_int64 (p, buf->time_base);
- p = write_int64 (p, buf->ptr_base);
- p = write_int64 (p, buf->obj_base);
- p = write_int64 (p, buf->thread_id);
- p = write_int64 (p, buf->method_base);
-
-#if defined (HAVE_SYS_ZLIB)
- if (profiler->gzfile) {
- gzwrite (profiler->gzfile, hbuf, p - hbuf);
- gzwrite (profiler->gzfile, buf->buf, buf->cursor - buf->buf);
- } else
-#endif
- {
- fwrite (hbuf, p - hbuf, 1, profiler->file);
- fwrite (buf->buf, buf->cursor - buf->buf, 1, profiler->file);
- fflush (profiler->file);
- }
-
- 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)
-{
- if (heapshot_requested)
- mono_gc_collect (mono_gc_max_generation ());
-}
-
-static void counters_init (MonoProfiler *profiler);
-static void counters_sample (MonoProfiler *profiler, uint64_t timestamp);
-
-static void
-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
- * there won't be a thread to process the data.
- *
- * While the runtime isn't initialized, we just accumulate data in the
- * thread local buffer list.
- */
- if (!InterlockedRead (&runtime_inited))
- return;
-
- MonoProfilerThread *thread = PROF_TLS_GET ();
-
- buffer_lock ();
-
- send_buffer (profiler, thread);
- init_buffer_state (thread);
-
- buffer_unlock ();
-}
-
-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 = PROF_TLS_GET ()->buffer;
-
- for (LogBuffer *iter = buf; iter; iter = iter->next)
- iter->thread_id = 0;
-
- 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)
-{
- /* 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 /* klass */ +
- LEB128_SIZE /* size */ +
- LEB128_SIZE /* num */ +
- num * (
- LEB128_SIZE /* offset */ +
- LEB128_SIZE /* ref */
- )
- );
-
- emit_event (logbuffer, TYPE_HEAP_OBJECT | TYPE_HEAP);
- emit_obj (logbuffer, obj);
- emit_ptr (logbuffer, klass);
- emit_value (logbuffer, size);
- emit_value (logbuffer, num);
-
- 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]);
- }
-
- EXIT_LOG;
-
- return 0;
-}
-
-static unsigned int hs_mode_ms = 0;
-static unsigned int hs_mode_gc = 0;
-static unsigned int hs_mode_ondemand = 0;
-static unsigned int gc_count = 0;
-static uint64_t last_hs_time = 0;
-
-static void
-heap_walk (MonoProfiler *profiler)
-{
- if (!do_heap_shot)
- return;
-
- 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 = 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 = TRUE;
-
- if (!do_walk)
- return;
-
- heapshot_requested = 0;
-
- 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);
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */
- );
-
- now = current_time ();
-
- emit_event (logbuffer, TYPE_HEAP_END | TYPE_HEAP);
-
- EXIT_LOG;
-
- last_hs_time = now;
-}
-
-static void
-gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation)
-{
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- BYTE_SIZE /* gc event */ +
- BYTE_SIZE /* generation */
- );
-
- 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++;
- 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);
- 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)
-{
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* new size */
- );
-
- emit_event (logbuffer, TYPE_GC_RESIZE | TYPE_GC);
- emit_value (logbuffer, new_size);
-
- EXIT_LOG;
-}
-
-// If you alter MAX_FRAMES, you may need to alter SAMPLE_BLOCK_SIZE too.
-#define MAX_FRAMES 32
-
-typedef struct {
- int count;
- MonoMethod* methods [MAX_FRAMES];
- int32_t il_offsets [MAX_FRAMES];
- int32_t native_offsets [MAX_FRAMES];
-} FrameData;
-
-static int num_frames = MAX_FRAMES;
-
-static mono_bool
-walk_stack (MonoMethod *method, int32_t native_offset, int32_t il_offset, mono_bool managed, void* data)
-{
- FrameData *frame = (FrameData *)data;
- if (method && frame->count < num_frames) {
- frame->il_offsets [frame->count] = il_offset;
- frame->native_offsets [frame->count] = native_offset;
- frame->methods [frame->count++] = method;
- //printf ("In %d %s at %d (native: %d)\n", frame->count, mono_method_get_name (method), il_offset, native_offset);
- }
- return frame->count == num_frames;
-}
-
-/*
- * a note about stack walks: they can cause more profiler events to fire,
- * so we need to make sure they don't happen after we started emitting an
- * event, hence the collect_bt/emit_bt split.
- */
-static void
-collect_bt (FrameData *data)
-{
- data->count = 0;
- mono_stack_walk_no_il (walk_stack, data);
-}
-
-static void
-emit_bt (MonoProfiler *prof, LogBuffer *logbuffer, FrameData *data)
-{
- /* FIXME: this is actually tons of data and we should
- * just output it the first time and use an id the next
- */
- if (data->count > num_frames)
- printf ("bad num frames: %d\n", data->count);
- 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 (prof, logbuffer, data->methods [--data->count]);
- }
-}
-
-static void
-gc_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
-{
- init_thread (TRUE);
-
- int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces) ? TYPE_ALLOC_BT : 0;
- FrameData data;
- 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);
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* klass */ +
- LEB128_SIZE /* obj */ +
- LEB128_SIZE /* size */ +
- (do_bt ? (
- LEB128_SIZE /* count */ +
- data.count * (
- LEB128_SIZE /* method */
- )
- ) : 0)
- );
-
- 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;
-
- send_if_needed (prof);
-
- process_requests (prof);
-}
-
-static void
-gc_moves (MonoProfiler *prof, void **objects, int num)
-{
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* num */ +
- num * (
- LEB128_SIZE /* object */
- )
- );
-
- emit_event (logbuffer, TYPE_GC_MOVE | TYPE_GC);
- emit_value (logbuffer, num);
-
- for (int i = 0; i < num; ++i)
- emit_obj (logbuffer, objects [i]);
-
- EXIT_LOG;
-}
-
-static void
-gc_roots (MonoProfiler *prof, int num, void **objects, int *root_types, uintptr_t *extra_info)
-{
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* num */ +
- LEB128_SIZE /* collections */ +
- num * (
- LEB128_SIZE /* object */ +
- LEB128_SIZE /* root type */ +
- LEB128_SIZE /* extra info */
- )
- );
-
- emit_event (logbuffer, TYPE_HEAP_ROOT | TYPE_HEAP);
- emit_value (logbuffer, num);
- emit_value (logbuffer, mono_gc_collection_count (mono_gc_max_generation ()));
-
- for (int i = 0; i < num; ++i) {
- emit_obj (logbuffer, objects [i]);
- emit_byte (logbuffer, root_types [i]);
- emit_value (logbuffer, extra_info [i]);
- }
-
- 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;
- FrameData data;
-
- if (do_bt)
- collect_bt (&data);
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* type */ +
- LEB128_SIZE /* handle */ +
- (op == MONO_PROFILER_GC_HANDLE_CREATED ? (
- LEB128_SIZE /* obj */
- ) : 0) +
- (do_bt ? (
- LEB128_SIZE /* count */ +
- data.count * (
- LEB128_SIZE /* method */
- )
- ) : 0)
- );
-
- if (op == MONO_PROFILER_GC_HANDLE_CREATED)
- 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_event (logbuffer, (do_bt ? TYPE_GC_HANDLE_DESTROYED_BT : TYPE_GC_HANDLE_DESTROYED) | TYPE_GC);
- else
- g_assert_not_reached ();
-
- emit_value (logbuffer, type);
- emit_value (logbuffer, handle);
-
- if (op == MONO_PROFILER_GC_HANDLE_CREATED)
- emit_obj (logbuffer, obj);
-
- if (do_bt)
- emit_bt (prof, logbuffer, &data);
-
- 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 char*
-push_nesting (char *p, MonoClass *klass)
-{
- MonoClass *nesting;
- const char *name;
- const char *nspace;
- nesting = mono_class_get_nesting_type (klass);
- if (nesting) {
- p = push_nesting (p, nesting);
- *p++ = '/';
- *p = 0;
- }
- name = mono_class_get_name (klass);
- nspace = mono_class_get_namespace (klass);
- if (*nspace) {
- strcpy (p, nspace);
- p += strlen (nspace);
- *p++ = '.';
- *p = 0;
- }
- strcpy (p, name);
- p += strlen (name);
- return p;
-}
-
-static char*
-type_name (MonoClass *klass)
-{
- char buf [1024];
- char *p;
- push_nesting (buf, klass);
- p = (char *)malloc (strlen (buf) + 1);
- strcpy (p, buf);
- return p;
-}
-
-static void
-image_loaded (MonoProfiler *prof, MonoImage *image, int result)
-{
- if (result != MONO_PROFILE_OK)
- return;
-
- const char *name = mono_image_get_filename (image);
- int nlen = strlen (name) + 1;
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* image */ +
- nlen /* name */
- );
-
- emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
- emit_byte (logbuffer, TYPE_IMAGE);
- emit_ptr (logbuffer, image);
- memcpy (logbuffer->cursor, name, nlen);
- logbuffer->cursor += nlen;
-
- EXIT_LOG;
-
- send_if_needed (prof);
-
- process_requests (prof);
-
- InterlockedIncrement (&image_loads);
-}
-
-static void
-image_unloaded (MonoProfiler *prof, MonoImage *image)
-{
- const char *name = mono_image_get_filename (image);
- int nlen = strlen (name) + 1;
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* image */ +
- nlen /* name */
- );
-
- emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
- emit_byte (logbuffer, TYPE_IMAGE);
- emit_ptr (logbuffer, image);
- memcpy (logbuffer->cursor, name, nlen);
- logbuffer->cursor += nlen;
-
- EXIT_LOG;
-
- send_if_needed (prof);
-
- process_requests (prof);
-
- InterlockedIncrement (&image_unloads);
-}
-
-static void
-assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly, int result)
-{
- if (result != MONO_PROFILE_OK)
- return;
-
- 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 */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* assembly */ +
- nlen /* name */
- );
-
- emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
- emit_byte (logbuffer, TYPE_ASSEMBLY);
- emit_ptr (logbuffer, assembly);
- memcpy (logbuffer->cursor, name, nlen);
- logbuffer->cursor += nlen;
-
- EXIT_LOG;
-
- mono_free (name);
-
- send_if_needed (prof);
-
- process_requests (prof);
-
- InterlockedIncrement (&assembly_loads);
-}
-
-static void
-assembly_unloaded (MonoProfiler *prof, MonoAssembly *assembly)
-{
- 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 */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* assembly */ +
- nlen /* name */
- );
-
- emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
- emit_byte (logbuffer, TYPE_ASSEMBLY);
- emit_ptr (logbuffer, assembly);
- memcpy (logbuffer->cursor, name, nlen);
- logbuffer->cursor += nlen;
-
- EXIT_LOG;
-
- mono_free (name);
-
- send_if_needed (prof);
-
- process_requests (prof);
-
- InterlockedIncrement (&assembly_unloads);
-}
-
-static void
-class_loaded (MonoProfiler *prof, MonoClass *klass, int result)
-{
- 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);
-
- int nlen = strlen (name) + 1;
- MonoImage *image = mono_class_get_image (klass);
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* klass */ +
- LEB128_SIZE /* image */ +
- nlen /* name */
- );
-
- emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
- emit_byte (logbuffer, TYPE_CLASS);
- emit_ptr (logbuffer, klass);
- emit_ptr (logbuffer, image);
- memcpy (logbuffer->cursor, name, nlen);
- logbuffer->cursor += nlen;
-
- EXIT_LOG;
-
- if (runtime_inited)
- mono_free (name);
- else
- g_free (name);
-
- send_if_needed (prof);
-
- process_requests (prof);
-
- InterlockedIncrement (&class_loads);
-}
-
-static void
-class_unloaded (MonoProfiler *prof, MonoClass *klass)
-{
- char *name;
-
- if (InterlockedRead (&runtime_inited))
- name = mono_type_get_name (mono_class_get_type (klass));
- else
- name = type_name (klass);
-
- int nlen = strlen (name) + 1;
- MonoImage *image = mono_class_get_image (klass);
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* klass */ +
- LEB128_SIZE /* image */ +
- nlen /* name */
- );
-
- emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
- emit_byte (logbuffer, TYPE_CLASS);
- emit_ptr (logbuffer, klass);
- emit_ptr (logbuffer, image);
- memcpy (logbuffer->cursor, name, nlen);
- logbuffer->cursor += nlen;
-
- EXIT_LOG;
-
- if (runtime_inited)
- mono_free (name);
- else
- g_free (name);
-
- send_if_needed (prof);
-
- process_requests (prof);
-
- InterlockedIncrement (&class_unloads);
-}
-
-#ifndef DISABLE_HELPER_THREAD
-static void process_method_enter_coverage (MonoProfiler *prof, MonoMethod *method);
-#endif /* DISABLE_HELPER_THREAD */
-
-static void
-method_enter (MonoProfiler *prof, MonoMethod *method)
-{
-#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 */
- );
-
- emit_event (logbuffer, TYPE_ENTER | TYPE_METHOD);
- emit_method (prof, logbuffer, method);
-
- EXIT_LOG;
- }
-
- send_if_needed (prof);
-
- process_requests (prof);
-}
-
-static void
-method_leave (MonoProfiler *prof, MonoMethod *method)
-{
- 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)
-{
- 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
-method_jitted (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *ji, int result)
-{
- if (result != MONO_PROFILE_OK)
- return;
-
- register_method_local (prof, method, ji);
-
- process_requests (prof);
-}
-
-static void
-code_buffer_new (MonoProfiler *prof, void *buffer, int size, MonoProfilerCodeBufferType type, void *data)
-{
- char *name;
- int nlen;
-
- if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
- name = (char *) data;
- nlen = strlen (name) + 1;
- } else {
- name = NULL;
- nlen = 0;
- }
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* buffer */ +
- LEB128_SIZE /* size */ +
- (name ? (
- nlen /* name */
- ) : 0)
- );
-
- 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;
-
- process_requests (prof);
-}
-
-static void
-throw_exc (MonoProfiler *prof, MonoObject *object)
-{
- int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces) ? TYPE_THROW_BT : 0;
- FrameData data;
-
- if (do_bt)
- collect_bt (&data);
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* object */ +
- (do_bt ? (
- LEB128_SIZE /* count */ +
- data.count * (
- LEB128_SIZE /* method */
- )
- ) : 0)
- );
-
- emit_event (logbuffer, do_bt | TYPE_EXCEPTION);
- emit_obj (logbuffer, object);
-
- if (do_bt)
- emit_bt (prof, logbuffer, &data);
-
- EXIT_LOG;
-
- process_requests (prof);
-}
-
-static void
-clause_exc (MonoProfiler *prof, MonoMethod *method, int clause_type, int clause_num)
-{
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- BYTE_SIZE /* clause type */ +
- LEB128_SIZE /* clause num */ +
- LEB128_SIZE /* method */
- );
-
- emit_event (logbuffer, TYPE_EXCEPTION | TYPE_CLAUSE);
- emit_byte (logbuffer, clause_type);
- emit_value (logbuffer, clause_num);
- emit_method (prof, logbuffer, method);
-
- 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;
- FrameData data;
-
- if (do_bt)
- collect_bt (&data);
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* object */ +
- (do_bt ? (
- LEB128_SIZE /* count */ +
- data.count * (
- LEB128_SIZE /* method */
- )
- ) : 0)
- );
-
- emit_event (logbuffer, (event << 4) | do_bt | TYPE_MONITOR);
- emit_obj (logbuffer, object);
-
- if (do_bt)
- emit_bt (profiler, logbuffer, &data);
-
- EXIT_LOG;
-
- process_requests (profiler);
-}
-
-static void
-thread_start (MonoProfiler *prof, uintptr_t tid)
-{
- init_thread (TRUE);
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* tid */
- );
-
- emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
- emit_byte (logbuffer, TYPE_THREAD);
- emit_ptr (logbuffer, (void*) tid);
-
- EXIT_LOG;
-
- send_if_needed (prof);
-
- process_requests (prof);
-
- InterlockedIncrement (&thread_starts);
-}
-
-static void
-thread_end (MonoProfiler *prof, uintptr_t tid)
-{
- ENTER_LOG;
-
- 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);
-
- EXIT_LOG;
-
- // Don't process requests as the thread is detached from the runtime.
-
- remove_thread (prof, PROF_TLS_GET (), TRUE);
-
- InterlockedIncrement (&thread_ends);
-}
-
-static void
-domain_loaded (MonoProfiler *prof, MonoDomain *domain, int result)
-{
- if (result != MONO_PROFILE_OK)
- return;
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* domain id */
- );
-
- emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
- emit_byte (logbuffer, TYPE_DOMAIN);
- emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
-
- EXIT_LOG;
-
- send_if_needed (prof);
-
- process_requests (prof);
-
- InterlockedIncrement (&domain_loads);
-}
-
-static void
-domain_unloaded (MonoProfiler *prof, MonoDomain *domain)
-{
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* domain id */
- );
-
- emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
- emit_byte (logbuffer, TYPE_DOMAIN);
- emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
-
- EXIT_LOG;
-
- send_if_needed (prof);
-
- process_requests (prof);
-
- InterlockedIncrement (&domain_unloads);
-}
-
-static void
-domain_name (MonoProfiler *prof, MonoDomain *domain, const char *name)
-{
- int nlen = strlen (name) + 1;
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* domain id */ +
- nlen /* name */
- );
-
- emit_event (logbuffer, TYPE_METADATA);
- emit_byte (logbuffer, TYPE_DOMAIN);
- emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
- memcpy (logbuffer->cursor, name, nlen);
- logbuffer->cursor += nlen;
-
- 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 */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* context id */ +
- LEB128_SIZE /* domain id */
- );
-
- 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_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
-
- EXIT_LOG;
-
- send_if_needed (prof);
-
- process_requests (prof);
-
- InterlockedIncrement (&context_loads);
-}
-
-static void
-context_unloaded (MonoProfiler *prof, MonoAppContext *context)
-{
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* context id */ +
- LEB128_SIZE /* domain id */
- );
-
- 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_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
-
- EXIT_LOG;
-
- send_if_needed (prof);
-
- process_requests (prof);
-
- InterlockedIncrement (&context_unloads);
-}
-
-static void
-thread_name (MonoProfiler *prof, uintptr_t tid, const char *name)
-{
- int len = strlen (name) + 1;
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* tid */ +
- len /* name */
- );
-
- emit_event (logbuffer, TYPE_METADATA);
- emit_byte (logbuffer, TYPE_THREAD);
- emit_ptr (logbuffer, (void*)tid);
- memcpy (logbuffer->cursor, name, len);
- logbuffer->cursor += len;
-
- EXIT_LOG;
-
- send_if_needed (prof);
-
- process_requests (prof);
-}
-
-typedef struct {
- MonoMethod *method;
- MonoDomain *domain;
- void *base_address;
- int offset;
-} AsyncFrameInfo;
-
-typedef struct {
- MonoLockFreeQueueNode node;
- MonoProfiler *prof;
- uint64_t time;
- uintptr_t tid;
- void *ip;
- int count;
- AsyncFrameInfo frames [MONO_ZERO_LEN_ARRAY];
-} SampleHit;
-
-static mono_bool
-async_walk_stack (MonoMethod *method, MonoDomain *domain, void *base_address, int offset, void *data)
-{
- SampleHit *sample = (SampleHit *) data;
-
- if (sample->count < num_frames) {
- int i = sample->count;
-
- sample->frames [i].method = method;
- sample->frames [i].domain = domain;
- sample->frames [i].base_address = base_address;
- sample->frames [i].offset = offset;
-
- sample->count++;
- }
-
- return sample->count == num_frames;
-}
-
-#define SAMPLE_SLOT_SIZE(FRAMES) (sizeof (SampleHit) + sizeof (AsyncFrameInfo) * (FRAMES - MONO_ZERO_LEN_ARRAY))
-#define SAMPLE_BLOCK_SIZE (mono_pagesize ())
-
-static void
-enqueue_sample_hit (gpointer p)
-{
- SampleHit *sample = p;
-
- mono_lock_free_queue_node_unpoison (&sample->node);
- mono_lock_free_queue_enqueue (&sample->prof->dumper_queue, &sample->node);
- mono_os_sem_post (&sample->prof->dumper_queue_sem);
-
- InterlockedIncrement (&sample_flushes);
-}
-
-static void
-mono_sample_hit (MonoProfiler *profiler, unsigned char *ip, void *context)
-{
- /*
- * Please note: We rely on the runtime loading the profiler with
- * MONO_DL_EAGER (RTLD_NOW) so that references to runtime functions within
- * this function (and its siblings) are resolved when the profiler is
- * loaded. Otherwise, we would potentially invoke the dynamic linker when
- * invoking runtime functions, which is not async-signal-safe.
- */
-
- if (in_shutdown)
- return;
-
- InterlockedIncrement (&sample_hits);
-
- SampleHit *sample = (SampleHit *) mono_lock_free_queue_dequeue (&profiler->sample_reuse_queue);
-
- if (!sample) {
- /*
- * If we're out of reusable sample events and we're not allowed to
- * allocate more, we have no choice but to drop the event.
- */
- if (InterlockedRead (&sample_allocations) >= max_allocated_sample_hits)
- return;
-
- sample = mono_lock_free_alloc (&profiler->sample_allocator);
- sample->prof = profiler;
- mono_lock_free_queue_node_init (&sample->node, TRUE);
-
- InterlockedIncrement (&sample_allocations);
- }
-
- sample->count = 0;
- mono_stack_walk_async_safe (&async_walk_stack, context, sample);
-
- 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 *) sample->tid, (unsigned long long int) ((sample->time - profiler->startup_time) / 10000 / 100));
- len = strlen (buf);
- ign_res (write (2, buf, len));
- }
-
- mono_thread_hazardous_try_free (sample, enqueue_sample_hit);
-}
-
-static uintptr_t *code_pages = 0;
-static int num_code_pages = 0;
-static int size_code_pages = 0;
-#define CPAGE_SHIFT (9)
-#define CPAGE_SIZE (1 << CPAGE_SHIFT)
-#define CPAGE_MASK (~(CPAGE_SIZE - 1))
-#define CPAGE_ADDR(p) ((p) & CPAGE_MASK)
-
-static uintptr_t
-add_code_page (uintptr_t *hash, uintptr_t hsize, uintptr_t page)
-{
- uintptr_t i;
- uintptr_t start_pos;
- start_pos = (page >> CPAGE_SHIFT) % hsize;
- i = start_pos;
- do {
- if (hash [i] && CPAGE_ADDR (hash [i]) == CPAGE_ADDR (page)) {
- return 0;
- } else if (!hash [i]) {
- hash [i] = page;
- return 1;
- }
- /* wrap around */
- if (++i == hsize)
- i = 0;
- } while (i != start_pos);
- /* should not happen */
- printf ("failed code page store\n");
- return 0;
-}
-
-static void
-add_code_pointer (uintptr_t ip)
-{
- uintptr_t i;
- if (num_code_pages * 2 >= size_code_pages) {
- uintptr_t *n;
- uintptr_t old_size = size_code_pages;
- size_code_pages *= 2;
- if (size_code_pages == 0)
- size_code_pages = 16;
- n = (uintptr_t *)calloc (sizeof (uintptr_t) * size_code_pages, 1);
- for (i = 0; i < old_size; ++i) {
- if (code_pages [i])
- add_code_page (n, size_code_pages, code_pages [i]);
- }
- if (code_pages)
- g_free (code_pages);
- code_pages = n;
- }
- num_code_pages += add_code_page (code_pages, size_code_pages, ip & CPAGE_MASK);
-}
-
-/* ELF code crashes on some systems. */
-//#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
-#if 0
-static void
-dump_ubin (const char *filename, uintptr_t load_addr, uint64_t offset, uintptr_t size)
-{
- int len = strlen (filename) + 1;
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* load address */ +
- LEB128_SIZE /* offset */ +
- LEB128_SIZE /* size */ +
- nlen /* file name */
- );
-
- 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)
-{
- int len = strlen (name) + 1;
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* value */ +
- LEB128_SIZE /* size */ +
- len /* name */
- );
-
- 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. */
-//#if defined(ELFMAG0)
-#if 0
-
-#if SIZEOF_VOID_P == 4
-#define ELF_WSIZE 32
-#else
-#define ELF_WSIZE 64
-#endif
-#ifndef ElfW
-#define ElfW(type) _ElfW (Elf, ELF_WSIZE, type)
-#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t)
-#define _ElfW_1(e,w,t) e##w##t
-#endif
-
-static void
-dump_elf_symbols (ElfW(Sym) *symbols, int num_symbols, const char *strtab, void *load_addr)
-{
- int i;
- for (i = 0; i < num_symbols; ++i) {
- const char* sym;
- sym = strtab + symbols [i].st_name;
- if (!symbols [i].st_name || !symbols [i].st_size || (symbols [i].st_info & 0xf) != STT_FUNC)
- continue;
- //printf ("symbol %s at %d\n", sym, symbols [i].st_value);
- dump_usym (sym, (uintptr_t)load_addr + symbols [i].st_value, symbols [i].st_size);
- }
-}
-
-static int
-read_elf_symbols (MonoProfiler *prof, const char *filename, void *load_addr)
-{
- int fd, i;
- void *data;
- struct stat statb;
- uint64_t file_size;
- ElfW(Ehdr) *header;
- ElfW(Shdr) *sheader;
- ElfW(Shdr) *shstrtabh;
- ElfW(Shdr) *symtabh = NULL;
- ElfW(Shdr) *strtabh = NULL;
- ElfW(Sym) *symbols = NULL;
- const char *strtab;
- int num_symbols;
-
- fd = open (filename, O_RDONLY);
- if (fd < 0)
- return 0;
- if (fstat (fd, &statb) != 0) {
- close (fd);
- return 0;
- }
- file_size = statb.st_size;
- data = mmap (NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
- close (fd);
- if (data == MAP_FAILED)
- return 0;
- header = data;
- if (header->e_ident [EI_MAG0] != ELFMAG0 ||
- header->e_ident [EI_MAG1] != ELFMAG1 ||
- header->e_ident [EI_MAG2] != ELFMAG2 ||
- header->e_ident [EI_MAG3] != ELFMAG3 ) {
- munmap (data, file_size);
- return 0;
- }
- sheader = (void*)((char*)data + header->e_shoff);
- shstrtabh = (void*)((char*)sheader + (header->e_shentsize * header->e_shstrndx));
- strtab = (const char*)data + shstrtabh->sh_offset;
- for (i = 0; i < header->e_shnum; ++i) {
- //printf ("section header: %d\n", sheader->sh_type);
- if (sheader->sh_type == SHT_SYMTAB) {
- symtabh = sheader;
- strtabh = (void*)((char*)data + header->e_shoff + sheader->sh_link * header->e_shentsize);
- /*printf ("symtab section header: %d, .strstr: %d\n", i, sheader->sh_link);*/
- break;
- }
- sheader = (void*)((char*)sheader + header->e_shentsize);
- }
- if (!symtabh || !strtabh) {
- munmap (data, file_size);
- return 0;
- }
- strtab = (const char*)data + strtabh->sh_offset;
- num_symbols = symtabh->sh_size / symtabh->sh_entsize;
- symbols = (void*)((char*)data + symtabh->sh_offset);
- dump_elf_symbols (symbols, num_symbols, strtab, load_addr);
- munmap (data, file_size);
- return 1;
-}
-#endif
-
-/* ELF code crashes on some systems. */
-//#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
-#if 0
-static int
-elf_dl_callback (struct dl_phdr_info *info, size_t size, void *data)
-{
- MonoProfiler *prof = data;
- char buf [256];
- const char *filename;
- BinaryObject *obj;
- char *a = (void*)info->dlpi_addr;
- int i, num_sym;
- ElfW(Dyn) *dyn = NULL;
- ElfW(Sym) *symtab = NULL;
- ElfW(Word) *hash_table = NULL;
- ElfW(Ehdr) *header = NULL;
- const char* strtab = NULL;
- for (obj = prof->binary_objects; obj; obj = obj->next) {
- if (obj->addr == a)
- return 0;
- }
- filename = info->dlpi_name;
- if (!filename)
- return 0;
- if (!info->dlpi_addr && !filename [0]) {
- int l = readlink ("/proc/self/exe", buf, sizeof (buf) - 1);
- if (l > 0) {
- buf [l] = 0;
- filename = buf;
- }
- }
- obj = g_calloc (sizeof (BinaryObject), 1);
- obj->addr = (void*)info->dlpi_addr;
- obj->name = pstrdup (filename);
- obj->next = prof->binary_objects;
- prof->binary_objects = obj;
- //printf ("loaded file: %s at %p, segments: %d\n", filename, (void*)info->dlpi_addr, info->dlpi_phnum);
- a = NULL;
- for (i = 0; i < info->dlpi_phnum; ++i) {
- //printf ("segment type %d file offset: %d, size: %d\n", info->dlpi_phdr[i].p_type, info->dlpi_phdr[i].p_offset, info->dlpi_phdr[i].p_memsz);
- if (info->dlpi_phdr[i].p_type == PT_LOAD && !header) {
- header = (ElfW(Ehdr)*)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
- if (header->e_ident [EI_MAG0] != ELFMAG0 ||
- header->e_ident [EI_MAG1] != ELFMAG1 ||
- header->e_ident [EI_MAG2] != ELFMAG2 ||
- header->e_ident [EI_MAG3] != ELFMAG3 ) {
- header = NULL;
- }
- dump_ubin (filename, info->dlpi_addr + info->dlpi_phdr[i].p_vaddr, info->dlpi_phdr[i].p_offset, info->dlpi_phdr[i].p_memsz);
- } else if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
- dyn = (ElfW(Dyn) *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
- }
- }
- if (read_elf_symbols (prof, filename, (void*)info->dlpi_addr))
- return 0;
- if (!info->dlpi_name || !info->dlpi_name[0])
- return 0;
- if (!dyn)
- return 0;
- for (i = 0; dyn [i].d_tag != DT_NULL; ++i) {
- if (dyn [i].d_tag == DT_SYMTAB) {
- if (symtab && do_debug)
- printf ("multiple symtabs: %d\n", i);
- symtab = (ElfW(Sym) *)(a + dyn [i].d_un.d_ptr);
- } else if (dyn [i].d_tag == DT_HASH) {
- hash_table = (ElfW(Word) *)(a + dyn [i].d_un.d_ptr);
- } else if (dyn [i].d_tag == DT_STRTAB) {
- strtab = (const char*)(a + dyn [i].d_un.d_ptr);
- }
- }
- if (!hash_table)
- return 0;
- num_sym = hash_table [1];
- dump_elf_symbols (symtab, num_sym, strtab, (void*)info->dlpi_addr);
- return 0;
-}
-
-static int
-load_binaries (MonoProfiler *prof)
-{
- dl_iterate_phdr (elf_dl_callback, prof);
- return 1;
-}
-#else
-static int
-load_binaries (MonoProfiler *prof)
-{
- return 0;
-}
-#endif
-
-static const char*
-symbol_for (uintptr_t code)
-{
-#ifdef HAVE_DLADDR
- void *ip = (void*)code;
- Dl_info di;
- if (dladdr (ip, &di)) {
- if (di.dli_sname)
- return di.dli_sname;
- } else {
- /* char **names;
- names = backtrace_symbols (&ip, 1);
- if (names) {
- const char* p = names [0];
- g_free (names);
- return p;
- }
- */
- }
-#endif
- return NULL;
-}
-
-static void
-dump_unmanaged_coderefs (MonoProfiler *prof)
-{
- int i;
- const char* last_symbol;
- uintptr_t addr, page_end;
-
- if (load_binaries (prof))
- return;
- for (i = 0; i < size_code_pages; ++i) {
- const char* sym;
- if (!code_pages [i] || code_pages [i] & 1)
- continue;
- last_symbol = NULL;
- addr = CPAGE_ADDR (code_pages [i]);
- page_end = addr + CPAGE_SIZE;
- code_pages [i] |= 1;
- /* we dump the symbols for the whole page */
- for (; addr < page_end; addr += 16) {
- sym = symbol_for (addr);
- if (sym && sym == last_symbol)
- continue;
- last_symbol = sym;
- if (!sym)
- continue;
- dump_usym (sym, addr, 0); /* let's not guess the size */
- //printf ("found symbol at %p: %s\n", (void*)addr, sym);
- }
- }
-}
-
-static int
-mono_cpu_count (void)
-{
-#ifdef PLATFORM_ANDROID
- /* Android tries really hard to save power by powering off CPUs on SMP phones which
- * means the normal way to query cpu count returns a wrong value with userspace API.
- * Instead we use /sys entries to query the actual hardware CPU count.
- */
- int count = 0;
- char buffer[8] = {'\0'};
- int present = open ("/sys/devices/system/cpu/present", O_RDONLY);
- /* Format of the /sys entry is a cpulist of indexes which in the case
- * of present is always of the form "0-(n-1)" when there is more than
- * 1 core, n being the number of CPU cores in the system. Otherwise
- * the value is simply 0
- */
- if (present != -1 && read (present, (char*)buffer, sizeof (buffer)) > 3)
- count = strtol (((char*)buffer) + 2, NULL, 10);
- if (present != -1)
- close (present);
- if (count > 0)
- return count + 1;
-#endif
-
-#if defined(HOST_ARM) || defined (HOST_ARM64)
-
- /* ARM platforms tries really hard to save power by powering off CPUs on SMP phones which
- * means the normal way to query cpu count returns a wrong value with userspace API. */
-
-#ifdef _SC_NPROCESSORS_CONF
- {
- int count = sysconf (_SC_NPROCESSORS_CONF);
- if (count > 0)
- return count;
- }
-#endif
-
-#else
-
-#ifdef HAVE_SCHED_GETAFFINITY
- {
- cpu_set_t set;
- if (sched_getaffinity (getpid (), sizeof (set), &set) == 0)
- return CPU_COUNT (&set);
- }
-#endif
-#ifdef _SC_NPROCESSORS_ONLN
- {
- int count = sysconf (_SC_NPROCESSORS_ONLN);
- if (count > 0)
- return count;
- }
-#endif
-
-#endif /* defined(HOST_ARM) || defined (HOST_ARM64) */
-
-#ifdef USE_SYSCTL
- {
- int count;
- int mib [2];
- size_t len = sizeof (int);
- mib [0] = CTL_HW;
- mib [1] = HW_NCPU;
- if (sysctl (mib, 2, &count, &len, NULL, 0) == 0)
- return count;
- }
-#endif
-#ifdef HOST_WIN32
- {
- SYSTEM_INFO info;
- GetSystemInfo (&info);
- return info.dwNumberOfProcessors;
- }
-#endif
- /* FIXME: warn */
- return 1;
-}
-
-#if USE_PERF_EVENTS
-
-typedef struct {
- int perf_fd;
- unsigned int prev_pos;
- void *mmap_base;
- struct perf_event_mmap_page *page_desc;
-} PerfData ;
-
-static PerfData *perf_data = NULL;
-static int num_perf;
-#define PERF_PAGES_SHIFT 4
-static int num_pages = 1 << PERF_PAGES_SHIFT;
-static unsigned int mmap_mask;
-
-typedef struct {
- struct perf_event_header h;
- uint64_t ip;
- uint32_t pid;
- uint32_t tid;
- uint64_t timestamp;
- uint64_t period;
- uint64_t nframes;
-} PSample;
-
-static int
-perf_event_syscall (struct perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags)
-{
- attr->size = PERF_ATTR_SIZE_VER0;
- //printf ("perf attr size: %d\n", attr->size);
-#if defined(__x86_64__)
- return syscall(/*__NR_perf_event_open*/ 298, attr, pid, cpu, group_fd, flags);
-#elif defined(__i386__)
- return syscall(/*__NR_perf_event_open*/ 336, attr, pid, cpu, group_fd, flags);
-#elif defined(__arm__) || defined (__aarch64__)
- return syscall(/*__NR_perf_event_open*/ 364, attr, pid, cpu, group_fd, flags);
-#else
- return -1;
-#endif
-}
-
-static int
-setup_perf_map (PerfData *perf)
-{
- perf->mmap_base = mmap (NULL, (num_pages + 1) * getpagesize (), PROT_READ|PROT_WRITE, MAP_SHARED, perf->perf_fd, 0);
- if (perf->mmap_base == MAP_FAILED) {
- if (do_debug)
- printf ("failed mmap\n");
- return 0;
- }
- perf->page_desc = perf->mmap_base;
- if (do_debug)
- printf ("mmap version: %d\n", perf->page_desc->version);
- return 1;
-}
-
-static void
-dump_perf_hits (MonoProfiler *prof, void *buf, int size)
-{
- int count = 1;
- int mbt_count = 0;
- void *end = (char*)buf + size;
- int samples = 0;
- int pid = getpid ();
-
- while (buf < end) {
- PSample *s = buf;
- if (s->h.size == 0)
- break;
- if (pid != s->pid) {
- if (do_debug)
- printf ("event for different pid: %d\n", s->pid);
- buf = (char*)buf + s->h.size;
- continue;
- }
- /*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);*/
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* tid */ +
- LEB128_SIZE /* count */ +
- count * (
- LEB128_SIZE /* ip */
- ) +
- LEB128_SIZE /* managed count */ +
- mbt_count * (
- LEB128_SIZE /* method */
- )
- );
-
- 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
- * perf is the kernel's thread ID.
- */
- emit_ptr (logbuffer, 0);
- emit_value (logbuffer, count);
- emit_ptr (logbuffer, (void*)(uintptr_t)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++;
- }
- if (do_debug)
- printf ("dumped %d samples\n", samples);
- dump_unmanaged_coderefs (prof);
-}
-
-/* read events from the ring buffer */
-static int
-read_perf_mmap (MonoProfiler* prof, int cpu)
-{
- PerfData *perf = perf_data + cpu;
- unsigned char *buf;
- unsigned char *data = (unsigned char*)perf->mmap_base + getpagesize ();
- unsigned int head = perf->page_desc->data_head;
- int diff, size;
- unsigned int old;
-
- mono_memory_read_barrier ();
-
- old = perf->prev_pos;
- diff = head - old;
- if (diff < 0) {
- if (do_debug)
- printf ("lost mmap events: old: %d, head: %d\n", old, head);
- old = head;
- }
- size = head - old;
- if ((old & mmap_mask) + size != (head & mmap_mask)) {
- buf = data + (old & mmap_mask);
- size = mmap_mask + 1 - (old & mmap_mask);
- old += size;
- /* size bytes at buf */
- if (do_debug)
- printf ("found1 bytes of events: %d\n", size);
- dump_perf_hits (prof, buf, size);
- }
- buf = data + (old & mmap_mask);
- size = head - old;
- /* size bytes at buf */
- if (do_debug)
- printf ("found bytes of events: %d\n", size);
- dump_perf_hits (prof, buf, size);
- old += size;
- perf->prev_pos = old;
- perf->page_desc->data_tail = old;
- return 0;
-}
-
-static int
-setup_perf_event_for_cpu (PerfData *perf, int cpu)
-{
- struct perf_event_attr attr;
- memset (&attr, 0, sizeof (attr));
- attr.type = PERF_TYPE_HARDWARE;
- switch (sample_type) {
- case SAMPLE_CYCLES: attr.config = PERF_COUNT_HW_CPU_CYCLES; break;
- case SAMPLE_INSTRUCTIONS: attr.config = PERF_COUNT_HW_INSTRUCTIONS; break;
- case SAMPLE_CACHE_MISSES: attr.config = PERF_COUNT_HW_CACHE_MISSES; break;
- case SAMPLE_CACHE_REFS: attr.config = PERF_COUNT_HW_CACHE_REFERENCES; break;
- case SAMPLE_BRANCHES: attr.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS; break;
- case SAMPLE_BRANCH_MISSES: attr.config = PERF_COUNT_HW_BRANCH_MISSES; break;
- default: attr.config = PERF_COUNT_HW_CPU_CYCLES; break;
- }
- attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_PERIOD | PERF_SAMPLE_TIME;
-// attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
- attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID;
- attr.inherit = 1;
- attr.freq = 1;
- attr.sample_freq = sample_freq;
-
- perf->perf_fd = perf_event_syscall (&attr, getpid (), cpu, -1, 0);
- if (do_debug)
- printf ("perf fd: %d, freq: %d, event: %llu\n", perf->perf_fd, sample_freq, attr.config);
- if (perf->perf_fd < 0) {
- if (perf->perf_fd == -EPERM) {
- fprintf (stderr, "Perf syscall denied, do \"echo 1 > /proc/sys/kernel/perf_event_paranoid\" as root to enable.\n");
- } else {
- if (do_debug)
- perror ("open perf event");
- }
- return 0;
- }
- if (!setup_perf_map (perf)) {
- close (perf->perf_fd);
- perf->perf_fd = -1;
- return 0;
- }
- return 1;
-}
-
-static int
-setup_perf_event (void)
-{
- int i, count = 0;
- mmap_mask = num_pages * getpagesize () - 1;
- num_perf = mono_cpu_count ();
- perf_data = g_calloc (num_perf, sizeof (PerfData));
- for (i = 0; i < num_perf; ++i) {
- count += setup_perf_event_for_cpu (perf_data + i, i);
- }
- if (count)
- return 1;
- g_free (perf_data);
- perf_data = NULL;
- return 0;
-}
-
-#endif /* USE_PERF_EVENTS */
-
-#ifndef DISABLE_HELPER_THREAD
-
-typedef struct MonoCounterAgent {
- MonoCounter *counter;
- // MonoCounterAgent specific data :
- void *value;
- size_t value_size;
- short index;
- short emitted;
- struct MonoCounterAgent *next;
-} MonoCounterAgent;
-
-static MonoCounterAgent* counters;
-static gboolean counters_initialized = FALSE;
-static int counters_index = 1;
-static mono_mutex_t counters_mutex;
-
-static void
-counters_add_agent (MonoCounter *counter)
-{
- MonoCounterAgent *agent, *item;
-
- if (!counters_initialized)
- return;
-
- mono_os_mutex_lock (&counters_mutex);
-
- for (agent = counters; agent; agent = agent->next) {
- if (agent->counter == counter) {
- agent->value_size = 0;
- if (agent->value) {
- g_free (agent->value);
- agent->value = NULL;
- }
- mono_os_mutex_unlock (&counters_mutex);
- return;
- }
- }
-
- agent = (MonoCounterAgent *)malloc (sizeof (MonoCounterAgent));
- agent->counter = counter;
- agent->value = NULL;
- agent->value_size = 0;
- agent->index = counters_index++;
- agent->emitted = 0;
- agent->next = NULL;
-
- if (!counters) {
- counters = agent;
- } else {
- item = counters;
- while (item->next)
- item = item->next;
- item->next = agent;
- }
-
- mono_os_mutex_unlock (&counters_mutex);
-}
-
-static mono_bool
-counters_init_foreach_callback (MonoCounter *counter, gpointer data)
-{
- counters_add_agent (counter);
- return TRUE;
-}
-
-static void
-counters_init (MonoProfiler *profiler)
-{
- assert (!counters_initialized);
-
- mono_os_mutex_init (&counters_mutex);
-
- counters_initialized = TRUE;
-
- mono_counters_on_register (&counters_add_agent);
- mono_counters_foreach (counters_init_foreach_callback, NULL);
-}
-
-static void
-counters_emit (MonoProfiler *profiler)
-{
- MonoCounterAgent *agent;
- int len = 0;
- int size =
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* len */
- ;
-
- if (!counters_initialized)
- return;
-
- mono_os_mutex_lock (&counters_mutex);
-
- for (agent = counters; agent; agent = agent->next) {
- if (agent->emitted)
- continue;
-
- size +=
- LEB128_SIZE /* section */ +
- strlen (mono_counter_get_name (agent->counter)) + 1 /* name */ +
- BYTE_SIZE /* type */ +
- BYTE_SIZE /* unit */ +
- BYTE_SIZE /* variance */ +
- LEB128_SIZE /* index */
- ;
-
- len += 1;
- }
-
- if (!len) {
- mono_os_mutex_unlock (&counters_mutex);
- return;
- }
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (size);
-
- emit_event (logbuffer, TYPE_SAMPLE_COUNTERS_DESC | TYPE_SAMPLE);
- emit_value (logbuffer, len);
-
- for (agent = counters; agent; agent = agent->next) {
- const char *name;
-
- if (agent->emitted)
- continue;
-
- name = mono_counter_get_name (agent->counter);
- emit_value (logbuffer, mono_counter_get_section (agent->counter));
- emit_string (logbuffer, name, strlen (name) + 1);
- 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;
-
- mono_os_mutex_unlock (&counters_mutex);
-}
-
-static void
-counters_sample (MonoProfiler *profiler, uint64_t timestamp)
-{
- MonoCounterAgent *agent;
- MonoCounter *counter;
- int type;
- int buffer_size;
- void *buffer;
- int size;
-
- if (!counters_initialized)
- return;
-
- counters_emit (profiler);
-
- buffer_size = 8;
- buffer = g_calloc (1, buffer_size);
-
- mono_os_mutex_lock (&counters_mutex);
-
- size =
- EVENT_SIZE /* event */
- ;
-
- for (agent = counters; agent; agent = agent->next) {
- size +=
- LEB128_SIZE /* index */ +
- BYTE_SIZE /* type */ +
- mono_counter_get_size (agent->counter) /* value */
- ;
- }
-
- size +=
- LEB128_SIZE /* stop marker */
- ;
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (size);
-
- emit_event_time (logbuffer, TYPE_SAMPLE_COUNTERS | TYPE_SAMPLE, timestamp);
-
- for (agent = counters; agent; agent = agent->next) {
- size_t size;
-
- counter = agent->counter;
-
- size = mono_counter_get_size (counter);
- if (size < 0) {
- continue; // FIXME error
- } else if (size > buffer_size) {
- buffer_size = size;
- buffer = g_realloc (buffer, buffer_size);
- }
-
- memset (buffer, 0, buffer_size);
-
- if (mono_counters_sample (counter, buffer, size) < 0)
- continue; // FIXME error
-
- type = mono_counter_get_type (counter);
-
- if (!agent->value) {
- agent->value = g_calloc (1, size);
- agent->value_size = size;
- } else {
- if (type == MONO_COUNTER_STRING) {
- if (strcmp (agent->value, buffer) == 0)
- continue;
- } else {
- if (agent->value_size == size && memcmp (agent->value, buffer, size) == 0)
- continue;
- }
- }
-
- emit_uvalue (logbuffer, agent->index);
- emit_byte (logbuffer, type);
- switch (type) {
- case MONO_COUNTER_INT:
-#if SIZEOF_VOID_P == 4
- case MONO_COUNTER_WORD:
-#endif
- emit_svalue (logbuffer, *(int*)buffer - *(int*)agent->value);
- break;
- case MONO_COUNTER_UINT:
- emit_uvalue (logbuffer, *(guint*)buffer - *(guint*)agent->value);
- break;
- case MONO_COUNTER_TIME_INTERVAL:
- case MONO_COUNTER_LONG:
-#if SIZEOF_VOID_P == 8
- case MONO_COUNTER_WORD:
-#endif
- emit_svalue (logbuffer, *(gint64*)buffer - *(gint64*)agent->value);
- break;
- case MONO_COUNTER_ULONG:
- emit_uvalue (logbuffer, *(guint64*)buffer - *(guint64*)agent->value);
- break;
- case MONO_COUNTER_DOUBLE:
- emit_double (logbuffer, *(double*)buffer);
- break;
- case MONO_COUNTER_STRING:
- if (size == 0) {
- emit_byte (logbuffer, 0);
- } else {
- emit_byte (logbuffer, 1);
- emit_string (logbuffer, (char*)buffer, size);
- }
- break;
- default:
- assert (0);
- }
-
- if (type == MONO_COUNTER_STRING && size > agent->value_size) {
- agent->value = g_realloc (agent->value, size);
- agent->value_size = size;
- }
-
- if (size > 0)
- memcpy (agent->value, buffer, size);
- }
- g_free (buffer);
-
- emit_value (logbuffer, 0);
-
- EXIT_LOG;
-
- mono_os_mutex_unlock (&counters_mutex);
-}
-
-typedef struct _PerfCounterAgent PerfCounterAgent;
-struct _PerfCounterAgent {
- PerfCounterAgent *next;
- int index;
- char *category_name;
- char *name;
- int type;
- gint64 value;
- guint8 emitted;
- guint8 updated;
- guint8 deleted;
-};
-
-static PerfCounterAgent *perfcounters = NULL;
-
-static void
-perfcounters_emit (MonoProfiler *profiler)
-{
- PerfCounterAgent *pcagent;
- int len = 0;
- int size =
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* len */
- ;
-
- for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
- if (pcagent->emitted)
- continue;
-
- size +=
- LEB128_SIZE /* section */ +
- strlen (pcagent->category_name) + 1 /* category name */ +
- strlen (pcagent->name) + 1 /* name */ +
- BYTE_SIZE /* type */ +
- BYTE_SIZE /* unit */ +
- BYTE_SIZE /* variance */ +
- LEB128_SIZE /* index */
- ;
-
- len += 1;
- }
-
- if (!len)
- return;
-
- ENTER_LOG;
-
- 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_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;
-}
-
-static gboolean
-perfcounters_foreach (char *category_name, char *name, unsigned char type, gint64 value, gpointer user_data)
-{
- PerfCounterAgent *pcagent;
-
- for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
- if (strcmp (pcagent->category_name, category_name) != 0 || strcmp (pcagent->name, name) != 0)
- continue;
- if (pcagent->value == value)
- return TRUE;
-
- pcagent->value = value;
- pcagent->updated = 1;
- pcagent->deleted = 0;
- return TRUE;
- }
-
- pcagent = g_new0 (PerfCounterAgent, 1);
- pcagent->next = perfcounters;
- pcagent->index = counters_index++;
- pcagent->category_name = g_strdup (category_name);
- pcagent->name = g_strdup (name);
- pcagent->type = (int) type;
- pcagent->value = value;
- pcagent->emitted = 0;
- pcagent->updated = 1;
- pcagent->deleted = 0;
-
- perfcounters = pcagent;
-
- return TRUE;
-}
-
-static void
-perfcounters_sample (MonoProfiler *profiler, uint64_t timestamp)
-{
- PerfCounterAgent *pcagent;
- int size;
-
- if (!counters_initialized)
- return;
-
- mono_os_mutex_lock (&counters_mutex);
-
- /* mark all perfcounters as deleted, foreach will unmark them as necessary */
- for (pcagent = perfcounters; pcagent; pcagent = pcagent->next)
- pcagent->deleted = 1;
-
- mono_perfcounter_foreach (perfcounters_foreach, perfcounters);
-
- perfcounters_emit (profiler);
-
- size =
- EVENT_SIZE /* event */
- ;
-
- for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
- if (pcagent->deleted || !pcagent->updated)
- continue;
-
- size +=
- LEB128_SIZE /* index */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* value */
- ;
- }
-
- size +=
- LEB128_SIZE /* stop marker */
- ;
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (size);
-
- emit_event_time (logbuffer, TYPE_SAMPLE_COUNTERS | TYPE_SAMPLE, timestamp);
-
- for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
- if (pcagent->deleted || !pcagent->updated)
- continue;
- emit_uvalue (logbuffer, pcagent->index);
- emit_byte (logbuffer, MONO_COUNTER_LONG);
- emit_svalue (logbuffer, pcagent->value);
-
- pcagent->updated = 0;
- }
-
- emit_value (logbuffer, 0);
-
- EXIT_LOG;
-
- mono_os_mutex_unlock (&counters_mutex);
-}
-
-static void
-counters_and_perfcounters_sample (MonoProfiler *prof)
-{
- uint64_t now = current_time ();
-
- counters_sample (prof, now);
- perfcounters_sample (prof, now);
-}
-
-#define COVERAGE_DEBUG(x) if (debug_coverage) {x}
-static mono_mutex_t coverage_mutex;
-static MonoConcurrentHashTable *coverage_methods = NULL;
-static MonoConcurrentHashTable *coverage_assemblies = NULL;
-static MonoConcurrentHashTable *coverage_classes = NULL;
-
-static MonoConcurrentHashTable *filtered_classes = NULL;
-static MonoConcurrentHashTable *entered_methods = NULL;
-static MonoConcurrentHashTable *image_to_methods = NULL;
-static MonoConcurrentHashTable *suppressed_assemblies = NULL;
-static gboolean coverage_initialized = FALSE;
-
-static GPtrArray *coverage_data = NULL;
-static int previous_offset = 0;
-
-typedef struct {
- MonoLockFreeQueueNode node;
- MonoMethod *method;
-} MethodNode;
-
-typedef struct {
- int offset;
- int counter;
- char *filename;
- int line;
- int column;
-} CoverageEntry;
-
-static void
-free_coverage_entry (gpointer data, gpointer userdata)
-{
- CoverageEntry *entry = (CoverageEntry *)data;
- g_free (entry->filename);
- g_free (entry);
-}
-
-static void
-obtain_coverage_for_method (MonoProfiler *prof, const MonoProfileCoverageEntry *entry)
-{
- int offset = entry->iloffset - previous_offset;
- CoverageEntry *e = g_new (CoverageEntry, 1);
-
- previous_offset = entry->iloffset;
-
- e->offset = offset;
- e->counter = entry->counter;
- e->filename = g_strdup(entry->filename ? entry->filename : "");
- e->line = entry->line;
- e->column = entry->col;
-
- g_ptr_array_add (coverage_data, e);
-}
-
-static char *
-parse_generic_type_names(char *name)
-{
- char *new_name, *ret;
- int within_generic_declaration = 0, generic_members = 1;
-
- if (name == NULL || *name == '\0')
- return g_strdup ("");
-
- if (!(ret = new_name = (char *)calloc (strlen (name) * 4 + 1, sizeof (char))))
- return NULL;
-
- do {
- switch (*name) {
- case '<':
- within_generic_declaration = 1;
- break;
-
- case '>':
- within_generic_declaration = 0;
-
- if (*(name - 1) != '<') {
- *new_name++ = '`';
- *new_name++ = '0' + generic_members;
- } else {
- memcpy (new_name, "<>", 8);
- new_name += 8;
- }
-
- generic_members = 0;
- break;
-
- case ',':
- generic_members++;
- break;
-
- default:
- if (!within_generic_declaration)
- *new_name++ = *name;
-
- break;
- }
- } while (*name++);
-
- return ret;
-}
-
-static int method_id;
-static void
-build_method_buffer (gpointer key, gpointer value, gpointer userdata)
-{
- MonoMethod *method = (MonoMethod *)value;
- MonoProfiler *prof = (MonoProfiler *)userdata;
- MonoClass *klass;
- MonoImage *image;
- char *class_name;
- const char *image_name, *method_name, *sig, *first_filename;
- guint i;
-
- previous_offset = 0;
- coverage_data = g_ptr_array_new ();
-
- mono_profiler_coverage_get (prof, method, obtain_coverage_for_method);
-
- klass = mono_method_get_class (method);
- image = mono_class_get_image (klass);
- image_name = mono_image_get_name (image);
-
- sig = mono_signature_get_desc (mono_method_signature (method), TRUE);
- class_name = parse_generic_type_names (mono_type_get_name (mono_class_get_type (klass)));
- method_name = mono_method_get_name (method);
-
- if (coverage_data->len != 0) {
- CoverageEntry *entry = (CoverageEntry *)coverage_data->pdata[0];
- first_filename = entry->filename ? entry->filename : "";
- } else
- first_filename = "";
-
- image_name = image_name ? image_name : "";
- sig = sig ? sig : "";
- method_name = method_name ? method_name : "";
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- strlen (image_name) + 1 /* image name */ +
- strlen (class_name) + 1 /* class name */ +
- strlen (method_name) + 1 /* method name */ +
- strlen (sig) + 1 /* signature */ +
- strlen (first_filename) + 1 /* first file name */ +
- LEB128_SIZE /* token */ +
- LEB128_SIZE /* method id */ +
- LEB128_SIZE /* entries */
- );
-
- 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_string (logbuffer, sig, strlen (sig) + 1);
- emit_string (logbuffer, first_filename, strlen (first_filename) + 1);
-
- emit_uvalue (logbuffer, mono_method_get_token (method));
- emit_uvalue (logbuffer, method_id);
- emit_value (logbuffer, coverage_data->len);
-
- EXIT_LOG;
-
- send_if_needed (prof);
-
- for (i = 0; i < coverage_data->len; i++) {
- CoverageEntry *entry = (CoverageEntry *)coverage_data->pdata[i];
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* method id */ +
- LEB128_SIZE /* offset */ +
- LEB128_SIZE /* counter */ +
- LEB128_SIZE /* line */ +
- LEB128_SIZE /* column */
- );
-
- 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;
-
- send_if_needed (prof);
- }
-
- method_id++;
-
- g_free (class_name);
-
- g_ptr_array_foreach (coverage_data, free_coverage_entry, NULL);
- g_ptr_array_free (coverage_data, TRUE);
- coverage_data = NULL;
-}
-
-/* This empties the queue */
-static guint
-count_queue (MonoLockFreeQueue *queue)
-{
- MonoLockFreeQueueNode *node;
- guint count = 0;
-
- while ((node = mono_lock_free_queue_dequeue (queue))) {
- count++;
- mono_thread_hazardous_try_free (node, free);
- }
-
- return count;
-}
-
-static void
-build_class_buffer (gpointer key, gpointer value, gpointer userdata)
-{
- MonoClass *klass = (MonoClass *)key;
- MonoLockFreeQueue *class_methods = (MonoLockFreeQueue *)value;
- MonoProfiler *prof = (MonoProfiler *)userdata;
- MonoImage *image;
- char *class_name;
- const char *assembly_name;
- int number_of_methods, partially_covered;
- guint fully_covered;
-
- image = mono_class_get_image (klass);
- assembly_name = mono_image_get_name (image);
- class_name = mono_type_get_name (mono_class_get_type (klass));
-
- assembly_name = assembly_name ? assembly_name : "";
- number_of_methods = mono_class_num_methods (klass);
- fully_covered = count_queue (class_methods);
- /* We don't handle partial covered yet */
- partially_covered = 0;
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- strlen (assembly_name) + 1 /* assembly name */ +
- strlen (class_name) + 1 /* class name */ +
- LEB128_SIZE /* no. methods */ +
- LEB128_SIZE /* fully covered */ +
- LEB128_SIZE /* partially covered */
- );
-
- 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;
-
- send_if_needed (prof);
-
- g_free (class_name);
-}
-
-static void
-get_coverage_for_image (MonoImage *image, int *number_of_methods, guint *fully_covered, int *partially_covered)
-{
- MonoLockFreeQueue *image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (image_to_methods, image);
-
- *number_of_methods = mono_image_get_table_rows (image, MONO_TABLE_METHOD);
- if (image_methods)
- *fully_covered = count_queue (image_methods);
- else
- *fully_covered = 0;
-
- // FIXME: We don't handle partially covered yet.
- *partially_covered = 0;
-}
-
-static void
-build_assembly_buffer (gpointer key, gpointer value, gpointer userdata)
-{
- MonoAssembly *assembly = (MonoAssembly *)value;
- MonoProfiler *prof = (MonoProfiler *)userdata;
- MonoImage *image = mono_assembly_get_image (assembly);
- const char *name, *guid, *filename;
- int number_of_methods = 0, partially_covered = 0;
- guint fully_covered = 0;
-
- name = mono_image_get_name (image);
- guid = mono_image_get_guid (image);
- filename = mono_image_get_filename (image);
-
- name = name ? name : "";
- guid = guid ? guid : "";
- filename = filename ? filename : "";
-
- get_coverage_for_image (image, &number_of_methods, &fully_covered, &partially_covered);
-
- ENTER_LOG;
-
- LogBuffer *logbuffer = ensure_logbuf (
- EVENT_SIZE /* event */ +
- strlen (name) + 1 /* name */ +
- strlen (guid) + 1 /* guid */ +
- strlen (filename) + 1 /* file name */ +
- LEB128_SIZE /* no. methods */ +
- LEB128_SIZE /* fully covered */ +
- LEB128_SIZE /* partially covered */
- );
-
- 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;
-
- send_if_needed (prof);
-}
-
-static void
-dump_coverage (MonoProfiler *prof)
-{
- if (!coverage_initialized)
- return;
-
- COVERAGE_DEBUG(fprintf (stderr, "Coverage: Started dump\n");)
- method_id = 0;
-
- mono_os_mutex_lock (&coverage_mutex);
- mono_conc_hashtable_foreach (coverage_assemblies, build_assembly_buffer, prof);
- mono_conc_hashtable_foreach (coverage_classes, build_class_buffer, prof);
- mono_conc_hashtable_foreach (coverage_methods, build_method_buffer, prof);
- mono_os_mutex_unlock (&coverage_mutex);
-
- COVERAGE_DEBUG(fprintf (stderr, "Coverage: Finished dump\n");)
-}
-
-static void
-process_method_enter_coverage (MonoProfiler *prof, MonoMethod *method)
-{
- MonoClass *klass;
- MonoImage *image;
-
- if (!coverage_initialized)
- return;
-
- klass = mono_method_get_class (method);
- image = mono_class_get_image (klass);
-
- if (mono_conc_hashtable_lookup (suppressed_assemblies, (gpointer) mono_image_get_name (image)))
- return;
-
- mono_os_mutex_lock (&coverage_mutex);
- mono_conc_hashtable_insert (entered_methods, method, method);
- mono_os_mutex_unlock (&coverage_mutex);
-}
-
-static MonoLockFreeQueueNode *
-create_method_node (MonoMethod *method)
-{
- MethodNode *node = (MethodNode *)g_malloc (sizeof (MethodNode));
- mono_lock_free_queue_node_init ((MonoLockFreeQueueNode *) node, FALSE);
- node->method = method;
-
- return (MonoLockFreeQueueNode *) node;
-}
-
-static gboolean
-coverage_filter (MonoProfiler *prof, MonoMethod *method)
-{
- MonoError error;
- MonoClass *klass;
- MonoImage *image;
- MonoAssembly *assembly;
- MonoMethodHeader *header;
- guint32 iflags, flags, code_size;
- char *fqn, *classname;
- gboolean has_positive, found;
- MonoLockFreeQueue *image_methods, *class_methods;
- MonoLockFreeQueueNode *node;
-
- if (!coverage_initialized)
- return FALSE;
-
- COVERAGE_DEBUG(fprintf (stderr, "Coverage filter for %s\n", mono_method_get_name (method));)
-
- flags = mono_method_get_flags (method, &iflags);
- if ((iflags & 0x1000 /*METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL*/) ||
- (flags & 0x2000 /*METHOD_ATTRIBUTE_PINVOKE_IMPL*/)) {
- COVERAGE_DEBUG(fprintf (stderr, " Internal call or pinvoke - ignoring\n");)
- return FALSE;
- }
-
- // Don't need to do anything else if we're already tracking this method
- if (mono_conc_hashtable_lookup (coverage_methods, method)) {
- COVERAGE_DEBUG(fprintf (stderr, " Already tracking\n");)
- return TRUE;
- }
-
- klass = mono_method_get_class (method);
- image = mono_class_get_image (klass);
-
- // Don't handle coverage for the core assemblies
- if (mono_conc_hashtable_lookup (suppressed_assemblies, (gpointer) mono_image_get_name (image)) != NULL)
- return FALSE;
-
- if (prof->coverage_filters) {
- /* Check already filtered classes first */
- if (mono_conc_hashtable_lookup (filtered_classes, klass)) {
- COVERAGE_DEBUG(fprintf (stderr, " Already filtered\n");)
- return FALSE;
- }
-
- classname = mono_type_get_name (mono_class_get_type (klass));
-
- fqn = g_strdup_printf ("[%s]%s", mono_image_get_name (image), classname);
-
- COVERAGE_DEBUG(fprintf (stderr, " Looking for %s in filter\n", fqn);)
- // Check positive filters first
- has_positive = FALSE;
- found = FALSE;
- for (guint i = 0; i < prof->coverage_filters->len; ++i) {
- char *filter = (char *)g_ptr_array_index (prof->coverage_filters, i);
-
- if (filter [0] == '+') {
- filter = &filter [1];
-
- COVERAGE_DEBUG(fprintf (stderr, " Checking against +%s ...", filter);)
-
- if (strstr (fqn, filter) != NULL) {
- COVERAGE_DEBUG(fprintf (stderr, "matched\n");)
- found = TRUE;
- } else
- COVERAGE_DEBUG(fprintf (stderr, "no match\n");)
-
- has_positive = TRUE;
- }
- }
-
- if (has_positive && !found) {
- COVERAGE_DEBUG(fprintf (stderr, " Positive match was not found\n");)
-
- mono_os_mutex_lock (&coverage_mutex);
- mono_conc_hashtable_insert (filtered_classes, klass, klass);
- mono_os_mutex_unlock (&coverage_mutex);
- g_free (fqn);
- g_free (classname);
-
- return FALSE;
- }
-
- for (guint i = 0; i < prof->coverage_filters->len; ++i) {
- // FIXME: Is substring search sufficient?
- char *filter = (char *)g_ptr_array_index (prof->coverage_filters, i);
- if (filter [0] == '+')
- continue;
-
- // Skip '-'
- filter = &filter [1];
- COVERAGE_DEBUG(fprintf (stderr, " Checking against -%s ...", filter);)
-
- if (strstr (fqn, filter) != NULL) {
- COVERAGE_DEBUG(fprintf (stderr, "matched\n");)
-
- mono_os_mutex_lock (&coverage_mutex);
- mono_conc_hashtable_insert (filtered_classes, klass, klass);
- mono_os_mutex_unlock (&coverage_mutex);
- g_free (fqn);
- g_free (classname);
-
- return FALSE;
- } else
- COVERAGE_DEBUG(fprintf (stderr, "no match\n");)
-
- }
-
- g_free (fqn);
- g_free (classname);
- }
-
- COVERAGE_DEBUG(fprintf (stderr, " Handling coverage for %s\n", mono_method_get_name (method));)
- header = mono_method_get_header_checked (method, &error);
- mono_error_cleanup (&error);
-
- mono_method_header_get_code (header, &code_size, NULL);
-
- assembly = mono_image_get_assembly (image);
-
- // Need to keep the assemblies around for as long as they are kept in the hashtable
- // Nunit, for example, has a habit of unloading them before the coverage statistics are
- // generated causing a crash. See https://bugzilla.xamarin.com/show_bug.cgi?id=39325
- mono_assembly_addref (assembly);
-
- mono_os_mutex_lock (&coverage_mutex);
- mono_conc_hashtable_insert (coverage_methods, method, method);
- mono_conc_hashtable_insert (coverage_assemblies, assembly, assembly);
- mono_os_mutex_unlock (&coverage_mutex);
-
- image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (image_to_methods, image);
-
- if (image_methods == NULL) {
- image_methods = (MonoLockFreeQueue *)g_malloc (sizeof (MonoLockFreeQueue));
- mono_lock_free_queue_init (image_methods);
- mono_os_mutex_lock (&coverage_mutex);
- mono_conc_hashtable_insert (image_to_methods, image, image_methods);
- mono_os_mutex_unlock (&coverage_mutex);
- }
-
- node = create_method_node (method);
- mono_lock_free_queue_enqueue (image_methods, node);
-
- class_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (coverage_classes, klass);
-
- if (class_methods == NULL) {
- class_methods = (MonoLockFreeQueue *)g_malloc (sizeof (MonoLockFreeQueue));
- mono_lock_free_queue_init (class_methods);
- mono_os_mutex_lock (&coverage_mutex);
- mono_conc_hashtable_insert (coverage_classes, klass, class_methods);
- mono_os_mutex_unlock (&coverage_mutex);
- }
-
- node = create_method_node (method);
- mono_lock_free_queue_enqueue (class_methods, node);
-
- return TRUE;
-}
-
-#define LINE_BUFFER_SIZE 4096
-/* Max file limit of 128KB */
-#define MAX_FILE_SIZE 128 * 1024
-static char *
-get_file_content (FILE *stream)
-{
- char *buffer;
- ssize_t bytes_read;
- long filesize;
- int res, offset = 0;
-
- res = fseek (stream, 0, SEEK_END);
- if (res < 0)
- return NULL;
-
- filesize = ftell (stream);
- if (filesize < 0)
- return NULL;
-
- res = fseek (stream, 0, SEEK_SET);
- if (res < 0)
- return NULL;
-
- if (filesize > MAX_FILE_SIZE)
- return NULL;
-
- buffer = (char *)g_malloc ((filesize + 1) * sizeof (char));
- while ((bytes_read = fread (buffer + offset, 1, LINE_BUFFER_SIZE, stream)) > 0)
- offset += bytes_read;
-
- /* NULL terminate our buffer */
- buffer[filesize] = '\0';
- return buffer;
-}
-
-static char *
-get_next_line (char *contents, char **next_start)
-{
- char *p = contents;
-
- if (p == NULL || *p == '\0') {
- *next_start = NULL;
- return NULL;
- }
-
- while (*p != '\n' && *p != '\0')
- p++;
-
- if (*p == '\n') {
- *p = '\0';
- *next_start = p + 1;
- } else
- *next_start = NULL;
-
- return contents;
-}
-
-static void
-init_suppressed_assemblies (void)
-{
- char *content;
- char *line;
- FILE *sa_file;
-
- suppressed_assemblies = mono_conc_hashtable_new (g_str_hash, g_str_equal);
- sa_file = fopen (SUPPRESSION_DIR "/mono-profiler-log.suppression", "r");
- if (sa_file == NULL)
- return;
-
- /* Don't need to free @content as it is referred to by the lines stored in @suppressed_assemblies */
- content = get_file_content (sa_file);
- if (content == NULL) {
- g_error ("mono-profiler-log.suppression is greater than 128kb - aborting\n");
- }
-
- while ((line = get_next_line (content, &content))) {
- line = g_strchomp (g_strchug (line));
- /* No locking needed as we're doing initialization */
- mono_conc_hashtable_insert (suppressed_assemblies, line, line);
- }
-
- fclose (sa_file);
-}
-
-#endif /* DISABLE_HELPER_THREAD */
-
-static void
-coverage_init (MonoProfiler *prof)
-{
-#ifndef DISABLE_HELPER_THREAD
- assert (!coverage_initialized);
-
- COVERAGE_DEBUG(fprintf (stderr, "Coverage initialized\n");)
-
- mono_os_mutex_init (&coverage_mutex);
- coverage_methods = mono_conc_hashtable_new (NULL, NULL);
- coverage_assemblies = mono_conc_hashtable_new (NULL, NULL);
- coverage_classes = mono_conc_hashtable_new (NULL, NULL);
- filtered_classes = mono_conc_hashtable_new (NULL, NULL);
- entered_methods = mono_conc_hashtable_new (NULL, NULL);
- image_to_methods = mono_conc_hashtable_new (NULL, NULL);
- init_suppressed_assemblies ();
-
- coverage_initialized = TRUE;
-#endif /* DISABLE_HELPER_THREAD */
-}
-
-static void
-unref_coverage_assemblies (gpointer key, gpointer value, gpointer userdata)
-{
- MonoAssembly *assembly = (MonoAssembly *)value;
- mono_assembly_close (assembly);
-}
-
-static void
-free_sample_hit (gpointer p)
-{
- mono_lock_free_free (p, SAMPLE_BLOCK_SIZE);
-}
-
-static void
-cleanup_reusable_samples (MonoProfiler *prof)
-{
- SampleHit *sample;
-
- while ((sample = (SampleHit *) mono_lock_free_queue_dequeue (&prof->sample_reuse_queue)))
- mono_thread_hazardous_try_free (sample, free_sample_hit);
-}
-
-static void
-log_shutdown (MonoProfiler *prof)
-{
- void *res;
-
- in_shutdown = 1;
-#ifndef DISABLE_HELPER_THREAD
- counters_and_perfcounters_sample (prof);
-
- dump_coverage (prof);
-
- if (prof->command_port) {
- char c = 1;
- ign_res (write (prof->pipes [1], &c, 1));
- pthread_join (prof->helper_thread, &res);
- }
-#endif
-#if USE_PERF_EVENTS
- if (perf_data) {
- int i;
- for (i = 0; i < num_perf; ++i)
- read_perf_mmap (prof, i);
- }
-#endif
-
- /*
- * 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);
- pthread_join (prof->dumper_thread, &res);
- mono_os_sem_destroy (&prof->dumper_queue_sem);
-
- InterlockedWrite (&prof->run_writer_thread, 0);
- mono_os_sem_post (&prof->writer_queue_sem);
- pthread_join (prof->writer_thread, &res);
- mono_os_sem_destroy (&prof->writer_queue_sem);
-
- cleanup_reusable_samples (prof);
-
- /*
- * Pump the entire hazard free queue to make sure that anything we allocated
- * in the profiler will be freed. If we don't do this, the runtime could get
- * around to freeing some items after the profiler has been unloaded, which
- * would mean calling into functions in the profiler library, leading to a
- * crash.
- */
- mono_thread_hazardous_try_free_all ();
-
- 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);
-#endif
- if (prof->pipe_output)
- pclose (prof->file);
- else
- fclose (prof->file);
-
- mono_conc_hashtable_destroy (prof->method_table);
- mono_os_mutex_destroy (&prof->method_table_mutex);
-
- if (coverage_initialized) {
- mono_os_mutex_lock (&coverage_mutex);
- mono_conc_hashtable_foreach (coverage_assemblies, unref_coverage_assemblies, prof);
- mono_os_mutex_unlock (&coverage_mutex);
-
- mono_conc_hashtable_destroy (coverage_methods);
- mono_conc_hashtable_destroy (coverage_assemblies);
- mono_conc_hashtable_destroy (coverage_classes);
- mono_conc_hashtable_destroy (filtered_classes);
-
- mono_conc_hashtable_destroy (entered_methods);
- mono_conc_hashtable_destroy (image_to_methods);
- mono_conc_hashtable_destroy (suppressed_assemblies);
- mono_os_mutex_destroy (&coverage_mutex);
- }
-
- PROF_TLS_FREE ();
-
- g_free (prof->args);
- g_free (prof);
-}
-
-static char*
-new_filename (const char* filename)
-{
- time_t t = time (NULL);
- int pid = process_id ();
- char pid_buf [16];
- char time_buf [16];
- char *res, *d;
- const char *p;
- int count_dates = 0;
- int count_pids = 0;
- int s_date, s_pid;
- struct tm *ts;
- for (p = filename; *p; p++) {
- if (*p != '%')
- continue;
- p++;
- if (*p == 't')
- count_dates++;
- else if (*p == 'p')
- count_pids++;
- else if (*p == 0)
- break;
- }
- if (!count_dates && !count_pids)
- return pstrdup (filename);
- snprintf (pid_buf, sizeof (pid_buf), "%d", pid);
- ts = gmtime (&t);
- snprintf (time_buf, sizeof (time_buf), "%d%02d%02d%02d%02d%02d",
- 1900 + ts->tm_year, 1 + ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec);
- s_date = strlen (time_buf);
- s_pid = strlen (pid_buf);
- d = res = (char *)malloc (strlen (filename) + s_date * count_dates + s_pid * count_pids);
- for (p = filename; *p; p++) {
- if (*p != '%') {
- *d++ = *p;
- continue;
- }
- p++;
- if (*p == 't') {
- strcpy (d, time_buf);
- d += s_date;
- continue;
- } else if (*p == 'p') {
- strcpy (d, pid_buf);
- d += s_pid;
- continue;
- } else if (*p == '%') {
- *d++ = '%';
- continue;
- } else if (*p == 0)
- break;
- *d++ = '%';
- *d++ = *p;
- }
- *d = 0;
- return res;
-}
-
-//this is exposed by the JIT, but it's not meant to be a supported API for now.
-extern void mono_threads_attach_tools_thread (void);
-
-#ifndef DISABLE_HELPER_THREAD
-
-static void*
-helper_thread (void* arg)
-{
- MonoProfiler* prof = (MonoProfiler *)arg;
- int command_socket;
- int len;
- char buf [64];
-
- 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) {
- fd_set rfds;
- struct timeval tv;
- int max_fd = -1;
- FD_ZERO (&rfds);
- FD_SET (prof->server_socket, &rfds);
- max_fd = prof->server_socket;
- FD_SET (prof->pipes [0], &rfds);
- if (max_fd < prof->pipes [0])
- max_fd = prof->pipes [0];
- if (command_socket >= 0) {
- FD_SET (command_socket, &rfds);
- if (max_fd < command_socket)
- max_fd = command_socket;
- }
-#if USE_PERF_EVENTS
- if (perf_data) {
- int i;
- for ( i = 0; i < num_perf; ++i) {
- if (perf_data [i].perf_fd < 0)
- continue;
- FD_SET (perf_data [i].perf_fd, &rfds);
- if (max_fd < perf_data [i].perf_fd)
- max_fd = perf_data [i].perf_fd;
- }
- }
-#endif
-
- 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;
- len = select (max_fd + 1, &rfds, NULL, NULL, &tv);
-
- if (len < 0) {
- if (errno == EINTR)
- continue;
-
- g_warning ("Error in proflog server: %s", strerror (errno));
- return NULL;
- }
-
- if (FD_ISSET (prof->pipes [0], &rfds)) {
- char c;
- read (prof->pipes [0], &c, 1);
- if (do_debug)
- fprintf (stderr, "helper shutdown\n");
-#if USE_PERF_EVENTS
- if (perf_data) {
- int i;
- for ( i = 0; i < num_perf; ++i) {
- if (perf_data [i].perf_fd < 0)
- continue;
- if (FD_ISSET (perf_data [i].perf_fd, &rfds))
- read_perf_mmap (prof, i);
- }
- }
-#endif
- safe_send_threadless (prof);
- return NULL;
- }
-#if USE_PERF_EVENTS
- if (perf_data) {
- int i;
- for ( i = 0; i < num_perf; ++i) {
- if (perf_data [i].perf_fd < 0)
- continue;
- if (FD_ISSET (perf_data [i].perf_fd, &rfds)) {
- read_perf_mmap (prof, i);
- send_if_needed_threadless (prof);
- }
- }
- }
-#endif
- if (command_socket >= 0 && FD_ISSET (command_socket, &rfds)) {
- len = read (command_socket, buf, sizeof (buf) - 1);
- if (len < 0)
- continue;
- if (len == 0) {
- close (command_socket);
- command_socket = -1;
- continue;
- }
- buf [len] = 0;
- if (strcmp (buf, "heapshot\n") == 0 && hs_mode_ondemand) {
- // Rely on the finalization callbacks invoking process_requests ().
- heapshot_requested = 1;
- mono_gc_finalize_notify ();
- }
- continue;
- }
- if (!FD_ISSET (prof->server_socket, &rfds)) {
- continue;
- }
- command_socket = accept (prof->server_socket, NULL, NULL);
- if (command_socket < 0)
- continue;
- //fprintf (stderr, "Accepted connection\n");
- }
-
- mono_thread_info_detach ();
-
- return NULL;
-}
-
-static int
-start_helper_thread (MonoProfiler* prof)
-{
- struct sockaddr_in server_address;
- int r;
- socklen_t slen;
- if (pipe (prof->pipes) < 0) {
- fprintf (stderr, "Cannot create pipe\n");
- return 0;
- }
- prof->server_socket = socket (PF_INET, SOCK_STREAM, 0);
- if (prof->server_socket < 0) {
- fprintf (stderr, "Cannot create server socket\n");
- return 0;
- }
- memset (&server_address, 0, sizeof (server_address));
- server_address.sin_family = AF_INET;
- server_address.sin_addr.s_addr = INADDR_ANY;
- server_address.sin_port = htons (prof->command_port);
- if (bind (prof->server_socket, (struct sockaddr *) &server_address, sizeof (server_address)) < 0) {
- fprintf (stderr, "Cannot bind server socket, port: %d: %s\n", prof->command_port, strerror (errno));
- close (prof->server_socket);
- return 0;
- }
- if (listen (prof->server_socket, 1) < 0) {
- fprintf (stderr, "Cannot listen server socket\n");
- close (prof->server_socket);
- return 0;
- }
- slen = sizeof (server_address);
- if (getsockname (prof->server_socket, (struct sockaddr *)&server_address, &slen) == 0) {
- prof->command_port = ntohs (server_address.sin_port);
- /*fprintf (stderr, "Assigned server port: %d\n", prof->command_port);*/
- }
-
- r = pthread_create (&prof->helper_thread, NULL, helper_thread, prof);
- if (r) {
- close (prof->server_socket);
- return 0;
- }
- return 1;
-}
-#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))) {
- if (!entry->methods)
- goto no_methods;
-
- 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);
-
- if (mono_conc_hashtable_lookup (prof->method_table, info->method))
- goto free_info; // This method already has metadata emitted.
-
- /*
- * Other threads use this hash table to get a general
- * idea of whether a method has already been emitted to
- * the stream. Due to the way we add to this table, it
- * can easily happen that multiple threads queue up the
- * same methods, but that's OK since eventually all
- * methods will be in this table and the thread-local
- * method lists will just be empty for the rest of the
- * app's lifetime.
- */
- mono_os_mutex_lock (&prof->method_table_mutex);
- mono_conc_hashtable_insert (prof->method_table, info->method, info->method);
- mono_os_mutex_unlock (&prof->method_table_mutex);
-
- char *name = mono_method_full_name (info->method, 1);
- int nlen = strlen (name) + 1;
- 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;
-
- buf = ensure_logbuf_unsafe (
- EVENT_SIZE /* event */ +
- LEB128_SIZE /* method */ +
- LEB128_SIZE /* start */ +
- LEB128_SIZE /* size */ +
- nlen /* name */
- );
-
- 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 (buf->cursor, name, nlen);
- buf->cursor += nlen;
-
- mono_free (name);
-
- free_info:
- g_free (info);
- }
-
- g_ptr_array_free (entry->methods, TRUE);
-
- 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_writer_entry);
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void *
-writer_thread (void *arg)
-{
- MonoProfiler *prof = (MonoProfiler *)arg;
-
- mono_threads_attach_tools_thread ();
- mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler writer");
-
- 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;
-}
-
-static int
-start_writer_thread (MonoProfiler* prof)
-{
- InterlockedWrite (&prof->run_writer_thread, 1);
-
- return !pthread_create (&prof->writer_thread, NULL, writer_thread, prof);
-}
-
-static void
-reuse_sample_hit (gpointer p)
-{
- SampleHit *sample = p;
-
- mono_lock_free_queue_node_unpoison (&sample->node);
- mono_lock_free_queue_enqueue (&sample->prof->sample_reuse_queue, &sample->node);
-}
-
-static gboolean
-handle_dumper_queue_entry (MonoProfiler *prof)
-{
- SampleHit *sample;
-
- if ((sample = (SampleHit *) mono_lock_free_queue_dequeue (&prof->dumper_queue))) {
- for (int i = 0; i < sample->count; ++i) {
- MonoMethod *method = sample->frames [i].method;
- MonoDomain *domain = sample->frames [i].domain;
- void *address = sample->frames [i].base_address;
-
- if (!method) {
- 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);
-
- if (ji)
- sample->frames [i].method = mono_jit_info_get_method (ji);
- }
- }
-
- LogBuffer *logbuffer = ensure_logbuf_unsafe (
- EVENT_SIZE /* event */ +
- BYTE_SIZE /* type */ +
- LEB128_SIZE /* tid */ +
- LEB128_SIZE /* count */ +
- 1 * (
- LEB128_SIZE /* ip */
- ) +
- LEB128_SIZE /* managed count */ +
- sample->count * (
- LEB128_SIZE /* method */
- )
- );
-
- 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);
-
- // TODO: Actual native unwinding.
- for (int i = 0; i < 1; ++i) {
- emit_ptr (logbuffer, sample->ip);
- add_code_pointer ((uintptr_t) sample->ip);
- }
-
- /* new in data version 6 */
- emit_uvalue (logbuffer, sample->count);
-
- for (int i = 0; i < sample->count; ++i)
- emit_method (prof, logbuffer, sample->frames [i].method);
-
- mono_thread_hazardous_try_free (sample, reuse_sample_hit);
-
- dump_unmanaged_coderefs (prof);
-
- send_if_needed_threadless (prof);
- }
-
- return FALSE;
-}
-
-static void *
-dumper_thread (void *arg)
-{
- MonoProfiler *prof = (MonoProfiler *)arg;
-
- 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);
- deinit_thread (thread);
-
- mono_thread_info_detach ();
-
- return NULL;
-}
-
-static int
-start_dumper_thread (MonoProfiler* prof)
-{
- InterlockedWrite (&prof->run_dumper_thread, 1);
-
- return !pthread_create (&prof->dumper_thread, NULL, dumper_thread, prof);
-}
-
-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))
- profiler->command_port = 0;
- }
-#endif
-
- start_writer_thread (profiler);
- start_dumper_thread (profiler);
-
- 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);
-#endif
- /* ensure the main thread data and startup are available soon */
- safe_send (profiler);
-}
-
-static MonoProfiler*
-create_profiler (const char *args, const char *filename, GPtrArray *filters)
-{
- MonoProfiler *prof;
- char *nf;
- int force_delete = 0;
- prof = (MonoProfiler *)calloc (1, sizeof (MonoProfiler));
-
- prof->args = pstrdup (args);
- prof->command_port = command_port;
- if (filename && *filename == '-') {
- force_delete = 1;
- filename++;
- }
- if (!filename) {
- if (do_report)
- filename = "|mprof-report -";
- else
- filename = "output.mlpd";
- nf = (char*)filename;
- } else {
- nf = new_filename (filename);
- if (do_report) {
- int s = strlen (nf) + 32;
- char *p = (char *)malloc (s);
- snprintf (p, s, "|mprof-report '--out=%s' -", nf);
- g_free (nf);
- nf = p;
- }
- }
- if (*nf == '|') {
- prof->file = popen (nf + 1, "w");
- prof->pipe_output = 1;
- } else if (*nf == '#') {
- int fd = strtol (nf + 1, NULL, 10);
- prof->file = fdopen (fd, "a");
- } else {
- if (force_delete)
- unlink (nf);
- prof->file = fopen (nf, "wb");
- }
- if (!prof->file) {
- fprintf (stderr, "Cannot create profiler output: %s\n", nf);
- exit (1);
- }
-#if defined (HAVE_SYS_ZLIB)
- if (use_zip)
- prof->gzfile = gzdopen (fileno (prof->file), "wb");
-#endif
-#if USE_PERF_EVENTS
- if (sample_type && sample_freq && !do_mono_sample)
- need_helper_thread = setup_perf_event ();
- if (!perf_data) {
- /* FIXME: warn if different freq or sample type */
- do_mono_sample = 1;
- }
-#endif
- if (do_mono_sample) {
- need_helper_thread = 1;
- }
- if (do_counters && !need_helper_thread) {
- need_helper_thread = 1;
- }
-
- /*
- * If you hit this assert while increasing MAX_FRAMES, you need to increase
- * SAMPLE_BLOCK_SIZE as well.
- */
- g_assert (SAMPLE_SLOT_SIZE (MAX_FRAMES) * 2 < LOCK_FREE_ALLOC_SB_USABLE_SIZE (SAMPLE_BLOCK_SIZE));
-
- // FIXME: We should free this stuff too.
- mono_lock_free_allocator_init_size_class (&prof->sample_size_class, SAMPLE_SLOT_SIZE (num_frames), SAMPLE_BLOCK_SIZE);
- mono_lock_free_allocator_init_allocator (&prof->sample_allocator, &prof->sample_size_class, MONO_MEM_ACCOUNT_PROFILER);
-
- mono_lock_free_queue_init (&prof->sample_reuse_queue);
-
-#ifdef DISABLE_HELPER_THREAD
- if (hs_mode_ondemand)
- fprintf (stderr, "Ondemand heapshot unavailable on this arch.\n");
-
- if (do_coverage)
- fprintf (stderr, "Coverage unavailable on this arch.\n");
-
-#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_MEM_ACCOUNT_PROFILER);
-
- mono_lock_free_queue_init (&prof->writer_queue);
- mono_os_sem_init (&prof->writer_queue_sem, 0);
-
- mono_lock_free_queue_init (&prof->dumper_queue);
- mono_os_sem_init (&prof->dumper_queue_sem, 0);
-
- mono_os_mutex_init (&prof->method_table_mutex);
- prof->method_table = mono_conc_hashtable_new (NULL, NULL);
-
- if (do_coverage)
- coverage_init (prof);
- prof->coverage_filters = filters;
-
- prof->startup_time = current_time ();
- return prof;
-}
-
-static void
-usage (int do_exit)
-{
- printf ("Log profiler version %d.%d (format: %d)\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
- printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
- printf ("Options:\n");
- printf ("\thelp show this usage info\n");
- printf ("\t[no]alloc enable/disable recording allocation info\n");
- printf ("\t[no]calls enable/disable recording enter/leave method events\n");
- printf ("\theapshot[=MODE] record heap shot info (by default at each major collection)\n");
- printf ("\t MODE: every XXms milliseconds, every YYgc collections, ondemand\n");
- printf ("\tcounters sample counters every 1s\n");
- printf ("\tsample[=TYPE] use statistical sampling mode (by default cycles/100)\n");
- printf ("\t TYPE: cycles,instr,cacherefs,cachemiss,branches,branchmiss\n");
- printf ("\t TYPE can be followed by /FREQUENCY\n");
- printf ("\ttime=fast use a faster (but more inaccurate) timer\n");
- printf ("\tmaxframes=NUM collect up to NUM stack frames\n");
- printf ("\tcalldepth=NUM ignore method events for call chain depth bigger than NUM\n");
- printf ("\toutput=FILENAME write the data to file FILENAME (-FILENAME to overwrite)\n");
- printf ("\toutput=|PROGRAM write the data to the stdin of PROGRAM\n");
- printf ("\t %%t is subtituted with date and time, %%p with the pid\n");
- printf ("\treport create a report instead of writing the raw data to a file\n");
- printf ("\tzip compress the output data\n");
- printf ("\tport=PORTNUM use PORTNUM for the listening command server\n");
- printf ("\tcoverage enable collection of code coverage data\n");
- printf ("\tcovfilter=ASSEMBLY add an assembly to the code coverage filters\n");
- printf ("\t add a + to include the assembly or a - to exclude it\n");
- printf ("\t filter=-mscorlib\n");
- printf ("\tcovfilter-file=FILE use FILE to generate the list of assemblies to be filtered\n");
- if (do_exit)
- exit (1);
-}
-
-static const char*
-match_option (const char* p, const char *opt, char **rval)
-{
- int len = strlen (opt);
- if (strncmp (p, opt, len) == 0) {
- if (rval) {
- if (p [len] == '=' && p [len + 1]) {
- const char *opt = p + len + 1;
- const char *end = strchr (opt, ',');
- char *val;
- int l;
- if (end == NULL) {
- l = strlen (opt);
- } else {
- l = end - opt;
- }
- val = (char *)malloc (l + 1);
- memcpy (val, opt, l);
- val [l] = 0;
- *rval = val;
- return opt + l;
- }
- if (p [len] == 0 || p [len] == ',') {
- *rval = NULL;
- return p + len + (p [len] == ',');
- }
- usage (1);
- } else {
- if (p [len] == 0)
- return p + len;
- if (p [len] == ',')
- return p + len + 1;
- }
- }
- return p;
-}
-
-typedef struct {
- const char *name;
- int sample_mode;
-} SampleMode;
-
-static const SampleMode sample_modes [] = {
- {"cycles", SAMPLE_CYCLES},
- {"instr", SAMPLE_INSTRUCTIONS},
- {"cachemiss", SAMPLE_CACHE_MISSES},
- {"cacherefs", SAMPLE_CACHE_REFS},
- {"branches", SAMPLE_BRANCHES},
- {"branchmiss", SAMPLE_BRANCH_MISSES},
- {NULL, 0}
-};
-
-static void
-set_sample_mode (char* val, int allow_empty)
-{
- char *end;
- char *maybe_freq = NULL;
- unsigned int count;
- const SampleMode *smode = sample_modes;
-#ifndef USE_PERF_EVENTS
- do_mono_sample = 1;
-#endif
- if (allow_empty && !val) {
- sample_type = SAMPLE_CYCLES;
- sample_freq = 100;
- return;
- }
- if (strcmp (val, "mono") == 0) {
- do_mono_sample = 1;
- sample_type = SAMPLE_CYCLES;
- g_free (val);
- return;
- }
- for (smode = sample_modes; smode->name; smode++) {
- int l = strlen (smode->name);
- if (strncmp (val, smode->name, l) == 0) {
- sample_type = smode->sample_mode;
- maybe_freq = val + l;
- break;
- }
- }
- if (!smode->name)
- usage (1);
- if (*maybe_freq == '/') {
- count = strtoul (maybe_freq + 1, &end, 10);
- if (maybe_freq + 1 == end)
- usage (1);
- sample_freq = count;
- } else if (*maybe_freq != 0) {
- usage (1);
- } else {
- sample_freq = 100;
- }
- g_free (val);
-}
-
-static void
-set_hsmode (char* val, int allow_empty)
-{
- char *end;
- unsigned int count;
- if (allow_empty && !val)
- return;
- if (strcmp (val, "ondemand") == 0) {
- hs_mode_ondemand = 1;
- g_free (val);
- return;
- }
- count = strtoul (val, &end, 10);
- if (val == end)
- usage (1);
- if (strcmp (end, "ms") == 0)
- hs_mode_ms = count;
- else if (strcmp (end, "gc") == 0)
- hs_mode_gc = count;
- else
- usage (1);
- g_free (val);
-}
-
-/*
- * declaration to silence the compiler: this is the entry point that
- * mono will load from the shared library and call.
- */
-extern void
-mono_profiler_startup (const char *desc);
-
-extern void
-mono_profiler_startup_log (const char *desc);
-
-/*
- * this is the entry point that will be used when the profiler
- * is embedded inside the main executable.
- */
-void
-mono_profiler_startup_log (const char *desc)
-{
- mono_profiler_startup (desc);
-}
-
-void
-mono_profiler_startup (const char *desc)
-{
- MonoProfiler *prof;
- GPtrArray *filters = NULL;
- char *filename = NULL;
- const char *p;
- const char *opt;
- int fast_time = 0;
- int calls_enabled = 0;
- int allocs_enabled = 0;
- int only_counters = 0;
- int only_coverage = 0;
- int events = MONO_PROFILE_GC|MONO_PROFILE_ALLOCATIONS|
- MONO_PROFILE_GC_MOVES|MONO_PROFILE_CLASS_EVENTS|MONO_PROFILE_THREADS|
- 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_GC_FINALIZATION;
-
- max_allocated_sample_hits = mono_cpu_count () * 1000;
-
- p = desc;
- if (strncmp (p, "log", 3))
- usage (1);
- p += 3;
- if (*p == ':')
- p++;
- for (; *p; p = opt) {
- char *val;
- if (*p == ',') {
- opt = p + 1;
- continue;
- }
- if ((opt = match_option (p, "help", NULL)) != p) {
- usage (0);
- continue;
- }
- if ((opt = match_option (p, "calls", NULL)) != p) {
- calls_enabled = 1;
- continue;
- }
- if ((opt = match_option (p, "nocalls", NULL)) != p) {
- events &= ~MONO_PROFILE_ENTER_LEAVE;
- nocalls = 1;
- continue;
- }
- if ((opt = match_option (p, "alloc", NULL)) != p) {
- allocs_enabled = 1;
- continue;
- }
- if ((opt = match_option (p, "noalloc", NULL)) != p) {
- events &= ~MONO_PROFILE_ALLOCATIONS;
- continue;
- }
- if ((opt = match_option (p, "time", &val)) != p) {
- if (strcmp (val, "fast") == 0)
- fast_time = 1;
- else if (strcmp (val, "null") == 0)
- fast_time = 2;
- else
- usage (1);
- g_free (val);
- continue;
- }
- if ((opt = match_option (p, "report", NULL)) != p) {
- do_report = 1;
- continue;
- }
- if ((opt = match_option (p, "debug", NULL)) != p) {
- do_debug = 1;
- continue;
- }
- if ((opt = match_option (p, "sampling-real", NULL)) != p) {
- sampling_mode = MONO_PROFILER_STAT_MODE_REAL;
- continue;
- }
- if ((opt = match_option (p, "sampling-process", NULL)) != p) {
- sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
- continue;
- }
- if ((opt = match_option (p, "heapshot", &val)) != p) {
- events &= ~MONO_PROFILE_ALLOCATIONS;
- events &= ~MONO_PROFILE_ENTER_LEAVE;
- nocalls = 1;
- do_heap_shot = 1;
- set_hsmode (val, 1);
- continue;
- }
- if ((opt = match_option (p, "sample", &val)) != p) {
- events &= ~MONO_PROFILE_ALLOCATIONS;
- events &= ~MONO_PROFILE_ENTER_LEAVE;
- nocalls = 1;
- set_sample_mode (val, 1);
- continue;
- }
- if ((opt = match_option (p, "hsmode", &val)) != p) {
- fprintf (stderr, "The hsmode profiler option is obsolete, use heapshot=MODE.\n");
- set_hsmode (val, 0);
- continue;
- }
- if ((opt = match_option (p, "zip", NULL)) != p) {
- use_zip = 1;
- continue;
- }
- if ((opt = match_option (p, "output", &val)) != p) {
- filename = val;
- continue;
- }
- if ((opt = match_option (p, "port", &val)) != p) {
- char *end;
- command_port = strtoul (val, &end, 10);
- g_free (val);
- continue;
- }
- if ((opt = match_option (p, "maxframes", &val)) != p) {
- char *end;
- num_frames = strtoul (val, &end, 10);
- if (num_frames > MAX_FRAMES)
- num_frames = MAX_FRAMES;
- g_free (val);
- notraces = num_frames == 0;
- continue;
- }
- if ((opt = match_option (p, "maxsamples", &val)) != p) {
- char *end;
- max_allocated_sample_hits = strtoul (val, &end, 10);
- if (!max_allocated_sample_hits)
- max_allocated_sample_hits = G_MAXINT32;
- g_free (val);
- continue;
- }
- if ((opt = match_option (p, "calldepth", &val)) != p) {
- char *end;
- max_call_depth = strtoul (val, &end, 10);
- g_free (val);
- continue;
- }
- if ((opt = match_option (p, "counters", NULL)) != p) {
- do_counters = 1;
- continue;
- }
- if ((opt = match_option (p, "countersonly", NULL)) != p) {
- only_counters = 1;
- continue;
- }
- if ((opt = match_option (p, "coverage", NULL)) != p) {
- do_coverage = 1;
- events |= MONO_PROFILE_ENTER_LEAVE;
- debug_coverage = (g_getenv ("MONO_PROFILER_DEBUG_COVERAGE") != NULL);
- continue;
- }
- if ((opt = match_option (p, "onlycoverage", NULL)) != p) {
- only_coverage = 1;
- continue;
- }
- if ((opt = match_option (p, "covfilter-file", &val)) != p) {
- FILE *filter_file;
- char *line, *content;
-
- if (filters == NULL)
- filters = g_ptr_array_new ();
-
- filter_file = fopen (val, "r");
- if (filter_file == NULL) {
- fprintf (stderr, "Unable to open %s\n", val);
- exit (0);
- }
-
- /* Don't need to free content as it is referred to by the lines stored in @filters */
- content = get_file_content (filter_file);
- if (content == NULL)
- fprintf (stderr, "WARNING: %s is greater than 128kb - ignoring\n", val);
-
- while ((line = get_next_line (content, &content)))
- g_ptr_array_add (filters, g_strchug (g_strchomp (line)));
-
- fclose (filter_file);
- continue;
- }
- if ((opt = match_option (p, "covfilter", &val)) != p) {
- if (filters == NULL)
- filters = g_ptr_array_new ();
-
- g_ptr_array_add (filters, val);
- continue;
- }
- if (opt == p) {
- usage (0);
- exit (0);
- }
- }
- if (calls_enabled) {
- events |= MONO_PROFILE_ENTER_LEAVE;
- nocalls = 0;
- }
- if (allocs_enabled)
- events |= MONO_PROFILE_ALLOCATIONS;
- if (only_counters)
- events = 0;
- if (only_coverage)
- events = MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_INS_COVERAGE;
-
- utils_init (fast_time);
-
- PROF_TLS_INIT ();
-
- prof = create_profiler (desc, filename, filters);
- if (!prof) {
- PROF_TLS_FREE ();
- return;
- }
-
- 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_install_class (NULL, class_loaded, class_unloaded, NULL);
- mono_profiler_install_module (NULL, image_loaded, image_unloaded, NULL);
- mono_profiler_install_assembly (NULL, assembly_loaded, assembly_unloaded, NULL);
- mono_profiler_install_thread (thread_start, thread_end);
- mono_profiler_install_thread_name (thread_name);
- mono_profiler_install_enter_leave (method_enter, method_leave);
- mono_profiler_install_jit_end (method_jitted);
- mono_profiler_install_code_buffer_new (code_buffer_new);
- mono_profiler_install_exception (throw_exc, method_exc_leave, clause_exc);
- mono_profiler_install_monitor (monitor_event);
- mono_profiler_install_runtime_initialized (runtime_initialized);
- if (do_coverage)
- mono_profiler_install_coverage_filter (coverage_filter);
-
- if (do_mono_sample && sample_type == SAMPLE_CYCLES && sample_freq && !only_counters) {
- events |= MONO_PROFILE_STATISTICAL;
- mono_profiler_set_statistical_mode (sampling_mode, sample_freq);
- mono_profiler_install_statistical (mono_sample_hit);
- }
-
- mono_profiler_set_events ((MonoProfileFlags)events);
-}
+++ /dev/null
-#ifndef __MONO_PROFLOG_H__
-#define __MONO_PROFLOG_H__
-
-#define BUF_ID 0x4D504C01
-#define LOG_HEADER_ID 0x4D505A01
-#define LOG_VERSION_MAJOR 1
-#define LOG_VERSION_MINOR 0
-#define LOG_DATA_VERSION 13
-/*
- * Changes in major/minor versions:
- * version 1.0: removed sysid field from header
- * added args, arch, os fields to header
- *
- * Changes in data versions:
- * version 2: added offsets in heap walk
- * version 3: added GC roots
- * version 4: added sample/statistical profiling
- * version 5: added counters sampling
- * version 6: added optional backtrace in sampling info
- * version 8: added TYPE_RUNTIME and JIT helpers/trampolines
- * version 9: added MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
- * version 10: added TYPE_COVERAGE
- * version 11: added thread ID to TYPE_SAMPLE_HIT
- added more load/unload events
- unload for class
- unload for image
- load/unload for appdomain
- load/unload for contexts
- load/unload/name for assemblies
- removed TYPE_LOAD_ERR flag (profiler never generated it, now removed from the format itself)
- 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_ALLOC,
- TYPE_GC,
- TYPE_METADATA,
- TYPE_METHOD,
- TYPE_EXCEPTION,
- TYPE_MONITOR,
- TYPE_HEAP,
- TYPE_SAMPLE,
- TYPE_RUNTIME,
- TYPE_COVERAGE,
- TYPE_META,
- /* extended type for TYPE_HEAP */
- TYPE_HEAP_START = 0 << 4,
- TYPE_HEAP_END = 1 << 4,
- TYPE_HEAP_OBJECT = 2 << 4,
- TYPE_HEAP_ROOT = 3 << 4,
- /* extended type for TYPE_METADATA */
- TYPE_END_LOAD = 2 << 4,
- TYPE_END_UNLOAD = 4 << 4,
- /* extended type for TYPE_GC */
- TYPE_GC_EVENT = 1 << 4,
- TYPE_GC_RESIZE = 2 << 4,
- TYPE_GC_MOVE = 3 << 4,
- TYPE_GC_HANDLE_CREATED = 4 << 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_EXC_LEAVE = 3 << 4,
- TYPE_JIT = 4 << 4,
- /* extended type for TYPE_EXCEPTION */
- TYPE_THROW_NO_BT = 0 << 7,
- TYPE_THROW_BT = 1 << 7,
- TYPE_CLAUSE = 1 << 4,
- /* extended type for TYPE_ALLOC */
- TYPE_ALLOC_NO_BT = 0 << 4,
- TYPE_ALLOC_BT = 1 << 4,
- /* extended type for TYPE_MONITOR */
- TYPE_MONITOR_NO_BT = 0 << 7,
- TYPE_MONITOR_BT = 1 << 7,
- /* extended type for TYPE_SAMPLE */
- TYPE_SAMPLE_HIT = 0 << 4,
- TYPE_SAMPLE_USYM = 1 << 4,
- TYPE_SAMPLE_UBIN = 2 << 4,
- TYPE_SAMPLE_COUNTERS_DESC = 3 << 4,
- TYPE_SAMPLE_COUNTERS = 4 << 4,
- /* extended type for TYPE_RUNTIME */
- TYPE_JITHELPER = 1 << 4,
- /* extended type for TYPE_COVERAGE */
- TYPE_COVERAGE_ASSEMBLY = 0 << 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
-};
-
-enum {
- /* metadata type byte for TYPE_METADATA */
- TYPE_CLASS = 1,
- TYPE_IMAGE = 2,
- TYPE_ASSEMBLY = 3,
- TYPE_DOMAIN = 4,
- TYPE_THREAD = 5,
- TYPE_CONTEXT = 6,
-};
-
-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 {
- SAMPLE_CYCLES = 1,
- SAMPLE_INSTRUCTIONS,
- SAMPLE_CACHE_MISSES,
- SAMPLE_CACHE_REFS,
- SAMPLE_BRANCHES,
- SAMPLE_BRANCH_MISSES,
- SAMPLE_LAST
-};
-
-#endif /* __MONO_PROFLOG_H__ */
+++ /dev/null
-/*
- * utils.c: log profiler and reporter utils
- *
- * We have here the minimal needed portability functions: we can't depend
- * on the ones provided by the runtime, since they are internal and,
- * especially mprof-report is an external program.
- * Note also that we don't take a glib/eglib dependency here for mostly
- * the same reason (but also because we need tight control in the profiler
- * over memory allocation, which needs to work with the world stopped).
- *
- * Author:
- * Paolo Molaro (lupus@ximian.com)
- *
- * Copyright 2010 Novell, Inc (http://www.novell.com)
- * Licensed under the MIT license. See LICENSE file in the project root for full license information.
- */
-#include "utils.h"
-#include <stdlib.h>
-#include <time.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#ifdef HOST_WIN32
-#include <windows.h>
-#else
-#include <pthread.h>
-#include <sched.h>
-#endif
-#include <glib.h>
-
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#if HAVE_SYS_MMAN_H
-#include <sys/mman.h>
-#endif
-
-#if defined(__APPLE__)
-#include <mach/mach_time.h>
-#include <stdio.h>
-
-static mach_timebase_info_data_t timebase_info;
-#endif
-
-#ifndef MAP_ANONYMOUS
-#define MAP_ANONYMOUS MAP_ANON
-#endif
-
-#define TICKS_PER_SEC 1000000000LL
-
-#if (defined(TARGET_X86) || defined(TARGET_AMD64)) && defined(__linux__) && defined(HAVE_SCHED_GETCPU)
-#define HAVE_RDTSC 1
-#endif
-
-typedef struct {
- unsigned int timer_count;
- int last_cpu;
- uint64_t last_rdtsc;
- uint64_t last_time;
-} TlsData;
-
-#ifdef HOST_WIN32
-static int tls_data;
-#define DECL_TLS_DATA TlsData *tls; tls = (TlsData *) TlsGetValue (tls_data); if (tls == NULL) { tls = (TlsData *) g_calloc (sizeof (TlsData), 1); TlsSetValue (tls_data, tls); }
-#define TLS_INIT(x) x = TlsAlloc()
-#elif HAVE_KW_THREAD
-static __thread TlsData tls_data;
-#define DECL_TLS_DATA TlsData *tls = &tls_data
-#define TLS_INIT(x)
-#else
-static pthread_key_t tls_data;
-#define DECL_TLS_DATA TlsData *tls; tls = (TlsData *) pthread_getspecific (tls_data); if (tls == NULL) { tls = (TlsData *) g_calloc (sizeof (TlsData), 1); pthread_setspecific (tls_data, tls); }
-#define TLS_INIT(x) pthread_key_create(&x, NULL)
-#endif
-
-#ifdef HOST_WIN32
-static CRITICAL_SECTION log_lock;
-static LARGE_INTEGER pcounter_freq;
-#else
-static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
-#endif
-
-static int timer_overhead = 0;
-static uint64_t time_inc = 0;
-typedef uint64_t (*TimeFunc)(void);
-
-static TimeFunc time_func;
-
-static uint64_t
-clock_time (void)
-{
-#if defined(__APPLE__)
- uint64_t time = mach_absolute_time ();
-
- time *= timebase_info.numer;
- time /= timebase_info.denom;
-
- return time;
-#elif defined(HOST_WIN32)
- LARGE_INTEGER value;
- QueryPerformanceCounter (&value);
- return value.QuadPart * TICKS_PER_SEC / pcounter_freq.QuadPart;
-#elif defined(CLOCK_MONOTONIC)
- struct timespec tspec;
- clock_gettime (CLOCK_MONOTONIC, &tspec);
- return ((uint64_t)tspec.tv_sec * TICKS_PER_SEC + tspec.tv_nsec);
-#else
- struct timeval tv;
- gettimeofday (&tv, NULL);
- return ((uint64_t)tv.tv_sec * TICKS_PER_SEC + tv.tv_usec * 1000);
-#endif
-}
-
-/* must be power of two */
-#define TIME_ADJ 8
-
-static uint64_t
-fast_current_time (void)
-{
- DECL_TLS_DATA;
- if (tls->timer_count++ & (TIME_ADJ - 1)) {
- tls->last_time += time_inc;
- return tls->last_time;
- }
- tls->last_time = clock_time ();
- return tls->last_time;
-}
-
-#if HAVE_RDTSC
-
-#define rdtsc(low,high) \
- __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
-
-static uint64_t
-safe_rdtsc (int *cpu)
-{
- unsigned int low, high;
- int c1 = sched_getcpu ();
- int c2;
- rdtsc (low, high);
- c2 = sched_getcpu ();
- if (c1 != c2) {
- *cpu = -1;
- return 0;
- }
- *cpu = c1;
- return (((uint64_t) high) << 32) + (uint64_t)low;
-}
-
-static double cpu_freq;
-
-static int
-have_rdtsc (void) {
- char buf[256];
- int have_freq = 0;
- int have_flag = 0;
- float val;
- FILE *cpuinfo;
- int cpu = sched_getcpu ();
-
- if (cpu < 0)
- return 0;
-
- if (!(cpuinfo = fopen ("/proc/cpuinfo", "r")))
- return 0;
- while (fgets (buf, sizeof(buf), cpuinfo)) {
- if (sscanf (buf, "cpu MHz : %f", &val) == 1) {
- /*printf ("got mh: %f\n", val);*/
- have_freq = 1;
- cpu_freq = val * 1000000;
- }
- if (strncmp (buf, "flags :", 5) == 0) {
- if (strstr (buf, "constant_tsc")) {
- have_flag = 1;
- /*printf ("have tsc\n");*/
- }
- }
- }
- fclose (cpuinfo);
- return have_flag? have_freq: 0;
-}
-
-static uint64_t
-rdtsc_current_time (void)
-{
- DECL_TLS_DATA;
- if (tls->timer_count++ & (TIME_ADJ*8 - 1)) {
- int cpu;
- uint64_t tsc = safe_rdtsc (&cpu);
- if (cpu != -1 && cpu == tls->last_cpu) {
- int64_t diff = tsc - tls->last_rdtsc;
- uint64_t nsecs;
- if (diff > 0) {
- nsecs = (double)diff/cpu_freq;
- //printf ("%llu cycles: %llu nsecs\n", diff, nsecs);
- return tls->last_time + nsecs;
- } else {
- printf ("tsc went backwards\n");
- }
- } else {
- //printf ("wrong cpu: %d\n", cpu);
- }
- }
- tls->last_time = clock_time ();
- tls->last_rdtsc = safe_rdtsc (&tls->last_cpu);
- return tls->last_time;
-}
-#else
-#define have_rdtsc() 0
-#define rdtsc_current_time fast_current_time
-#endif
-
-static uint64_t
-null_time (void)
-{
- static uint64_t timer = 0;
- return timer++;
-}
-
-void
-utils_init (int fast_time)
-{
- int i;
- uint64_t time_start, time_end;
- TLS_INIT (tls_data);
-#ifdef HOST_WIN32
- InitializeCriticalSection (&log_lock);
- QueryPerformanceFrequency (&pcounter_freq);
-#endif
-#if defined (__APPLE__)
- mach_timebase_info (&timebase_info);
-#endif
-
- if (fast_time > 1) {
- time_func = null_time;
- } else if (fast_time) {
- uint64_t timea;
- uint64_t timeb;
- clock_time ();
- timea = clock_time ();
- timeb = clock_time ();
- time_inc = (timeb - timea) / TIME_ADJ;
- /*printf ("time inc: %llu, timea: %llu, timeb: %llu, diff: %llu\n", time_inc, timea, timeb, timec-timeb);*/
- if (have_rdtsc ())
- time_func = rdtsc_current_time;
- else
- time_func = fast_current_time;
- } else {
- time_func = clock_time;
- }
- time_start = time_func ();
- for (i = 0; i < 256; ++i)
- time_func ();
- time_end = time_func ();
- timer_overhead = (time_end - time_start) / 256;
-}
-
-int
-get_timer_overhead (void)
-{
- return timer_overhead;
-}
-
-uint64_t
-current_time (void)
-{
- return time_func ();
-}
-
-void*
-alloc_buffer (int size)
-{
- void *ptr;
-#ifdef HOST_WIN32
- ptr = VirtualAlloc (NULL, size, MEM_COMMIT, PAGE_READWRITE);
- return ptr;
-#else
- ptr = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
- if (ptr == MAP_FAILED)
- return NULL;
- return ptr;
-#endif
-}
-
-void
-free_buffer (void *buf, int size)
-{
-#ifdef HOST_WIN32
- VirtualFree (buf, 0, MEM_RELEASE);
-#else
- munmap (buf, size);
-#endif
-}
-
-void
-take_lock (void)
-{
-#ifdef HOST_WIN32
- EnterCriticalSection (&log_lock);
-#else
- pthread_mutex_lock (&log_lock);
-#endif
-}
-
-void
-release_lock (void)
-{
-#ifdef HOST_WIN32
- LeaveCriticalSection (&log_lock);
-#else
- pthread_mutex_unlock (&log_lock);
-#endif
-}
-
-void
-encode_uleb128 (uint64_t value, uint8_t *buf, uint8_t **endbuf)
-{
- uint8_t *p = buf;
-
- do {
- uint8_t b = value & 0x7f;
- value >>= 7;
- if (value != 0) /* more bytes to come */
- b |= 0x80;
- *p ++ = b;
- } while (value);
-
- *endbuf = p;
-}
-
-void
-encode_sleb128 (intptr_t value, uint8_t *buf, uint8_t **endbuf)
-{
- int more = 1;
- int negative = (value < 0);
- unsigned int size = sizeof (intptr_t) * 8;
- uint8_t byte;
- uint8_t *p = buf;
-
- while (more) {
- byte = value & 0x7f;
- value >>= 7;
- /* the following is unnecessary if the
- * implementation of >>= uses an arithmetic rather
- * than logical shift for a signed left operand
- */
- if (negative)
- /* sign extend */
- value |= - ((intptr_t)1 <<(size - 7));
- /* sign bit of byte is second high order bit (0x40) */
- if ((value == 0 && !(byte & 0x40)) ||
- (value == -1 && (byte & 0x40)))
- more = 0;
- else
- byte |= 0x80;
- *p ++= byte;
- }
-
- *endbuf = p;
-}
-
-uint64_t
-decode_uleb128 (uint8_t *buf, uint8_t **endbuf)
-{
- uint64_t res = 0;
- int shift = 0;
-
- while (1) {
- uint8_t b = *buf++;
-
- res |= (((uint64_t)(b & 0x7f)) << shift);
- if (!(b & 0x80))
- break;
- shift += 7;
- }
-
- *endbuf = buf;
-
- return res;
-}
-
-intptr_t
-decode_sleb128 (uint8_t *buf, uint8_t **endbuf)
-{
- uint8_t *p = buf;
- intptr_t res = 0;
- int shift = 0;
-
- while (1) {
- uint8_t b = *p;
- p ++;
-
- res = res | (((intptr_t)(b & 0x7f)) << shift);
- shift += 7;
- if (!(b & 0x80)) {
- if (shift < sizeof (intptr_t) * 8 && (b & 0x40))
- res |= - ((intptr_t)1 << shift);
- break;
- }
- }
-
- *endbuf = p;
-
- return res;
-}
-
-uintptr_t
-thread_id (void)
-{
-#ifdef HOST_WIN32
- return (uintptr_t)GetCurrentThreadId ();
-#else
- return (uintptr_t)pthread_self ();
-#endif
-}
-
-uintptr_t
-process_id (void)
-{
-#ifdef HOST_WIN32
- return GetCurrentProcessId ();
-#else
- return (uintptr_t)getpid ();
-#endif
-}
-
+++ /dev/null
-#ifndef __MONO_MPLOG_UTILS_H__
-#define __MONO_MPLOG_UTILS_H__
-
-#include "config.h"
-#include "mono/utils/mono-publib.h"
-
-void utils_init (int fast_time);
-int get_timer_overhead (void);
-uint64_t current_time (void);
-void* alloc_buffer (int size);
-void free_buffer (void *buf, int size);
-void take_lock (void);
-void release_lock (void);
-uintptr_t thread_id (void);
-uintptr_t process_id (void);
-
-void encode_uleb128 (uint64_t value, uint8_t *buf, uint8_t **endbuf);
-void encode_sleb128 (intptr_t value, uint8_t *buf, uint8_t **endbuf);
-uint64_t decode_uleb128 (uint8_t *buf, uint8_t **endbuf);
-intptr_t decode_sleb128 (uint8_t *buf, uint8_t **endbuf);
-
-
-#endif /* __MONO_MPLOG_UTILS_H__ */
-
fprintf (out, "ref-array %lu\n", count_ref_array);
fprintf (out, "vtype-array %lu\n", count_vtype_array);
}
+#else
+#ifdef _MSC_VER
+// Quiet Visual Studio linker warning, LNK4221, in cases when this source file intentional ends up empty.
+void __mono_win32_sgen_layout_stats_quiet_lnk4221(void) {}
#endif
+#endif /* SGEN_OBJECT_LAYOUT_STATISTICS */
#endif
{
return new Thread (() => {
/* Exit bypassing completely the runtime */
- try {
- pthread_exit (IntPtr.Zero);
- } catch (EntryPointNotFoundException) {
- }
-
- try {
- ExitThread (IntPtr.Zero);
- } catch (EntryPointNotFoundException) {
- }
+ if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX)
+ pthread_exit (IntPtr.Zero);
+ else
+ ExitThread (IntPtr.Zero);
});
}
#define __MONO_LOCKFREEALLOC_H__
#include <glib.h>
-
-#include "lock-free-queue.h"
+#include <mono/utils/lock-free-queue.h>
+#include <mono/utils/mono-mmap.h>
typedef struct {
MonoLockFreeQueue partial;
#define MONO_LLVM_INTERNAL
#endif
-#if HAVE_DEPRECATED
-#define MONO_DEPRECATED __attribute__ ((deprecated))
-#else
-#define MONO_DEPRECATED
-#endif
-
#ifdef __GNUC__
#define MONO_ALWAYS_INLINE __attribute__((always_inline))
#elif defined(_MSC_VER)
static inline void
mono_os_sem_init (MonoSemType *sem, int value)
{
+#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
*sem = CreateSemaphore (NULL, value, 0x7FFFFFFF, NULL);
+#else
+ *sem = CreateSemaphoreEx (NULL, value, 0x7FFFFFFF, NULL, 0, SEMAPHORE_ALL_ACCESS);
+#endif
+
if (G_UNLIKELY (*sem == NULL))
g_error ("%s: CreateSemaphore failed with error %d", __func__, GetLastError ());
}
#define MONO_RT_EXTERNAL_ONLY
#endif /* MONO_INSIDE_RUNTIME */
+#ifdef __GNUC__
+#define _MONO_DEPRECATED __attribute__ ((deprecated))
+#elif defined (_MSC_VER)
+#define _MONO_DEPRECATED __declspec (deprecated)
+#else
+#define _MONO_DEPRECATED
+#endif
+
+#define MONO_DEPRECATED MONO_API MONO_RT_EXTERNAL_ONLY _MONO_DEPRECATED
MONO_END_DECLS
#endif
}
+gboolean
+mono_native_thread_join (MonoNativeThreadId tid)
+{
+ void *res;
+
+ return !pthread_join (tid, &res);
+}
+
void
mono_threads_platform_set_exited (gpointer handle)
{
return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
}
+gboolean
+mono_native_thread_join (MonoNativeThreadId tid)
+{
+ HANDLE handle;
+
+ if (!(handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid)))
+ return FALSE;
+
+ DWORD res = WaitForSingleObject (handle, INFINITE);
+
+ CloseHandle (handle);
+
+ return res != WAIT_FAILED;
+}
+
#if HAVE_DECL___READFSDWORD==0
static MONO_ALWAYS_INLINE unsigned long long
__readfsdword (unsigned long offset)
MONO_API MonoNativeThreadId
mono_native_thread_id_get (void);
-gboolean
+MONO_API gboolean
mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2);
-gboolean
+MONO_API gboolean
mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg);
MONO_API void
mono_native_thread_set_name (MonoNativeThreadId tid, const char *name);
+MONO_API gboolean
+mono_native_thread_join (MonoNativeThreadId tid);
+
/*Mach specific internals */
void mono_threads_init_dead_letter (void);
void mono_threads_install_dead_letter (void);
/al
/al1
/al2
+/btls-cert-sync
/caspol
/cert-sync
/cert2spc
prj2make$(SCRIPT_SUFFIX) \
soapsuds$(SCRIPT_SUFFIX) \
caspol$(SCRIPT_SUFFIX) \
+ btls-cert-sync$(SCRIPT_SUFFIX) \
cert-sync$(SCRIPT_SUFFIX) \
cert2spc$(SCRIPT_SUFFIX) \
certmgr$(SCRIPT_SUFFIX) \
if [[ ${CI_TAGS} == *'monolite'* ]]; then make get-monolite-latest; fi
${TESTCMD} --label=make --timeout=300m --fatal make -j4 -w V=1
-if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]];
- then
- exit 0
- # we don't run the test suite on Windows PRs, we just ensure the build succeeds, so end here
-fi
if [[ ${CI_TAGS} == *'acceptance-tests'* ]];
then
${TESTCMD} --label=profiler --timeout=30m make -w -C mono/profiler -k check
${TESTCMD} --label=compiler --timeout=30m make -w -C mcs/tests run-test
${TESTCMD} --label=compiler-errors --timeout=30m make -w -C mcs/errors run-test
-${TESTCMD} --label=System --timeout=10m make -w -C mcs/class/System run-test
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=System --skip; else ${TESTCMD} --label=System --timeout=10m make -w -C mcs/class/System run-test; fi
${TESTCMD} --label=System.XML --timeout=5m make -w -C mcs/class/System.XML run-test
${TESTCMD} --label=Mono.Security --timeout=5m make -w -C mcs/class/Mono.Security run-test
-${TESTCMD} --label=System.Security --timeout=5m make -w -C mcs/class/System.Security run-test;
-${TESTCMD} --label=System.Drawing --timeout=5m make -w -C mcs/class/System.Drawing run-test
-if [[ ${label} == osx-* ]]
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=System.Security --skip; else ${TESTCMD} --label=System.Security --timeout=5m make -w -C mcs/class/System.Security run-test; fi
+if [[ ${label} == w* ]]
+then ${TESTCMD} --label=System.Drawing --skip;
+else ${TESTCMD} --label=System.Drawing --timeout=5m make -w -C mcs/class/System.Drawing run-test
+fi
+if [[ ${label} == osx-* ]] || [[ ${label} == w* ]]
then ${TESTCMD} --label=Windows.Forms --skip;
else ${TESTCMD} --label=Windows.Forms --timeout=5m make -w -C mcs/class/System.Windows.Forms run-test
fi
${TESTCMD} --label=System.Data --timeout=5m make -w -C mcs/class/System.Data run-test
${TESTCMD} --label=System.Data.OracleClient --timeout=5m make -w -C mcs/class/System.Data.OracleClient run-test;
${TESTCMD} --label=System.Design --timeout=5m make -w -C mcs/class/System.Design run-test;
-${TESTCMD} --label=Mono.Posix --timeout=5m make -w -C mcs/class/Mono.Posix run-test
-${TESTCMD} --label=System.Web --timeout=30m make -w -C mcs/class/System.Web run-test
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=Mono.Posix --skip; else ${TESTCMD} --label=Mono.Posix --timeout=5m make -w -C mcs/class/Mono.Posix run-test; fi
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=System.Web --skip; else ${TESTCMD} --label=System.Web --timeout=30m make -w -C mcs/class/System.Web run-test; fi
${TESTCMD} --label=System.Web.Services --timeout=5m make -w -C mcs/class/System.Web.Services run-test
${TESTCMD} --label=System.Runtime.SFS --timeout=5m make -w -C mcs/class/System.Runtime.Serialization.Formatters.Soap run-test;
-${TESTCMD} --label=System.Runtime.Remoting --timeout=5m make -w -C mcs/class/System.Runtime.Remoting run-test;
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=System.Runtime.Remoting --skip; else ${TESTCMD} --label=System.Runtime.Remoting --timeout=5m make -w -C mcs/class/System.Runtime.Remoting run-test; fi
${TESTCMD} --label=Cscompmgd --timeout=5m make -w -C mcs/class/Cscompmgd run-test;
${TESTCMD} --label=Commons.Xml.Relaxng --timeout=5m make -w -C mcs/class/Commons.Xml.Relaxng run-test;
-${TESTCMD} --label=System.ServiceProcess --timeout=5m make -w -C mcs/class/System.ServiceProcess run-test;
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=System.ServiceProcess --skip; else ${TESTCMD} --label=System.ServiceProcess --timeout=5m make -w -C mcs/class/System.ServiceProcess run-test; fi
${TESTCMD} --label=I18N.CJK --timeout=5m make -w -C mcs/class/I18N/CJK run-test
${TESTCMD} --label=I18N.West --timeout=5m make -w -C mcs/class/I18N/West run-test
${TESTCMD} --label=I18N.MidEast --timeout=5m make -w -C mcs/class/I18N/MidEast run-test
${TESTCMD} --label=System.DirectoryServices --timeout=5m make -w -C mcs/class/System.DirectoryServices run-test
-${TESTCMD} --label=Microsoft.Build.Engine --timeout=5m make -w -C mcs/class/Microsoft.Build.Engine run-test
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=Microsoft.Build.Engine --skip; else ${TESTCMD} --label=Microsoft.Build.Engine --timeout=5m make -w -C mcs/class/Microsoft.Build.Engine run-test; fi
${TESTCMD} --label=Microsoft.Build.Framework --timeout=5m make -w -C mcs/class/Microsoft.Build.Framework run-test
-${TESTCMD} --label=Microsoft.Build.Tasks --timeout=5m make -w -C mcs/class/Microsoft.Build.Tasks run-test
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=Microsoft.Build.Tasks --skip; else ${TESTCMD} --label=Microsoft.Build.Tasks --timeout=5m make -w -C mcs/class/Microsoft.Build.Tasks run-test; fi
${TESTCMD} --label=Microsoft.Build.Utilities --timeout=5m make -w -C mcs/class/Microsoft.Build.Utilities run-test
${TESTCMD} --label=Mono.C5 --timeout=5m make -w -C mcs/class/Mono.C5 run-test
${TESTCMD} --label=Mono.Tasklets --timeout=5m make -w -C mcs/class/Mono.Tasklets run-test
${TESTCMD} --label=System.Configuration --timeout=5m make -w -C mcs/class/System.Configuration run-test
${TESTCMD} --label=System.Transactions --timeout=5m make -w -C mcs/class/System.Transactions run-test
-${TESTCMD} --label=System.Web.Extensions --timeout=5m make -w -C mcs/class/System.Web.Extensions run-test
-${TESTCMD} --label=System.Core --timeout=15m make -w -C mcs/class/System.Core run-test
-${TESTCMD} --label=symbolicate --timeout=60m make -w -C mcs/tools/mono-symbolicate check
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=System.Web.Extensions --skip; else ${TESTCMD} --label=System.Web.Extensions --timeout=5m make -w -C mcs/class/System.Web.Extensions run-test; fi
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=System.Core --skip; else ${TESTCMD} --label=System.Core --timeout=15m make -w -C mcs/class/System.Core run-test; fi
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=symbolicate --skip; else ${TESTCMD} --label=symbolicate --timeout=60m make -w -C mcs/tools/mono-symbolicate check; fi
${TESTCMD} --label=System.Xml.Linq --timeout=5m make -w -C mcs/class/System.Xml.Linq run-test
${TESTCMD} --label=System.Data.DSE --timeout=5m make -w -C mcs/class/System.Data.DataSetExtensions run-test
${TESTCMD} --label=System.Web.Abstractions --timeout=5m make -w -C mcs/class/System.Web.Abstractions run-test
${TESTCMD} --label=Mono.CodeContracts --timeout=5m make -w -C mcs/class/Mono.CodeContracts run-test
${TESTCMD} --label=System.Runtime.Caching --timeout=5m make -w -C mcs/class/System.Runtime.Caching run-test
${TESTCMD} --label=System.Data.Services --timeout=5m make -w -C mcs/class/System.Data.Services run-test
-${TESTCMD} --label=System.Web.DynamicData --timeout=5m make -w -C mcs/class/System.Web.DynamicData run-test
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=System.Web.DynamicData --skip; else ${TESTCMD} --label=System.Web.DynamicData --timeout=5m make -w -C mcs/class/System.Web.DynamicData run-test; fi
${TESTCMD} --label=Mono.CSharp --timeout=5m make -w -C mcs/class/Mono.CSharp run-test
-${TESTCMD} --label=WindowsBase --timeout=5m make -w -C mcs/class/WindowsBase run-test
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=WindowsBase --skip; else ${TESTCMD} --label=WindowsBase --timeout=5m make -w -C mcs/class/WindowsBase run-test; fi
${TESTCMD} --label=System.Numerics --timeout=5m make -w -C mcs/class/System.Numerics run-test
${TESTCMD} --label=System.Runtime.DurableInstancing --timeout=5m make -w -C mcs/class/System.Runtime.DurableInstancing run-test
${TESTCMD} --label=System.ServiceModel.Discovery --timeout=5m make -w -C mcs/class/System.ServiceModel.Discovery run-test
${TESTCMD} --label=System.Json --timeout=5m make -w -C mcs/class/System.Json run-test
${TESTCMD} --label=System.Threading.Tasks.Dataflow --timeout=5m make -w -C mcs/class/System.Threading.Tasks.Dataflow run-test
${TESTCMD} --label=Mono.Debugger.Soft --timeout=5m make -w -C mcs/class/Mono.Debugger.Soft run-test
-${TESTCMD} --label=Microsoft.Build --timeout=5m make -w -C mcs/class/Microsoft.Build run-test
-${TESTCMD} --label=monodoc --timeout=10m make -w -C mcs/tools/mdoc run-test
-${TESTCMD} --label=Microsoft.Build-12 --timeout=10m make -w -C mcs/class/Microsoft.Build run-test PROFILE=xbuild_12
-${TESTCMD} --label=Microsoft.Build.Engine-12 --timeout=60m make -w -C mcs/class/Microsoft.Build.Engine run-test PROFILE=xbuild_12
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=Microsoft.Build --skip; else ${TESTCMD} --label=Microsoft.Build --timeout=5m make -w -C mcs/class/Microsoft.Build run-test; fi
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=monodoc --skip; else ${TESTCMD} --label=monodoc --timeout=10m make -w -C mcs/tools/mdoc run-test; fi
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=Microsoft.Build-12 --skip; else ${TESTCMD} --label=Microsoft.Build-12 --timeout=10m make -w -C mcs/class/Microsoft.Build run-test PROFILE=xbuild_12; fi
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=Microsoft.Build.Engine-12 --skip; else ${TESTCMD} --label=Microsoft.Build.Engine-12 --timeout=60m make -w -C mcs/class/Microsoft.Build.Engine run-test PROFILE=xbuild_12; fi
${TESTCMD} --label=Microsoft.Build.Framework-12 --timeout=60m make -w -C mcs/class/Microsoft.Build.Framework run-test PROFILE=xbuild_12
-${TESTCMD} --label=Microsoft.Build.Tasks-12 --timeout=60m make -w -C mcs/class/Microsoft.Build.Tasks run-test PROFILE=xbuild_12
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=Microsoft.Build.Tasks-12 --skip; else ${TESTCMD} --label=Microsoft.Build.Tasks-12 --timeout=60m make -w -C mcs/class/Microsoft.Build.Tasks run-test PROFILE=xbuild_12; fi
${TESTCMD} --label=Microsoft.Build.Utilities-12 --timeout=60m make -w -C mcs/class/Microsoft.Build.Utilities run-test PROFILE=xbuild_12
-${TESTCMD} --label=Microsoft.Build-14 --timeout=60m make -w -C mcs/class/Microsoft.Build run-test PROFILE=xbuild_14
-${TESTCMD} --label=Microsoft.Build.Engine-14 --timeout=60m make -w -C mcs/class/Microsoft.Build.Engine run-test PROFILE=xbuild_14
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=Microsoft.Build-14 --skip; else ${TESTCMD} --label=Microsoft.Build-14 --timeout=60m make -w -C mcs/class/Microsoft.Build run-test PROFILE=xbuild_14; fi
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=Microsoft.Build.Engine-14 --skip; else ${TESTCMD} --label=Microsoft.Build.Engine-14 --timeout=60m make -w -C mcs/class/Microsoft.Build.Engine run-test PROFILE=xbuild_14; fi
${TESTCMD} --label=Microsoft.Build.Framework-14 --timeout=60m make -w -C mcs/class/Microsoft.Build.Framework run-test PROFILE=xbuild_14
-${TESTCMD} --label=Microsoft.Build.Tasks-14 --timeout=60m make -w -C mcs/class/Microsoft.Build.Tasks run-test PROFILE=xbuild_14
+if [[ -n "${ghprbPullId}" ]] && [[ ${label} == w* ]]; then ${TESTCMD} --label=Microsoft.Build.Tasks-14 --skip; else ${TESTCMD} --label=Microsoft.Build.Tasks-14 --timeout=60m make -w -C mcs/class/Microsoft.Build.Tasks run-test PROFILE=xbuild_14; fi
${TESTCMD} --label=Microsoft.Build.Utilities-14 --timeout=60m make -w -C mcs/class/Microsoft.Build.Utilities run-test PROFILE=xbuild_14
if [[ ${label} == osx-* ]]
then ${TESTCMD} --label=ms-test-suite --timeout=15m make -w -C acceptance-tests check-ms-test-suite
export TESTCMD=`dirname "${BASH_SOURCE[0]}"`/run-step.sh
-${TESTCMD} --label=check-profiler-stress --timeout=20h make -C acceptance-tests check-profiler-stress
+${TESTCMD} --label=check-profiler-stress --timeout=24h make -C acceptance-tests check-profiler-stress