Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- Android builds with Minify (R8) enabled failed to launch the SDK with `ClassNotFoundException: com.bugsee.unity.UnityWrapper`: Unity does not read the package's `Plugins/Android/bugsee-unity-wrapper.proguard`, so R8 stripped the wrapper classes C# reaches over JNI. A new post-generate step (`BugseeAndroidProguardRules`) appends the rules, between markers and idempotently, to the generated `unityLibrary/proguard-unity.txt` (the library's consumer rules), so no Player Settings change is needed. The rules now also keep the `UnityManagedException` constructor (built by name from C#) and the SDK interfaces implemented with `AndroidJavaProxy`; tests fail when a class C# names over JNI, a wrapper method it calls, or a proxied interface has no rule.
- IL2CPP Android, iOS and tvOS builds now write `LineNumberMappings.json` and `MethodMap.tsv`: the package adds `--emit-source-mapping --emit-method-map` to the Additional IL2CPP Arguments for the build (restored afterward, also after a failed or cancelled build: on the editor's next update, or when the editor quits, which covers a batchmode run that exits right after a failed build; only a killed editor process leaves them in `ProjectSettings.asset` until the next editor session restores them) when the il2cpp-linemap upload is on (`BUGSEE_APP_TOKEN` set in the Unity Editor's environment, `BUGSEE_NO_IL2CPP_MAPPING` unset, and no explicit mapping file via `BUGSEE_IL2CPP_MAPPING`). IL2CPP writes neither file unless asked (with Cloud Diagnostics crash reporting off, or on Unity 2021.3, where Unity does not add `--emit-source-mapping` itself), so the uploaded linemap had no `MethodMap.tsv` and mangled managed names could not be demangled (without the source-mapping argument there was no linemap at all). Verified with real Android IL2CPP builds on Unity 2022.3.62f3 and 2021.3.45f1 (both arguments exist in 6000.0.84f1 too; not built there, and iOS/tvOS were not built).
- Android builds on Unity 6 failed in Gradle (`DependencyHandler.module(java.lang.Object)` while evaluating `:launcher`): the package wrote its own `Assets/Plugins/Android/baseProjectTemplate.gradle` and `launcherTemplate.gradle` pinning Android Gradle plugin 7.4.2, which cannot run on Unity 6's Gradle 9. The package no longer creates Gradle templates. It patches the Gradle project Unity generates instead (so nothing in Assets goes stale on Unity upgrades): the Bugsee Gradle plugin is added when the generated project pins AGP 8.6+ (previously: any Unity 6), and on 2021.3 the release build type gets `debugSymbolLevel 'FULL'`. The exact templates earlier package versions wrote are removed on an Android build when Unity's default AGP is another major; any other template pinning a different AGP major is left alone with a warning naming the file and the fix. The sample no longer ships custom Gradle templates. Verified with real IL2CPP arm64 builds on 2021.3.45f1 and 6000.0.84f1; Bugsee Gradle plugin 4.0.8 works with Unity 6's AGP 9.0.0 / Gradle 9.3.1.
- Android native symbols on Unity 6: release builds no longer force `debugSymbolLevel 'FULL'` in the launcher; the level follows Player Settings (Create symbols.zip), which Unity 6 defaults to Disabled. Public gives symbol tables only (no file:line); Debugging is about ten times larger. An Android build with a Bugsee app token set now warns when the level is not Debugging, naming what is lost and the size trade-off. On Unity 6 the setting is `UnityEditor.Android.UserBuildSettings.DebugSymbols` (level None/SymbolTable/Full plus a Zip format; the 2021.3 `androidCreateSymbols` is obsolete there), and the opt-in also enables the Zip format. Opt in with `Bugsee > Android > Use Full Native Symbols (Debugging) For Symbol Upload` (or `BUGSEE_ANDROID_FULL_SYMBOLS=1` in CI): that build uses Debugging and the setting is restored afterwards (the saved state lives in `Library/Bugsee`, survives an editor exit after an interrupted build, and is restored only while the settings still hold what the opt-in set; a non-Android build never touches it).
- Android Gradle cleanup never deletes a template you edited: a file is removed only when it is exactly what an earlier package version generated (or an unedited Unity 2021.3 default base copy), after copying it (and its `.meta`) to `Library/Bugsee/legacy-templates/<timestamp>/`. A marker-bearing launcher with your own changes is kept with a warning. A launcher that is left alone and still uses `lintOptions`/`aaptOptions` is reported, and an unreadable AGP version (version catalog, Kotlin DSL) now warns that the Bugsee Gradle plugin was skipped.
Expand Down
6 changes: 3 additions & 3 deletions Documentation~/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ The iOS Run Script uploads for **every** Xcode configuration (Debug included, so

| Artifact | When | CLI type |
|---|---|---|
| `LineNumberMappings.json` (+ MethodMap) | Android post-build; iOS at **Xcode build** (every configuration) | `il2cpp-linemap` |
| `LineNumberMappings.json` + `MethodMap.tsv` (the package passes `--emit-source-mapping --emit-method-map` to IL2CPP for the build) | Android post-build; iOS at **Xcode build** (every configuration) | `il2cpp-linemap` |
| Android `symbols.zip` / ELF | Android post-build | `elf` |
| ProGuard / R8 `mapping.txt` | Android post-build when present | `proguard` |
| iOS `.dSYM` | Xcode Run Script (`Bugsee Upload Symbols`), every configuration | `dsym` |
Expand Down Expand Up @@ -196,14 +196,14 @@ Releases built without R8 (Minify off, the Unity default) have no mapping and no
**iOS**

1. Scripting Backend: IL2CPP
2. Enable source mapping / Script Debugging as needed for LineNumberMappings generation
2. Nothing to enable for LineNumberMappings: when `BUGSEE_APP_TOKEN` is set in the environment of the Unity Editor process during the build, the package adds `--emit-source-mapping --emit-method-map` to the IL2CPP arguments for the build (restored afterward, also after a failed build). A token set only for the Xcode Run Script (step 4) is too late: Unity then adds no arguments and the export may lack `MethodMap.tsv`
3. Build or archive in Xcode: the Run Script uploads the dSYMs of the configuration being built. Debug has a dSYM only with `DEBUG_INFORMATION_FORMAT = dwarf-with-dsym` (Unity exports Debug as `dwarf`); without one the script prints a warning and uploads no dSYM
4. Ensure `BUGSEE_APP_TOKEN` is visible to the Xcode Run Script phase
5. The iOS export sets `DEBUG_INFORMATION_FORMAT=dwarf-with-dsym` on every configuration of Unity-iPhone and UnityFramework, so Debug builds have a dSYM too. `dsymutil` on UnityFramework adds time to each Debug build; to keep Unity's own setting, tick **Bugsee → iOS → Keep Xcode Debug Info Format (skip dSYM force)** or set `BUGSEE_NO_DSYM_FORCE` in the Editor's environment (CI)

### Script Debugging vs dSYM

- **Script Debugging** / IL2CPP stacktrace information controls managed stack quality and LineNumberMappings generation.
- **Script Debugging** / IL2CPP stacktrace information controls managed stack quality. LineNumberMappings.json and MethodMap.tsv come from the IL2CPP arguments the package adds for the build when the upload is on (not from Script Debugging); set `BUGSEE_NO_IL2CPP_MAPPING` to turn that and the upload off, or `BUGSEE_IL2CPP_MAPPING` to upload a file you made yourself (Player Settings are then left alone). Adding or removing the token changes the arguments, which makes IL2CPP regenerate its cache once.
- **dSYM / ELF** carry native line programs used for the primary LNM remap (native PC → C++ file/line → C#).
- You need **both** the linemap upload and native symbols with line info for viewer C# file/line on native fatals.

Expand Down
3 changes: 2 additions & 1 deletion Editor/BugseeIl2CppLinemapUpload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ public void OnPostprocessBuild(BuildReport report)
{
Debug.LogWarning(
"Bugsee: LineNumberMappings.json not found — skipping il2cpp-linemap upload. " +
"Set BUGSEE_IL2CPP_MAPPING or enable IL2CPP source mapping in Player Settings.");
"The package adds --emit-source-mapping --emit-method-map to the IL2CPP arguments of an IL2CPP build when this upload is on; " +
"check that the scripting backend is IL2CPP, or set BUGSEE_IL2CPP_MAPPING to a LineNumberMappings.json.");
return;
}
}
Expand Down
221 changes: 221 additions & 0 deletions Editor/BugseeIl2CppMappingArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
#if UNITY_EDITOR
using System;
using System.IO;
using Bugsee.WrapperPolicy;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;

namespace Bugsee.Editor
{
/// <summary>
/// Makes an IL2CPP Android/iOS build write <c>LineNumberMappings.json</c> and <c>MethodMap.tsv</c>
/// (see <see cref="Il2CppMappingArgs"/>): IL2CPP writes neither unless asked, so the il2cpp-linemap upload had no
/// mapping, or one without MethodMap (mangled names could not be demangled). Runs only when the il2cpp-linemap upload
/// is on (a Bugsee app token, and BUGSEE_NO_IL2CPP_MAPPING unset). The arguments are added for this build and the
/// Player Settings string is restored afterwards (the string is saved in the VCS-tracked ProjectSettings.asset while
/// the build runs). The post-build callback is skipped when the pipeline aborts, so a one-shot editor update handler
/// restores once no build is running (success, failure or cancel); the saved state in Library/Bugsee also covers an
/// editor exit (an editor quit restores too, so a batchmode run that exits right after a failed build is clean; only a
/// killed editor process leaves the arguments for the next session). A string the user changed meanwhile is kept (see <see cref="Il2CppArgsRestorePolicy"/>).
/// Unity itself adds --emit-source-mapping on 2022.3 and 6000 when Cloud Diagnostics crash reporting is on; the
/// duplicate argument is harmless. Toggling the token changes the arguments and so invalidates the IL2CPP cache.
/// </summary>
[InitializeOnLoad]
sealed class BugseeIl2CppMappingArgs : IPreprocessBuildWithReport, IPostprocessBuildWithReport
{
public int callbackOrder => 20;

const string InProgressKey = "Bugsee.Il2CppArgsBuildInProgress";

[Serializable]
sealed class PendingRestore
{
public string previous;
public string applied;
}

static string PendingPath =>
Path.Combine(Path.GetDirectoryName(Path.GetFullPath(Application.dataPath)), "Library", "Bugsee", "il2cpp-args-restore.json");

static BugseeIl2CppMappingArgs()
{
EditorApplication.quitting += RestoreOnQuit;
EditorApplication.delayCall += () =>
{
if (!File.Exists(PendingPath))
{
return;
}

// A domain reload during a build loses the update handler: set it again until the build is over.
if (BuildPipeline.isBuildingPlayer || SessionState.GetBool(InProgressKey, false))
{
EditorApplication.update += RestoreWhenBuildEnds;
}
else
{
ResolvePending(force: false);
}
};
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

static void RestoreOnQuit()
{
try
{
// A batchmode run that exits straight after a failed build never reaches an update: restore here (the
// change is written to ProjectSettings.asset before the editor exits, checked on 2022.3.62f3).
// Never while a build is still running.
if (!BuildPipeline.isBuildingPlayer)
{
ResolvePending(force: true);
}
}
catch (Exception)
{
}
}

static void RestoreWhenBuildEnds()
{
if (BuildPipeline.isBuildingPlayer)
{
return;
}

EditorApplication.update -= RestoreWhenBuildEnds;
ResolvePending(force: false);
}

static bool Applies(BuildReport report)
{
BuildTarget platform = report.summary.platform;
if (platform != BuildTarget.Android && platform != BuildTarget.iOS && platform != BuildTarget.tvOS)
{
return false;
}

// iOS has no Mono backend; Android builds Mono too, which writes no IL2CPP output.
bool il2cpp = platform != BuildTarget.Android ||
PlayerSettings.GetScriptingBackend(NamedBuildTarget.Android) == ScriptingImplementation.IL2CPP;
return il2cpp &&
BugseeCliRunner.HasAppToken &&
Environment.GetEnvironmentVariable("BUGSEE_NO_IL2CPP_MAPPING") == null &&
string.IsNullOrEmpty(Environment.GetEnvironmentVariable("BUGSEE_IL2CPP_MAPPING"));
}

public void OnPreprocessBuild(BuildReport report)
{
// A restore left by an earlier build is settled first (no build of this kind is running yet).
ResolvePending(force: true);
if (!Applies(report))
{
return;
}

string previous = PlayerSettings.GetAdditionalIl2CppArgs() ?? string.Empty;
string applied = Il2CppMappingArgs.Ensure(previous);
if (string.Equals(previous, applied, StringComparison.Ordinal))
{
return;
}

// The record is the only way back to the user's arguments: without it nothing is applied, and an
// unwritable Library/Bugsee must not fail the build over this optional feature.
try
{
Directory.CreateDirectory(Path.GetDirectoryName(PendingPath));
File.WriteAllText(PendingPath, JsonUtility.ToJson(new PendingRestore { previous = previous, applied = applied }));
}
catch (Exception ex)
{
Debug.LogWarning("[Bugsee] Could not save the IL2CPP arguments restore record (" + ex.Message +
"); not adding the mapping arguments, so the line-number map and MethodMap are not written for this build.");
return;
}

SessionState.SetBool(InProgressKey, true);
try
{
PlayerSettings.SetAdditionalIl2CppArgs(applied);
}
catch (Exception ex)
{
Debug.LogWarning("[Bugsee] Could not set the additional IL2CPP arguments: " + ex.Message);
SessionState.EraseBool(InProgressKey);
try { File.Delete(PendingPath); } catch (Exception) { }
return;
}

EditorApplication.update -= RestoreWhenBuildEnds;
EditorApplication.update += RestoreWhenBuildEnds;
Debug.Log("[Bugsee] Building with IL2CPP arguments \"" + applied + "\" so the line-number map and MethodMap are written (restored afterwards).");
}

public void OnPostprocessBuild(BuildReport report)
{
// Fast path: BuildPipeline.isBuildingPlayer is still true inside this callback.
ResolvePending(force: true);
}

/// <param name="force">The caller knows no build of ours is running (post-build callback, or before a new build).</param>
static void ResolvePending(bool force)
{
if (!File.Exists(PendingPath))
{
return;
}

PendingRestore pending = null;
try
{
pending = JsonUtility.FromJson<PendingRestore>(File.ReadAllText(PendingPath));
}
catch (Exception ex) when (Il2CppArgsRestorePolicy.IsTransientReadError(ex))
{
// Unreadable right now, not unparseable: keep the record for the next attempt.
Debug.LogWarning("[Bugsee] Could not read the IL2CPP arguments restore record: " + ex.Message);
return;
}
catch (Exception)
{
// Unparseable: nothing to restore from.
}

bool restoreThrew = false;
if (pending != null)
{
try
{
bool unchanged = string.Equals(PlayerSettings.GetAdditionalIl2CppArgs(), pending.applied, StringComparison.Ordinal);
switch (Il2CppArgsRestorePolicy.Decide(!force && BuildPipeline.isBuildingPlayer, unchanged))
{
case SymbolRestoreAction.Skip:
return;
case SymbolRestoreAction.Restore:
PlayerSettings.SetAdditionalIl2CppArgs(pending.previous ?? string.Empty);
Debug.Log("[Bugsee] Restored the additional IL2CPP arguments.");
break;
default:
Debug.Log("[Bugsee] The additional IL2CPP arguments were changed since the Bugsee build added the mapping arguments; keeping your setting.");
break;
}
}
catch (Exception ex)
{
restoreThrew = true;
Debug.LogWarning("[Bugsee] Could not restore the additional IL2CPP arguments (the record is kept for the next editor session): " + ex.Message);
}
}

if (Il2CppArgsRestorePolicy.DeleteRecord(pending != null, restoreThrew))
{
SessionState.EraseBool(InProgressKey);
try { File.Delete(PendingPath); } catch (Exception) { }
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
#endif
11 changes: 11 additions & 0 deletions Editor/BugseeIl2CppMappingArgs.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading