Skip to content

Commit a71d542

Browse files
authored
JIT stack trace parser (#39)
* JIT stack trace parser * Version + changelog update
1 parent 571deee commit a71d542

File tree

8 files changed

+100
-3
lines changed

8 files changed

+100
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ For example, to only get error reporting for hangs or crashes then only return f
2929
* Database add operation time (`performance.database`),
3030
* Database single send method time (`performance.send`),
3131
* Database single flush method time (`performance.flush`)
32-
32+
- Improvements to JIT stack frame parsing.
3333

3434
## Version 3.0.2
3535
- `BacktraceDatabase` now provides a new `Send` method. This method will try to send all objects from the database respecting the client side deduplication and retry setting. This can be used as an alternative to the `Flush` method which will try to send all objects from the database ignoring any client side deduplication and retry settings.

Runtime/Model/BacktraceData.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public class BacktraceData
4545
/// <summary>
4646
/// Version of the C# library
4747
/// </summary>
48-
public const string AgentVersion = "3.0.2";
48+
public const string AgentVersion = "3.0.3";
4949

5050
/// <summary>
5151
/// Application thread details

Runtime/Model/BacktraceUnhandledException.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Runtime.CompilerServices;
45

56
namespace Backtrace.Unity.Model
67
{
@@ -116,6 +117,10 @@ private void ConvertStackFrames()
116117
{
117118
stackFrame = SetNativeStackTraceInformation(frameString);
118119
}
120+
else if (frameString.StartsWith("#"))
121+
{
122+
stackFrame = SetJITStackTraceInformation(frameString);
123+
}
119124
else if (frameString.IndexOf('(', methodNameEndIndex + 1) > -1)
120125
{
121126
stackFrame = SetDefaultStackTraceInformation(frameString);
@@ -140,6 +145,56 @@ private void ConvertStackFrames()
140145
}
141146
}
142147

148+
/// <summary>
149+
/// Try to convert JIT stack trace
150+
/// </summary>
151+
/// <param name="frameString">JIT stack frame</param>
152+
/// <returns>Backtrace stack frame</returns>
153+
private BacktraceStackFrame SetJITStackTraceInformation(string frameString)
154+
{
155+
156+
var stackFrame = new BacktraceStackFrame();
157+
if (!frameString.StartsWith("#"))
158+
{
159+
//handle sitaution when we detected jit stack trace
160+
// but jit stack trace doesn't start with #
161+
stackFrame.FunctionName = frameString;
162+
return stackFrame;
163+
}
164+
165+
frameString = frameString.Substring(frameString.IndexOf(' ')).Trim();
166+
const string monoJitPrefix = "(Mono JIT Code)";
167+
var monoPrefixIndex = frameString.IndexOf(monoJitPrefix);
168+
if (monoPrefixIndex != -1)
169+
{
170+
frameString = frameString.Substring(monoPrefixIndex + monoJitPrefix.Length).Trim();
171+
}
172+
173+
const string managedWraperPrefix = "(wrapper managed-to-native)";
174+
var managedWraperIndex = frameString.IndexOf(managedWraperPrefix);
175+
if (managedWraperIndex != -1)
176+
{
177+
frameString = frameString.Substring(managedWraperIndex + managedWraperPrefix.Length).Trim();
178+
}
179+
180+
// right now we outfiltered all known prefixes
181+
// we should have only function name with parameters
182+
183+
// filter parameters, if we can't use full frameString as function name
184+
var parametersStart = frameString.IndexOf('(');
185+
var parametersEnd = frameString.IndexOf(')');
186+
if (parametersStart != -1 && parametersEnd != -1 && parametersEnd > parametersStart)
187+
{
188+
stackFrame.FunctionName = frameString.Substring(0, parametersStart).Trim();
189+
}
190+
else
191+
{
192+
stackFrame.FunctionName = frameString;
193+
}
194+
return stackFrame;
195+
196+
}
197+
143198
/// <summary>
144199
/// Try to convert native stack frame
145200
/// </summary>
@@ -237,6 +292,11 @@ private BacktraceStackFrame SetAndroidStackTraceInformation(string frameString)
237292
/// <returns></returns>
238293
private BacktraceStackFrame SetDefaultStackTraceInformation(string frameString)
239294
{
295+
const string wrapperPrefix = "(wrapper remoting-invoke-with-check)";
296+
if (frameString.StartsWith(wrapperPrefix))
297+
{
298+
frameString = frameString.Replace(wrapperPrefix, string.Empty);
299+
}
240300
// find method parameters
241301
int methodNameEndIndex = frameString.IndexOf(')');
242302

Tests/Runtime/BacktraceBaseTest.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ protected virtual BacktraceConfiguration GenerateDefaultConfiguration()
6161
[OneTimeTearDown]
6262
public void Cleanup()
6363
{
64+
BacktraceClient.BeforeSend = null;
65+
BacktraceClient.RequestHandler = null;
6466
Object.DestroyImmediate(GameObject);
6567
}
6668
}

Tests/Runtime/BacktraceClientTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public void Setup()
1717
BeforeSetup();
1818
AfterSetup(false);
1919
}
20+
2021

2122
[UnityTest]
2223
public IEnumerator TestClientCreation_ValidBacktraceConfiguration_ValidClientCreation()

Tests/Runtime/BacktraceStackTraceTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,34 @@ public void ExceptionStackTrace_ShouldGenerateEnvStackTraceIfExStackTraceIsEmpty
468468
Assert.IsTrue(error.StackFrames.Any());
469469
}
470470

471+
[Test]
472+
public void JITStackTrace_ShouldParseCorrectlyJITStackTrace_StackTraceObjectIsGeneratedCorrectly()
473+
{
474+
475+
var simpleFunctionName = "GetStacktrace";
476+
var functionNameWithWrappedManagedPrefix = "UnityEngine.DebugLogHandler:Internal_Log";
477+
var functioNamewithMonoJitCodePrefix = "ServerGameManager:SendInitialiseNetObjectToClient";
478+
var jitStackTrace = string.Format(@"#0 {0} (int)
479+
#1 DebugStringToFile(DebugStringToFileData const&)
480+
#2 DebugLogHandler_CUSTOM_Internal_Log(LogType, LogOption, ScriptingBackendNativeStringPtrOpaque*, ScriptingBackendNativeObjectPtrOpaque*)
481+
#3 (Mono JIT Code) (wrapper managed-to-native) {1} (UnityEngine.LogType,UnityEngine.LogOption,string,UnityEngine.Object)
482+
#4 (Mono JIT Code) {2} (FG.Common.GameConnection,FG.Common.NetObjectSpawnData)
483+
#5 (Mono JIT Code) ServerGameManager:SpawnAlreadySpawnedObjectsForClient (FG.Common.GameConnection)
484+
#6 (Mono JIT Code) ServerGameManager:ProcessLevelLoaded (FG.Common.GameConnection)
485+
#7 (Mono JIT Code) ServerGameManager:ProcessClientReady (FG.Common.GameConnection,FG.Common.PlayerReadinessState)
486+
#8 (Mono JIT Code) FG.Common.UnityNetworkMessageHandler:HandleAndFreeInboundGameMessage (FG.Common.MessageEnvelope)
487+
#9 (Mono JIT Code) FG.Common.UnityNetworkMessageHandler:PeekAndHandleMessage (UnityEngine.Networking.NetworkMessage,FG.Common.GameConnection)
488+
#10 (Mono JIT Code) FG.Common.FG_UnityInternetNetworkManager:ServerHandleMessageReceived (UnityEngine.Networking.NetworkMessage)",
489+
simpleFunctionName, functionNameWithWrappedManagedPrefix, functioNamewithMonoJitCodePrefix);
490+
491+
492+
var backtraceUnhandledException = new BacktraceUnhandledException("foo", jitStackTrace);
493+
494+
Assert.AreEqual(11, backtraceUnhandledException.StackFrames.Count);
495+
Assert.AreEqual(backtraceUnhandledException.StackFrames[0].FunctionName, simpleFunctionName);
496+
Assert.AreEqual(backtraceUnhandledException.StackFrames[3].FunctionName, functionNameWithWrappedManagedPrefix);
497+
Assert.AreEqual(backtraceUnhandledException.StackFrames[4].FunctionName, functioNamewithMonoJitCodePrefix);
498+
}
471499

472500
internal string ConvertStackTraceToString(List<SampleStackFrame> data)
473501
{

Tests/Runtime/ClientSendTests.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ public void Setup()
2424
client.Refresh();
2525
}
2626

27+
[TearDown]
28+
public void Cleanup()
29+
{
30+
client.RequestHandler = null;
31+
}
32+
2733
[UnityTest]
2834
public IEnumerator SendReport_ExceptionReport_ValidSend()
2935
{

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "io.backtrace.unity",
33
"displayName": "Backtrace",
4-
"version": "3.0.2",
4+
"version": "3.0.3",
55
"unity": "2017.1",
66
"description": "Backtrace's integration with Unity games allows customers to capture and report handled and unhandled Unity exceptions to their Backtrace instance, instantly offering the ability to prioritize and debug software errors.",
77
"keywords": [

0 commit comments

Comments
 (0)