64ac736ec5
Former-commit-id: f3cc9b82f3e5bd8f0fd3ebc098f789556b44e9cd
637 lines
24 KiB
C#
637 lines
24 KiB
C#
/**
|
|
* \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;
|
|
|
|
[assembly: CLSCompliant (false)]
|
|
|
|
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, InMemory = 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 (SymbolsNotFoundException)
|
|
{
|
|
// ignore assemblies without debug symbols
|
|
continue;
|
|
}
|
|
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_3 (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
|
|
|
|
foreach (var st in stackTraces) {
|
|
var thread_id = st["native_thread_id"]?.ToString();
|
|
|
|
var unmanaged_frames = new List<JObject>();
|
|
var managed_frames = new List<JObject>();
|
|
var exceptions = new List<JObject>();
|
|
|
|
var thread_name = st["thread_name"]?.ToString ();
|
|
if (thread_name == null || thread_name?.Length == 0)
|
|
thread_name = "Unnamed thread";
|
|
|
|
var payload_unmanaged_frames = st["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 = st["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);
|
|
}
|
|
}
|
|
|
|
var payload_exceptions = st["exceptions"] as JArray;
|
|
for (int exc=0; payload_exceptions != null && exc < payload_exceptions.Count; exc++)
|
|
{
|
|
var exception_obj = payload_exceptions [exc] as JObject;
|
|
var exc_managed_frames = new List<JObject>();
|
|
var payload_exception_frames = exception_obj ["managed_frames"] as JArray;
|
|
for (int fr = 0; payload_exception_frames != null && fr < payload_exception_frames.Count; fr++)
|
|
{
|
|
var frame = payload_exception_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)));
|
|
|
|
exc_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);
|
|
|
|
exc_managed_frames.Add (blob);
|
|
}
|
|
}
|
|
|
|
var type = new JProperty ("type", exception_obj ["type"].ToString ());
|
|
var managed_st = new JProperty("frames", new JArray(exc_managed_frames.ToArray()));
|
|
var exc_blob = new JObject(type, managed_st);
|
|
|
|
exceptions.Add (exc_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", st ["native_thread_id"]);
|
|
var active = new JProperty ("active", "true");
|
|
|
|
if (st["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", st["native_thread_id"]);
|
|
var active = new JProperty ("active", "true");
|
|
|
|
if (unmanaged_frames.Count == 0 && st["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);
|
|
}
|
|
}
|
|
|
|
bool first_exception = true;
|
|
foreach (var exc_input in exceptions) {
|
|
// The 0.0.3 managed exception only has one thread, so we want it to be active and set crashed to true.
|
|
var active = new JProperty ("active", first_exception ? "true" : "false");
|
|
first_exception = false;
|
|
|
|
var name = "Caught Managed Exception";
|
|
|
|
var exception_thread = new JObject (active, new JProperty ("crashed", "true"), new JProperty ("name", name), new JProperty ("id", name), new JProperty ("exception", exc_input));
|
|
var exception_exc = new JObject(new JProperty("module", name),
|
|
new JProperty("type", path),
|
|
new JProperty("value", ""),
|
|
new JProperty("stacktrace", exc_input ["frames"]));
|
|
|
|
thread_objs.Add (exception_thread);
|
|
exc_objs.Add (exception_exc);
|
|
}
|
|
}
|
|
|
|
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 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 = "MonoSentryUploader/1.0.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:", ex.Response.Headers [""]);
|
|
for (int i=0; i < ex.Response.Headers.Count; i++)
|
|
Console.WriteLine("\t\t {0} : {1}", ex.Response.Headers.Keys [i], ex.Response.Headers [i]);
|
|
}
|
|
}
|
|
|
|
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.4") {
|
|
// Same for now
|
|
sentry_message = Format_0_0_3 (filePath, payload, hash);
|
|
} else if (version_string == "0.0.3") {
|
|
sentry_message = Format_0_0_3 (filePath, payload, hash);
|
|
} else if (version_string == "0.0.2") {
|
|
sentry_message = Format_0_0_2 (filePath, payload, hash);
|
|
} else {
|
|
Console.WriteLine ("ERROR: Crash reporting version mismatch");
|
|
return;
|
|
}
|
|
|
|
// sent to url via post?
|
|
// Console.WriteLine (sentry_message);
|
|
|
|
SendMessage (sentry_message, url);
|
|
}
|
|
|
|
public Uploader (CodeCollection assemblies)
|
|
{
|
|
this.codebase = assemblies;
|
|
}
|
|
|
|
static string[] GetAssemblies (string fileRoot) {
|
|
var dlls = Directory.GetFiles (fileRoot, "*.dll", SearchOption.AllDirectories);
|
|
var exes = Directory.GetFiles (fileRoot, "*.exe", SearchOption.AllDirectories);
|
|
|
|
return dlls.Concat (exes).ToArray ();
|
|
}
|
|
|
|
static string[] GetFiles (string fileRoot) {
|
|
return Directory.GetFiles (fileRoot, "mono_crash.*.json", SearchOption.AllDirectories);
|
|
}
|
|
|
|
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);
|
|
|
|
var files = GetFiles (fileRoot);
|
|
|
|
if (files.Length == 0)
|
|
return;
|
|
|
|
// Find all of the assemblies in tree that could have made the crash dump
|
|
var assemblies = GetAssemblies (fileRoot);
|
|
var codebase = new CodeCollection (assemblies);
|
|
|
|
foreach (var file in files) {
|
|
Console.WriteLine ($"Processing {file} ...");
|
|
var state = new Uploader (codebase);
|
|
state.Upload (file, os_tag, dsn);
|
|
}
|
|
}
|
|
}
|
|
}
|