diff options
author | Alexander Kyte <alexmkyte@gmail.com> | 2018-10-23 18:24:26 +0300 |
---|---|---|
committer | Alexander Köplinger <alex.koeplinger@outlook.com> | 2018-10-23 18:24:26 +0300 |
commit | 109563930d6559e3ade2c8f652f21574ea43ad1d (patch) | |
tree | 083bdc5d74f542d081438fbd520f05050eb385aa | |
parent | 5de2976c1168f402567380e77536482d58ae68c9 (diff) |
[crash] Add mono_crash uploader for Sentry (#10840)
* [crash] Report which thread was crashing
* [crash] Added upload-to-sentry tool
* [crash] Add sentry uploader step to telemetry
-rw-r--r-- | mcs/tools/Makefile | 2 | ||||
-rw-r--r-- | mcs/tools/upload-to-sentry/Makefile | 31 | ||||
-rw-r--r-- | mcs/tools/upload-to-sentry/UploadToSentry.cs | 423 | ||||
-rw-r--r-- | mcs/tools/upload-to-sentry/upload-to-sentry.exe.sources | 156 | ||||
-rw-r--r-- | mono/metadata/icall.c | 4 | ||||
-rw-r--r-- | mono/metadata/threads.c | 6 | ||||
-rw-r--r-- | mono/utils/mono-state.c | 11 | ||||
-rw-r--r-- | mono/utils/mono-state.h | 6 | ||||
-rwxr-xr-x | scripts/ci/run-jenkins.sh | 2 | ||||
-rwxr-xr-x | scripts/ci/run-upload-sentry.sh | 15 |
10 files changed, 643 insertions, 13 deletions
diff --git a/mcs/tools/Makefile b/mcs/tools/Makefile index 891d8c32c4c..3056f53a1d2 100644 --- a/mcs/tools/Makefile +++ b/mcs/tools/Makefile @@ -51,7 +51,7 @@ net_4_5_dirs := \ gacutil build_SUBDIRS = -build_PARALLEL_SUBDIRS := resgen gacutil security culevel commoncryptogenerator resx2sr linker cil-strip corcompare mono-api-diff mono-api-html +build_PARALLEL_SUBDIRS := resgen gacutil security culevel upload-to-sentry commoncryptogenerator resx2sr linker cil-strip corcompare mono-api-diff mono-api-html monodroid_tools_SUBDIRS = monodroid_tools_PARALLEL_SUBDIRS = cil-strip linker-analyzer mkbundle mdoc mono-symbolicate monodroid_SUBDIRS = nunit-lite diff --git a/mcs/tools/upload-to-sentry/Makefile b/mcs/tools/upload-to-sentry/Makefile new file mode 100644 index 00000000000..6605744cc29 --- /dev/null +++ b/mcs/tools/upload-to-sentry/Makefile @@ -0,0 +1,31 @@ +thisdir = tools/upload-to-sentry +SUBDIRS = +include ../../build/rules.make + +PROGRAM = upload-to-sentry.exe +LIB_REFS = \ + System \ + System.Core \ + System.Data \ + System.Runtime.Serialization \ + System.Xml \ + System.Xml.Linq \ + Mono.Posix \ + System Mono.Cecil + +NO_INSTALL = yes + +upload-crashes: + MONO_PATH="$(topdir)/class/lib/$(BUILD_TOOLS_PROFILE)$(PLATFORM_PATH_SEPARATOR)$$MONO_PATH" $(RUNTIME) $(topdir)/class/lib/build-$(HOST_PLATFORM)/upload-to-sentry.exe + +# It can be run using system .net during boostrap +TARGET_NET_REFERENCE = v4.6 +# Trick to make it work during boostrap where it has to run with system +# assemblies not the ones in lib/basic folder +PROGRAM_USE_INTERMEDIATE_FILE = 1 + +ifdef MCS_MODE +LIB_REFS += Mono.Cecil.Mdb +endif + +include ../../build/executable.make diff --git a/mcs/tools/upload-to-sentry/UploadToSentry.cs b/mcs/tools/upload-to-sentry/UploadToSentry.cs new file mode 100644 index 00000000000..2a1309d0661 --- /dev/null +++ b/mcs/tools/upload-to-sentry/UploadToSentry.cs @@ -0,0 +1,423 @@ +/** + * \file UploadToSentry.cs + * Support for reading verbose unmanaged crash dumps + * + * Author: + * Alexander Kyte (alkyte@microsoft.com) + * + * (C) 2018 Microsoft, Inc. + * + */ + +using System; + +using System.IO; +using System.Linq; +using System.Collections.Generic; +using Mono.Collections.Generic; + +using System.Text; +using System.Text.RegularExpressions; + +using Newtonsoft.Json.Linq; + +using System.Reflection; +using Mono.Cecil; +using Mono.Cecil.Cil; + +using System.Net; + +namespace UploadToSentry +{ + // Modeled after https://github.com/getsentry/raven-csharp/blob/develop/src/app/SharpRaven/Dsn.cs + class Dsn { + public readonly string PrivateKey; + public readonly string PublicKey; + public readonly string ProjectID; + + private readonly string path; + private readonly int port; + private readonly string sentryUriString; + private readonly Uri uri; + + public Dsn (string dsn) { + this.uri = new Uri(dsn); + this.PrivateKey = GetPrivateKey(this.uri); + this.PublicKey = GetPublicKey(this.uri) ?? throw new ArgumentException("A publicKey is required.", nameof(dsn)); + this.port = this.uri.Port; + this.ProjectID = GetProjectID(this.uri); + this.path = GetPath(this.uri); + this.sentryUriString = String.Format("{0}://{1}:{2}{3}/api/{4}/store/", this.uri.Scheme, this.uri.DnsSafeHost, this.port, this.path, this.ProjectID); + } + + private static string GetPath(Uri uri) + { + int lastSlash = uri.AbsolutePath.LastIndexOf("/", StringComparison.Ordinal); + return uri.AbsolutePath.Substring(0, lastSlash); + } + + private static string GetProjectID(Uri uri) + { + int lastSlash = uri.AbsoluteUri.LastIndexOf("/", StringComparison.Ordinal); + return uri.AbsoluteUri.Substring(lastSlash + 1); + } + + public Uri SentryUri { + get { + return new Uri (sentryUriString); + } + } + + private static string GetPrivateKey(Uri uri) + { + var parts = uri.UserInfo.Split(':'); + return parts.Length == 2 ? parts[1] : null; + } + + private static string GetPublicKey(Uri uri) + { + var publicKey = uri.UserInfo.Split(':')[0]; + return publicKey != string.Empty ? publicKey : null; + } + } + + class CodeCollection + { + Dictionary<Tuple<string, uint>, Collection<SequencePoint>> Lookup; + Dictionary<Tuple<string, uint>, Tuple<string, string, string>> Types; + + public void Add (string assembly, string klass, string function, string mvid, uint token, Collection<SequencePoint> seqs) + { + var key = new Tuple<string, uint>(mvid, token); + Lookup[key] = seqs; + Types[key] = new Tuple<string, string, string>(assembly, klass, function); + } + + public CodeCollection(string [] assemblies) + { + Lookup = new Dictionary<Tuple<string, uint>, Collection<SequencePoint>>(); + Types = new Dictionary<Tuple<string, uint>, Tuple<string, string, string>>(); + + foreach (string assembly in assemblies) + { + if (assembly.EndsWith(".dll") || assembly.EndsWith(".exe")) + { + // Console.WriteLine("Reading {0}", assembly); + var readerParameters = new ReaderParameters { ReadSymbols = true }; + AssemblyDefinition myLibrary = null; + try + { + myLibrary = AssemblyDefinition.ReadAssembly(assembly, readerParameters); + string mvid = myLibrary.MainModule.Mvid.ToString().ToUpper(); + Console.WriteLine("\t-- Success Parsing {0}: {1}", assembly, mvid); + + foreach (var ty in myLibrary.MainModule.Types) + { + for (int i = 0; i < ty.Methods.Count; i++) + { + string klass = ty.FullName; + string function = ty.Methods[i].FullName; + uint token = Convert.ToUInt32(ty.Methods[i].MetadataToken.ToInt32()); + this.Add(assembly, klass, function, mvid, token, ty.Methods[i].DebugInformation.SequencePoints); + } + } + } + catch (Exception e) + { + Console.WriteLine("\t-- Error Parsing {0}: {1}", assembly, e.Message); + } + } + } + } + + public JObject Find (string mvid, uint token, uint goal) + { + var method_idx = new Tuple<string, uint>(mvid, token); + if (!Lookup.ContainsKey(method_idx)) + return null; + + var seqs = Lookup[method_idx]; + + var accum = new JObject(); + foreach (var seq in seqs) + { + if (goal != seq.Offset) + continue; + + accum.Add (new JProperty("lineno", seq.StartLine)); + accum.Add (new JProperty("filename", seq.Document.Url)); + break; + } + + var typ = Types[method_idx]; + var assembly = typ.Item1; + var klass = typ.Item2; + accum.Add (new JProperty("module", String.Format("{0} {1}", assembly, klass))); + accum.Add (new JProperty("function", typ.Item3)); + + return accum; + } + } + + + class Uploader + { + CodeCollection codebase; + + public JObject Format_0_0_2 (string fileName, JObject payload, string hash) + { + var event_id = new JProperty("event_id", Guid.NewGuid().ToString("n")); + var timestamp = new JProperty("timestamp", DateTime.UtcNow.ToString("s", System.Globalization.CultureInfo.InvariantCulture)); + var exc_objs = new List<JObject> (); + var thread_objs = new List<JObject> (); + + var stackTraces = payload["threads"] as JArray; + + var path = Path.GetDirectoryName (fileName); // Best differentiator in-tree for files is where they are run from + + for (int i=0; i < stackTraces.Count; i++){ + var thread_id = stackTraces[i]["native_thread_id"].ToString(); + + var unmanaged_frames = new List<JObject>(); + var managed_frames = new List<JObject>(); + + var thread_name = stackTraces [i]["thread_name"].ToString (); + if (thread_name == null || thread_name.Length == 0) + thread_name = "Unnamed thread"; + + var payload_unmanaged_frames = stackTraces[i]["unmanaged_frames"] as JArray; + for (int fr=0; payload_unmanaged_frames != null && fr < payload_unmanaged_frames.Count; fr++) + { + var frame = payload_unmanaged_frames [fr] as JObject; + var native_address = frame["native_address"]; + var unmanaged_name = frame["unmanaged_name"] != null ? frame["unmanaged_name"].ToString() : ""; + + var fn_filename = new JProperty("filename", ""); + var function = new JProperty("function", unmanaged_name); + var module = new JProperty("module", "mono-sgen"); + var vars = new JProperty("vars", new JObject(new JProperty ("native_address", native_address))); + var blob = new JObject(fn_filename, function, module, vars); + + unmanaged_frames.Add (blob); + } + + var payload_managed_frames = stackTraces[i]["managed_frames"] as JArray; + for (int fr = 0; payload_managed_frames != null && fr < payload_managed_frames.Count; fr++) + { + var frame = payload_managed_frames [fr] as JObject; + if (frame["is_managed"] != null && frame["is_managed"].ToString ().ToUpper () == "TRUE") + { + var guid_val = frame["guid"].ToString (); + var token_val = Convert.ToUInt32(frame["token"].ToString (), 16); + var offset_val = Convert.ToUInt32(frame["il_offset"].ToString (), 16); + + var output_frame = codebase.Find (guid_val, token_val, offset_val); + if (output_frame == null) + continue; + + var guid = new JProperty("guid", guid_val); + var token = new JProperty("token", token_val); + var il_offset = new JProperty("il_offset", offset_val); + + output_frame.Add (new JProperty("vars", new JObject(guid, token, il_offset))); + + managed_frames.Add(output_frame); + } else { + var native_address = frame["native_address"]; + var unmanaged_name = frame["unmanaged_name"] != null ? frame["unmanaged_name"].ToString() : ""; + + var fn_filename = new JProperty("filename", "mono-sgen"); + var function = new JProperty("function", unmanaged_name); + var module = new JProperty("module", ""); + var vars = new JProperty("vars", frame); + var blob = new JObject(fn_filename, function, module, vars); + + managed_frames.Add (blob); + } + } + + if (unmanaged_frames.Count > 0) { + var unmanaged_st = new JObject(new JProperty("frames", new JArray(unmanaged_frames.ToArray ()))); + var id = String.Format ("{0}_unmanaged", stackTraces[i]["native_thread_id"]); + var active = new JProperty ("active", "true"); + + if (stackTraces[i]["crashed"].ToString ().ToUpper () == "TRUE") { + var unmanaged_thread = new JObject (active, new JProperty ("crashed", "true"), new JProperty ("name", String.Format ("{0} unmanaged", thread_name)), new JProperty ("id", id)); + var unmanaged_exc = new JObject(new JProperty("module", String.Format("{0}_managed_frames", thread_id)), + new JProperty("type", path), + new JProperty("value", ""), + new JProperty("stacktrace", unmanaged_st), new JProperty("thread_id", id)); + + thread_objs.Add(unmanaged_thread); + exc_objs.Add (unmanaged_exc); + } else { + var unmanaged_thread = new JObject (active, new JProperty ("name", String.Format ("{0} Unmanaged", thread_name)), + new JProperty ("id", id), new JProperty ("stacktrace", unmanaged_st)); + thread_objs.Add(unmanaged_thread); + } + } + + if (managed_frames.Count > 0) { + var managed_st = new JObject(new JProperty("frames", new JArray(managed_frames.ToArray()))); + // If we are the crashing thread, set the exception object to the + // managed stacktrace and the thread object to the managed thread + // + // If we aren't, add the thread + st to + var id = String.Format ("{0}_managed", stackTraces[i]["native_thread_id"]); + var active = new JProperty ("active", "true"); + + if (unmanaged_frames.Count == 0 && stackTraces[i]["crashed"].ToString ().ToUpper () == "TRUE") { + var managed_thread = new JObject (active, new JProperty ("crashed", "true"), new JProperty ("name", String.Format ("{0} managed", thread_name)), new JProperty ("id", id)); + var managed_exc = new JObject(new JProperty("module", String.Format("{0}_managed_frames", thread_id)), + new JProperty("type", path), + new JProperty("value", ""), + new JProperty("stacktrace", managed_st), new JProperty("thread_id", id)); + + thread_objs.Add(managed_thread); + exc_objs.Add (managed_exc); + } else { + var managed_thread = new JObject (active, new JProperty ("name", String.Format ("{0} managed", thread_name)), + new JProperty ("id", id), new JProperty ("stacktrace", managed_st)); + thread_objs.Add(managed_thread); + } + } + } + + var exception = new JProperty("exception", new JObject (new JProperty ("values", new JArray(exc_objs.ToArray ())))); + var threads = new JProperty("threads", new JObject (new JProperty ("values", new JArray(thread_objs.ToArray ())))); + // Bake in the whole blob + var embedded = new JProperty("extra", payload); + var fingerprint = new JProperty ("fingerprint", new JArray (new JValue (hash))); + + var sentry_message = new JObject (timestamp, event_id, exception, embedded, threads, fingerprint); + + return sentry_message; + } + + public void SendMessage (JObject sentry_message, Dsn url) + { + // Console.WriteLine ("Sending {0}", sentry_message.ToString ()); + + var request = (HttpWebRequest) WebRequest.Create (url.SentryUri); + request.Method = "POST"; + request.ContentType = "application/json"; + request.UserAgent = "SharpRaven/2.4.0.0"; + + var sentryVersion = 7; + var time = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; + var key = url.PrivateKey != null ? ", sentry_secret=" + url.PrivateKey : null; + var header = String.Format("Sentry sentry_version={0}" + + ", sentry_client={1}, sentry_timestamp={2}, sentry_key={3}{4}", + sentryVersion, request.UserAgent, time, url.PublicKey, key); + request.Headers ["X-Sentry-Auth"] = header; + + byte[] byteArray = Encoding.UTF8.GetBytes(sentry_message.ToString ()); + request.ContentLength = byteArray.Length; + + Stream dataStream = request.GetRequestStream(); + dataStream.Write(byteArray, 0, byteArray.Length); + dataStream.Close(); + + try { + WebResponse response = request.GetResponse (); + // Display the status. + // Console.WriteLine(((HttpWebResponse)response).StatusDescription); + + StreamReader reader = new StreamReader(response.GetResponseStream()); + // Read the content. + string responseFromServer = reader.ReadToEnd(); + // Display the content. + Console.WriteLine("\t-- HTTP POST Success {0}", responseFromServer); + // Clean up the streams. + } catch (WebException ex) { + Console.WriteLine("\t-- HTTP POST Error {0}", ex.Response.Headers [""]); + } + } + + public void Upload (string filePath, string os_tag, Dsn url) + { + if (!File.Exists(filePath)) + throw new Exception(String.Format("Json file not found {0}", filePath)); + + var dump = File.ReadAllText(filePath); + //var message = new SentryMessage(dump); + // var blob = new SentryEvent(message); + var payload = JObject.Parse(dump); + + // Try to extract a test name + var fileName = Path.GetFileName (filePath); + var extract = Regex.Match(fileName, @"mono_crash\.([A-Za-z0-9]+)\.(\d)\.json"); + if (!extract.Success) + throw new Exception ("File name does not match correct format"); + + var groups = extract.Groups; + var hash = groups[1].Value; + // var increment = groups[2].Value; + + var version_string = payload["protocol_version"].ToString(); + JObject sentry_message = null; + if (version_string == "0.0.2") + sentry_message = Format_0_0_2 (filePath, payload, hash); + else + throw new Exception ("Crash reporting version mismatch"); + + // sent to url via post? + // Console.WriteLine (sentry_message); + + SendMessage (sentry_message, url); + } + + public Uploader (CodeCollection assemblies) + { + this.codebase = assemblies; + } + + static bool IsAssembly (string fileName) { + var extension = Path.GetExtension(fileName).ToUpper (); + return (extension == ".EXE" || extension == ".DLL"); + } + + static IEnumerable<string> GetAssemblies (string fileRoot) { + return Directory.GetFiles (fileRoot, "*.*", SearchOption.AllDirectories).Where(path => IsAssembly (path)); + } + + static IEnumerable<string> GetFiles (string fileRoot) { + var file_regex = @".*mono_crash.*json"; + return Directory.GetFiles (fileRoot, "*.*", SearchOption.AllDirectories).Where (path => + Regex.Match(Path.GetFileName(path), file_regex).Success); + } + + public static void Main (string[] args) + { + var url = System.Environment.GetEnvironmentVariable ("MONO_SENTRY_URL"); + if (url == null) { + Console.WriteLine ("MONO_SENTRY_URL missing"); + return; + } + + var fileRoot = System.Environment.GetEnvironmentVariable ("MONO_SENTRY_ROOT"); + if (fileRoot == null) { + Console.WriteLine ("MONO_SENTRY_ROOT missing"); + return; + } + + var os_tag = System.Environment.GetEnvironmentVariable ("MONO_SENTRY_OS"); + if (os_tag == null) { + Console.WriteLine ("MONO_SENTRY_OS missing"); + return; + } + + var dsn = new Dsn(url); + + // Find all of the assemblies in tree that could have made the crash dump + var assemblies = GetAssemblies (fileRoot); + var codebase = new CodeCollection (assemblies.ToArray ()); + + var files = GetFiles (fileRoot); + foreach (var file in files) { + var state = new Uploader (codebase); + state.Upload (file, os_tag, dsn); + } + } + } +} diff --git a/mcs/tools/upload-to-sentry/upload-to-sentry.exe.sources b/mcs/tools/upload-to-sentry/upload-to-sentry.exe.sources new file mode 100644 index 00000000000..e069eacc6f3 --- /dev/null +++ b/mcs/tools/upload-to-sentry/upload-to-sentry.exe.sources @@ -0,0 +1,156 @@ +UploadToSentry.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Bson/BsonBinaryType.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Bson/BsonBinaryWriter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Bson/BsonReader.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Bson/BsonToken.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Bson/BsonType.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Bson/BsonWriter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Bson/BsonObjectId.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Converters/BinaryConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Converters/DataSetConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Converters/DataTableConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Converters/CustomCreationConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Converters/DateTimeConverterBase.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Converters/EntityKeyMemberConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Converters/ExpandoObjectConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Converters/KeyValuePairConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Converters/BsonObjectIdConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Converters/RegexConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Converters/StringEnumConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/ConstructorHandling.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Converters/VersionConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/DateFormatHandling.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/DateTimeZoneHandling.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Formatting.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonConstructorAttribute.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonPosition.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JPropertyKeyedCollection.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/DynamicProxy.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JPath.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JRaw.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Required.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonDynamicContract.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonFormatterConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonISerializableContract.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonLinqContract.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonPrimitiveContract.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/DynamicValueProvider.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/ErrorEventArgs.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JPropertyDescriptor.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/DefaultReferenceResolver.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/PreserveReferencesHandling.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/IJsonLineInfo.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonArrayAttribute.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonContainerAttribute.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/DefaultValueHandling.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonConverterAttribute.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonObjectAttribute.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonSerializerSettings.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonValidatingReader.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/IJEnumerable.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JTokenEqualityComparer.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/MemberSerialization.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/ObjectCreationHandling.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Converters/IsoDateTimeConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Converters/JavaScriptDateTimeConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonTextReader.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonPropertyAttribute.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonIgnoreAttribute.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonTextWriter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonWriterException.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonReaderException.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonConverter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonConverterCollection.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonReader.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonConvert.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonSerializationException.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonSerializer.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/Extensions.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JConstructor.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JContainer.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JEnumerable.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JObject.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JArray.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JTokenReader.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JTokenWriter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JToken.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JProperty.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JTokenType.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Linq/JValue.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/Extensions.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/JsonSchemaException.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/JsonSchemaModel.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/JsonSchemaModelBuilder.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/JsonSchemaNodeCollection.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/JsonSchemaNode.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/JsonSchemaResolver.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/JsonSchemaWriter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/UndefinedSchemaIdHandling.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/ValidationEventArgs.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/ValidationEventHandler.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/ErrorContext.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/IContractResolver.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/IValueProvider.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonContract.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonProperty.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonPropertyCollection.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/MissingMemberHandling.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/NullValueHandling.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/ReferenceLoopHandling.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/JsonSchema.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/JsonSchemaBuilder.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/JsonSchemaConstants.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/IReferenceResolver.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/JsonSchemaType.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonStringContract.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/CachedAttributeGetter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/LateBoundMetadataTypeAttribute.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/ReflectionValueProvider.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/OnErrorAttribute.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/Base64Encoder.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/DynamicWrapper.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/DynamicReflectionDelegateFactory.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Serialization/ObjectConstructor.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/ILGeneratorExtensions.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/ReflectionDelegateFactory.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/LateBoundReflectionDelegateFactory.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/MethodCall.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/StringReference.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/TypeNameHandling.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/BidirectionalDictionary.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/CollectionWrapper.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/DateTimeUtils.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/DictionaryWrapper.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/EnumUtils.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/EnumValue.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/EnumValues.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonToken.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonWriter.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/StringBuffer.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/ListWrapper.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/MathUtils.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/StringUtils.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/TypeExtensions.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/Utilities/ValidationUtils.cs +../../../external/Newtonsoft.Json/Src/Newtonsoft.Json/WriteState.cs diff --git a/mono/metadata/icall.c b/mono/metadata/icall.c index 904cc9efc6d..8d41869399b 100644 --- a/mono/metadata/icall.c +++ b/mono/metadata/icall.c @@ -5803,7 +5803,7 @@ ves_icall_Mono_Runtime_ExceptionToState (MonoExceptionHandle exc_handle, guint64 mono_json_writer_init (&writer); mono_native_state_init (&writer); gboolean first_thread_added = TRUE; - mono_native_state_add_thread (&writer, &out, NULL, first_thread_added); + mono_native_state_add_thread (&writer, &out, NULL, first_thread_added, TRUE); char *output = mono_native_state_free (&writer, FALSE); result = mono_string_new_handle (mono_domain_get (), output, error); g_free (output); @@ -5875,7 +5875,7 @@ ves_icall_Mono_Runtime_DumpStateSingle (guint64 *portable_hash, guint64 *unporta mono_json_writer_init (&writer); mono_native_state_init (&writer); gboolean first_thread_added = TRUE; - mono_native_state_add_thread (&writer, &this_thread, NULL, first_thread_added); + mono_native_state_add_thread (&writer, &this_thread, NULL, first_thread_added, TRUE); char *output = mono_native_state_free (&writer, FALSE); result = mono_string_new_handle (mono_domain_get (), output, error); g_free (output); diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 2e4ab8e3c46..9afb96778b8 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -6218,7 +6218,7 @@ summary_timedwait (SummarizerGlobalState *state, int timeout_seconds) } static void -summarizer_state_term (SummarizerGlobalState *state, gchar **out, gchar *mem, size_t provided_size) +summarizer_state_term (SummarizerGlobalState *state, gchar **out, gchar *mem, size_t provided_size, MonoThreadSummary *controlling) { // See the array writes mono_memory_barrier (); @@ -6246,7 +6246,7 @@ summarizer_state_term (SummarizerGlobalState *state, gchar **out, gchar *mem, si // much more stable to do it all from the controlling thread. mono_get_eh_callbacks ()->mono_summarize_managed_stack (thread); - mono_summarize_native_state_add_thread (thread, thread->ctx); + mono_summarize_native_state_add_thread (thread, thread->ctx, thread == controlling); // Set non-shared state to notify the waiting thread to clean up // without having to keep our shared state alive @@ -6313,7 +6313,7 @@ mono_threads_summarize_execute (MonoContext *ctx, gchar **out, MonoStackHash *ha MOSTLY_ASYNC_SAFE_PRINTF("Finished thread summarizer pause from 0x%zx.\n", current); // Dump and cleanup all the stack memory - summarizer_state_term (&state, out, mem, provided_size); + summarizer_state_term (&state, out, mem, provided_size, &this_thread); } else { // Wait here, keeping our stack memory alive // for the dumper diff --git a/mono/utils/mono-state.c b/mono/utils/mono-state.c index adb2440db4b..4515a8cd5a5 100644 --- a/mono/utils/mono-state.c +++ b/mono/utils/mono-state.c @@ -213,7 +213,7 @@ mono_native_state_add_frames (JsonWriter *writer, int num_frames, MonoFrameSumma } void -mono_native_state_add_thread (JsonWriter *writer, MonoThreadSummary *thread, MonoContext *ctx, gboolean first_thread) +mono_native_state_add_thread (JsonWriter *writer, MonoThreadSummary *thread, MonoContext *ctx, gboolean first_thread, gboolean crashing_thread) { assert_has_space (); @@ -231,6 +231,10 @@ mono_native_state_add_thread (JsonWriter *writer, MonoThreadSummary *thread, Mon assert_has_space (); mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "crashed"); + mono_json_writer_printf (writer, "%s,\n", crashing_thread ? "true" : "false"); + + mono_json_writer_indent (writer); mono_json_writer_object_key(writer, "managed_thread_ptr"); mono_json_writer_printf (writer, "\"0x%x\",\n", (gpointer) thread->managed_thread_ptr); @@ -615,11 +619,10 @@ mono_summarize_native_state_end (void) } void -mono_summarize_native_state_add_thread (MonoThreadSummary *thread, MonoContext *ctx) +mono_summarize_native_state_add_thread (MonoThreadSummary *thread, MonoContext *ctx, gboolean crashing_thread) { - static gboolean not_first_thread = FALSE; - mono_native_state_add_thread (&writer, thread, ctx, !not_first_thread); + mono_native_state_add_thread (&writer, thread, ctx, !not_first_thread, crashing_thread); not_first_thread = TRUE; } diff --git a/mono/utils/mono-state.h b/mono/utils/mono-state.h index d69dbf4ab3f..8c64ada6ac0 100644 --- a/mono/utils/mono-state.h +++ b/mono/utils/mono-state.h @@ -18,7 +18,7 @@ #include <mono/metadata/threads-types.h> #include <mono/utils/json.h> -#define MONO_NATIVE_STATE_PROTOCOL_VERSION "0.0.1" +#define MONO_NATIVE_STATE_PROTOCOL_VERSION "0.0.2" MONO_BEGIN_DECLS @@ -33,7 +33,7 @@ char * mono_summarize_native_state_end (void); void -mono_summarize_native_state_add_thread (MonoThreadSummary *thread, MonoContext *ctx); +mono_summarize_native_state_add_thread (MonoThreadSummary *thread, MonoContext *ctx, gboolean crashing_thread); /* * These use memory from the caller @@ -49,7 +49,7 @@ char * mono_native_state_free (JsonWriter *writer, gboolean free_data); void -mono_native_state_add_thread (JsonWriter *writer, MonoThreadSummary *thread, MonoContext *ctx, gboolean first_thread); +mono_native_state_add_thread (JsonWriter *writer, MonoThreadSummary *thread, MonoContext *ctx, gboolean first_thread, gboolean crashing_thread); void mono_crash_dump (const char *jsonFile, MonoStackHash *hashes); diff --git a/scripts/ci/run-jenkins.sh b/scripts/ci/run-jenkins.sh index a12c9820a45..22e0bf67b06 100755 --- a/scripts/ci/run-jenkins.sh +++ b/scripts/ci/run-jenkins.sh @@ -270,3 +270,5 @@ elif [[ ${CI_TAGS} == *'mac-sdk'* ]]; then ${MONO_REPO_ROOT}/sc elif [[ ${CI_TAGS} == *'no-tests'* ]]; then exit 0; else make check-ci; fi + +${MONO_REPO_ROOT}/scripts/ci/run-upload-sentry.sh diff --git a/scripts/ci/run-upload-sentry.sh b/scripts/ci/run-upload-sentry.sh new file mode 100755 index 00000000000..2527b53a34d --- /dev/null +++ b/scripts/ci/run-upload-sentry.sh @@ -0,0 +1,15 @@ +#!/bin/bash -e +export TESTCMD=`dirname "${BASH_SOURCE[0]}"`/run-step.sh + +export MONO_SENTRY_ROOT=`dirname "${BASH_SOURCE[0]}/../../"` + +if [[ ${CI_TAGS} == *'win-'* ]]; +then + echo "Skipping telemetry phase due to arch" +elif [[ -n "${MONO_SENTRY_URL}" ]] +then + # Define MONO_SENTRY_URL and MONO_SENTRY_OS in environment + ${TESTCMD} --label=sentry-telemetry-upload --timeout=10m make -C mcs/tools/upload-to-sentry upload-crashes MONO_SENTRY_ROOT="$MONO_REPO_ROOT" +else + echo "Skipping telemetry phase because URL missing" +fi |