Skip to content

Commit 93892f6

Browse files
[#55] Show type name in build_report_packed_asset_contents_view
Populate the types table from TypeIdRegistry when importing a BuildReport so the packed asset contents view can display human-readable type names instead of numeric class ids, even when the build output is not analyzed alongside the report.
1 parent b4f09dc commit 93892f6

8 files changed

Lines changed: 76 additions & 24 deletions

File tree

Analyzer/Resources/Init.sql

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,14 @@ WHERE m.type = 'Material';
151151
-- see SerializedFileSQLiteWriter for details
152152
INSERT INTO types (id, name) VALUES (-1, 'Scene');
153153

154-
-- Database schema version. Bump when the schema changes in a way that tools relying on it
155-
-- (e.g. find-refs) cannot read from an older database. 1 = normalized refs table (issue #44);
156-
-- 2 = renamed assets/asset_dependencies tables to assetbundle_assets/preload_dependencies
157-
-- (issue #82); 3 = renamed asset_bundles table to archives and the asset_bundle column/alias
158-
-- to archive (issue #68); databases produced before versioning report 0.
159-
PRAGMA user_version = 3;
154+
-- Database schema version. Bump on any schema change so the version records which schema a given
155+
-- analyze.db was produced with. Commands that read an existing database (currently only find-refs)
156+
-- can compare against it to give a clean error instead of failing on a missing table or column.
157+
-- 1 = normalized refs table (issue #44); 2 = renamed assets/asset_dependencies tables to
158+
-- assetbundle_assets/preload_dependencies (issue #82); 3 = renamed asset_bundles table to archives
159+
-- and the asset_bundle column/alias to archive (issue #68); 4 = build_report_packed_asset_contents_view
160+
-- type column changed from numeric id to type name (issue #55); databases produced before versioning report 0.
161+
PRAGMA user_version = 4;
160162

161163
PRAGMA synchronous = OFF;
162164
PRAGMA journal_mode = MEMORY;

Analyzer/Resources/PackedAssets.sql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ SELECT
4545
pa.path,
4646
pac.packed_assets_id,
4747
pac.object_id,
48-
pac.type,
48+
-- Show the type name when known (populated from TypeIdRegistry or TypeTree analysis),
49+
-- otherwise fall back to the numeric class id as text.
50+
COALESCE(t.name, CAST(pac.type AS TEXT)) as type,
4951
pac.size,
5052
pac.offset,
5153
sa.source_asset_guid,
@@ -56,6 +58,7 @@ LEFT JOIN build_report_packed_assets pa ON pac.packed_assets_id = pa.id
5658
LEFT JOIN objects o ON o.id = pa.id
5759
INNER JOIN serialized_files sf ON o.serialized_file = sf.id
5860
LEFT JOIN build_report_source_assets sa ON pac.source_asset_id = sa.id
61+
LEFT JOIN types t ON pac.type = t.id
5962
LEFT JOIN objects br_obj ON o.serialized_file = br_obj.serialized_file AND br_obj.type = 1125
6063
LEFT JOIN build_report_archive_contents brac ON br_obj.id = brac.build_report_id AND pa.path = brac.archive_content;
6164

Analyzer/SQLite/Commands/AbstractCommand.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ internal abstract class AbstractCommand
1414

1515
protected virtual string DDLSource { get => null; }
1616

17+
// Conflict-resolution clause for the INSERT (e.g. "OR IGNORE"). Defaults to none, so a
18+
// duplicate primary key surfaces as an error - some tables rely on that to detect problems
19+
// such as the same SerializedFile being analyzed twice.
20+
protected virtual string ConflictClause => "";
21+
1722
// run data definition language commands to create
1823
// tables and views, run once at the beginning of creating
1924
// the database
@@ -31,7 +36,8 @@ public void CreateCommand(SqliteConnection database)
3136
RunDDL(database);
3237

3338
m_Command = database.CreateCommand();
34-
var commandText = new StringBuilder($"INSERT INTO {TableName} (");
39+
var insert = string.IsNullOrEmpty(ConflictClause) ? "INSERT" : $"INSERT {ConflictClause}";
40+
var commandText = new StringBuilder($"{insert} INTO {TableName} (");
3541
commandText.Append(string.Join(", ", Fields.Keys));
3642
commandText.Append(") VALUES (@");
3743
commandText.Append(string.Join(", @", Fields.Keys));

Analyzer/SQLite/Commands/SerializedFile/AddType.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ internal class AddType : AbstractCommand
1818

1919
protected override string DDLSource => null;
2020

21+
// The BuildReport PackedAssetsHandler may have already inserted a type by numeric id (with a
22+
// name from TypeIdRegistry). The TypeTree name here is authoritative and identical for known
23+
// ids, so ignore the conflict rather than crashing when both a report and its build output
24+
// are analyzed together.
25+
protected override string ConflictClause => "OR IGNORE";
26+
2127
protected override Dictionary<string, SqliteType> Fields => new()
2228
{
2329
{ "id", SqliteType.Integer },

Analyzer/SQLite/Handlers/PackedAssetsHandler.cs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using Microsoft.Data.Sqlite;
44
using UnityDataTools.Analyzer.SerializedObjects;
5+
using UnityDataTools.FileSystem;
56
using UnityDataTools.FileSystem.TypeTreeReaders;
67

78
namespace UnityDataTools.Analyzer.SQLite.Handlers;
@@ -12,6 +13,7 @@ public class PackedAssetsHandler : ISQLiteHandler
1213
private SqliteCommand m_InsertSourceAssetCommand;
1314
private SqliteCommand m_GetSourceAssetIdCommand;
1415
private SqliteCommand m_InsertContentsCommand;
16+
private SqliteCommand m_InsertTypeCommand;
1517
private Dictionary<(string guid, string path), long> m_SourceAssetCache = new();
1618

1719
public void Init(SqliteConnection db)
@@ -59,6 +61,15 @@ public void Init(SqliteConnection db)
5961
m_InsertContentsCommand.Parameters.Add("@size", SqliteType.Integer);
6062
m_InsertContentsCommand.Parameters.Add("@offset", SqliteType.Integer);
6163
m_InsertContentsCommand.Parameters.Add("@source_asset_id", SqliteType.Integer);
64+
65+
// A BuildReport records object types by numeric id only. When we can map an id to a
66+
// human-readable name via TypeIdRegistry, we add it to the shared types table so the
67+
// build report views can show the name. INSERT OR IGNORE keeps any name already inserted
68+
// by TypeTree analysis of the actual build output (which is authoritative).
69+
m_InsertTypeCommand = db.CreateCommand();
70+
m_InsertTypeCommand.CommandText = "INSERT OR IGNORE INTO types(id, name) VALUES(@id, @name)";
71+
m_InsertTypeCommand.Parameters.Add("@id", SqliteType.Integer);
72+
m_InsertTypeCommand.Parameters.Add("@name", SqliteType.Text);
6273
}
6374

6475
public void Process(Context ctx, long objectId, RandomAccessReader reader, out string name, out long streamDataSize)
@@ -93,14 +104,19 @@ public void Process(Context ctx, long objectId, RandomAccessReader reader, out s
93104
m_SourceAssetCache[cacheKey] = sourceAssetId;
94105
}
95106

107+
// Populate the types table so views can display the type name even when the build
108+
// output (and its TypeTrees) is not analyzed alongside the report.
109+
if (TypeIdRegistry.TryGetTypeName(content.Type, out var typeName))
110+
{
111+
m_InsertTypeCommand.Transaction = ctx.Transaction;
112+
m_InsertTypeCommand.Parameters["@id"].Value = content.Type;
113+
m_InsertTypeCommand.Parameters["@name"].Value = typeName;
114+
m_InsertTypeCommand.ExecuteNonQuery();
115+
}
116+
96117
m_InsertContentsCommand.Transaction = ctx.Transaction;
97118
m_InsertContentsCommand.Parameters["@packed_assets_id"].Value = objectId;
98119
m_InsertContentsCommand.Parameters["@object_id"].Value = content.ObjectID;
99-
100-
// TODO: Ideally we would also populate the type table if the content.Type is
101-
// not already in that table, and if we have a string value for it in TypeIdRegistry. That would
102-
// make it possible to view object types as strings, for the most common types, when importing a BuildReport
103-
// without the associated built content.
104120
m_InsertContentsCommand.Parameters["@type"].Value = content.Type;
105121
m_InsertContentsCommand.Parameters["@size"].Value = (long)content.Size;
106122
m_InsertContentsCommand.Parameters["@offset"].Value = (long)content.Offset;
@@ -122,6 +138,7 @@ void IDisposable.Dispose()
122138
m_InsertSourceAssetCommand?.Dispose();
123139
m_GetSourceAssetIdCommand?.Dispose();
124140
m_InsertContentsCommand?.Dispose();
141+
m_InsertTypeCommand?.Dispose();
125142
}
126143
}
127144

Documentation/buildreport.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -258,21 +258,20 @@ For consistency and clarity, database columns use slightly different names than
258258
| `build_report_packed_assets.path` | `PackedAssets.ShortPath` | Filename of the SerializedFile, .resS, or .resource file ("short" was redundant since only one path is recorded) |
259259
| `build_report_packed_assets.file_header_size` | `PackedAssets.Overhead` | Size of the file header (zero for .resS and .resource files) |
260260
| `build_report_packed_asset_info.object_id` | `PackedAssetInfo.fileID` | Local file ID of the object (renamed for consistency with `objects.object_id`) |
261-
| `build_report_packed_asset_info.type` | `PackedAssetInfo.classID` | Unity object type (renamed for consistency with `objects.type`) |
261+
| `build_report_packed_asset_info.type` | `PackedAssetInfo.classID` | Unity object type as a numeric [Class ID](https://docs.unity3d.com/Manual/ClassIDReference.html) (renamed for consistency with `objects.type`). `build_report_packed_asset_contents_view` exposes this as the type name. |
262262

263263
## Limitations
264264

265-
**Duplicate filenames:** Multiple build reports cannot be imported into the same database if they share the same filename. This is a general UnityDataTool limitation that assumes unique SerializedFile names.
266-
267-
**Type names:** While `build_report_packed_asset_info.type` records valid [Class IDs](https://docs.unity3d.com/Manual/ClassIDReference.html), the string type name may not exist in the `types` table. The `types` table is only populated when processing object instances (during TypeTree analysis). Analyzing both build output **and** build report together ensures types are fully populated; otherwise only numeric IDs are available.
265+
**Duplicate filenames:** Multiple build reports cannot be imported into the same database if they share the same filename. This is a general UnityDataTool limitation that assumes unique SerializedFile names. The BuildReport files in the build history (introduced in Unity 6.6) intentionally have unique file names (incorporating the build session GUID), so analyze does not hit this issue when analyzing multiple entries from the BuildHistory folder. When analyzing multiple AssetBundle build reports, or build reports from older versions of unity, be sure to assign a unique filename to each. See also issue #36.
268266

269267
### Information Not Exported
270268

271269
Currently, only the most useful BuildReport data is extracted to SQL. Additional data may be added as needed:
272270

273-
* [Code stripping](https://docs.unity3d.com/ScriptReference/Build.Reporting.BuildReport-strippingInfo.html) appendix (IL2CPP Player builds)
274-
* [ScenesUsingAssets](https://docs.unity3d.com/ScriptReference/Build.Reporting.BuildReport-scenesUsingAssets.html) (detailed build reports)
275-
* `BuildReport.m_BuildSteps` array (SQL may not be ideal for this hierarchical data)
276-
* `BuildAssetBundleInfoSet` appendix (undocumented object listing files in each AssetBundle; `build_report_archive_contents` currently derives this from the File list)
271+
* [Code stripping](https://docs.unity3d.com/ScriptReference/Build.Reporting.BuildReport-strippingInfo.html) appendix (available only for IL2CPP Player builds)
272+
* [ScenesUsingAssets](https://docs.unity3d.com/ScriptReference/Build.Reporting.BuildReport-scenesUsingAssets.html) (available only in detailed build reports for Player builds)
273+
* `BuildReport.m_BuildSteps` array. Supporting this is low priority - SQL is not an ideal representation for this hierarchical data. Starting in Unity 6.6 the build steps are also reported in the BuildLog.jsonl file and Trace Event Profile (TEP) files, which are easy to parse.
274+
* `BuildAssetBundleInfoSet` appendix (undocumented object listing files in each AssetBundle; `build_report_archive_contents` currently derives this from the File list without relying on that data)
277275
* Analytics-only appendices (unlikely to be valuable for analysis)
278276

277+
Tip: All this additional BuildReport data can be viewed using the `dump` command.

UnityDataTool.Tests/BuildReportTests.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,10 +311,10 @@ public async Task Analyze_BuildReport_AssetBundle_ContainsPackedAssetsData()
311311
// Verify the specific content row (data[3] from the dump)
312312
const long objectId = -1350043613627603771;
313313
var contentRow = SQLTestHelper.QueryInt(db,
314-
$@"SELECT COUNT(*) FROM build_report_packed_asset_contents_view
315-
WHERE packed_assets_id = {packedAssetId}
314+
$@"SELECT COUNT(*) FROM build_report_packed_asset_contents_view
315+
WHERE packed_assets_id = {packedAssetId}
316316
AND object_id = {objectId}
317-
AND type = 28
317+
AND type = 'Texture2D'
318318
AND size = 204
319319
AND offset = 11840
320320
AND source_asset_guid = '8826f464101b93c4bb006e15a9aff317'
@@ -323,6 +323,15 @@ public async Task Analyze_BuildReport_AssetBundle_ContainsPackedAssetsData()
323323
Assert.AreEqual(1, contentRow,
324324
"Expected exactly one packed_asset_contents row matching the specified criteria");
325325

326+
// The type column shows the human-readable name (from TypeIdRegistry) even though the
327+
// build report records only the numeric class id and no build output was analyzed here.
328+
SQLTestHelper.AssertQueryString(db,
329+
$@"SELECT type FROM build_report_packed_asset_contents_view
330+
WHERE packed_assets_id = {packedAssetId}
331+
AND object_id = {objectId}",
332+
"Texture2D",
333+
"Expected type name 'Texture2D' in build_report_packed_asset_contents_view");
334+
326335
// Verify the view works correctly for this content row
327336
SQLTestHelper.AssertQueryString(db,
328337
$@"SELECT source_asset_guid FROM build_report_packed_asset_contents_view

UnityFileSystem/TypeIdRegistry.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,5 +272,15 @@ public static string GetTypeName(int typeId)
272272
? name
273273
: typeId.ToString();
274274
}
275+
276+
/// <summary>
277+
/// Looks up the type name for a TypeId, returning false when the id is not a known type.
278+
/// Unlike GetTypeName, this does not fall back to the numeric id, so callers can tell
279+
/// whether a real name is available.
280+
/// </summary>
281+
public static bool TryGetTypeName(int typeId, out string name)
282+
{
283+
return s_KnownTypes.TryGetValue(typeId, out name);
284+
}
275285
}
276286

0 commit comments

Comments
 (0)