Skip to content

Commit 6a586c7

Browse files
[#110] Add Player build report example queries matching Project Auditor views (#111)
Includes modest schema update: build_report_source_assets gains asset_name (filename without extension) and asset_extension
1 parent 5a1677f commit 6a586c7

11 files changed

Lines changed: 365 additions & 8 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ CLI entry point is `UnityDataTool/Program.cs` using System.CommandLine. Per-comm
132132
### Extending Analyze
133133

134134
* New Unity types can be added by following the same pattern as the existing types, for example MonoScripts.
135+
* Any database schema change (new or changed tables, views, or columns) must bump `PRAGMA user_version` in `Analyzer/Resources/Init.sql` and extend the version-history comment above it.
135136
* Analysis of additional file formats could be added, for example AssetBundle manifest files by following the pattern of Addressables build layout files are handled.
136137

137138
### Other Extensions

Analyzer/Resources/Init.sql

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,10 @@ INSERT INTO types (id, name) VALUES (-1, 'Scene');
203203
-- assetbundle_assets/preload_dependencies (issue #82); 3 = renamed asset_bundles table to archives
204204
-- and the asset_bundle column/alias to archive (issue #68); 4 = build_report_packed_asset_contents_view
205205
-- type column changed from numeric id to type name (issue #55); 5 = added dangling_refs table/view
206-
-- (issue #85); 6 = archives.name is unique (issue #51); databases produced before versioning report 0.
207-
PRAGMA user_version = 6;
206+
-- (issue #85); 6 = archives.name is unique (issue #51); 7 = Unity 6.6 build_reports columns and
207+
-- build_report_content_* tables (issue #107), asset_name/asset_extension columns on
208+
-- build_report_source_assets (issue #110); databases produced before versioning report 0.
209+
PRAGMA user_version = 7;
208210

209211
PRAGMA synchronous = OFF;
210212
PRAGMA journal_mode = MEMORY;

Analyzer/Resources/PackedAssets.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ CREATE TABLE IF NOT EXISTS build_report_source_assets(
99
id INTEGER PRIMARY KEY AUTOINCREMENT,
1010
source_asset_guid TEXT NOT NULL,
1111
build_time_asset_path TEXT NOT NULL,
12+
-- Derived from build_time_asset_path for convenient grouping: the filename without its
13+
-- extension, and the lower-cased extension without the dot (empty when there is none).
14+
asset_name TEXT NOT NULL,
15+
asset_extension TEXT NOT NULL,
1216
UNIQUE(source_asset_guid, build_time_asset_path)
1317
);
1418

@@ -52,6 +56,8 @@ SELECT
5256
pac.offset,
5357
sa.source_asset_guid,
5458
sa.build_time_asset_path,
59+
sa.asset_name,
60+
sa.asset_extension,
5561
br_obj.id as build_report_id
5662
FROM build_report_packed_asset_info pac
5763
LEFT JOIN build_report_packed_assets pa ON pac.packed_assets_id = pa.id

Analyzer/SQLite/Handlers/PackedAssetsHandler.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.IO;
34
using Microsoft.Data.Sqlite;
45
using UnityDataTools.Analyzer.SerializedObjects;
56
using UnityDataTools.FileSystem;
@@ -43,12 +44,14 @@ public void Init(SqliteConnection db)
4344

4445
m_InsertSourceAssetCommand = db.CreateCommand();
4546
m_InsertSourceAssetCommand.CommandText = @"INSERT OR IGNORE INTO build_report_source_assets(
46-
source_asset_guid, build_time_asset_path
47+
source_asset_guid, build_time_asset_path, asset_name, asset_extension
4748
) VALUES(
48-
@source_asset_guid, @build_time_asset_path
49+
@source_asset_guid, @build_time_asset_path, @asset_name, @asset_extension
4950
)";
5051
m_InsertSourceAssetCommand.Parameters.Add("@source_asset_guid", SqliteType.Text);
5152
m_InsertSourceAssetCommand.Parameters.Add("@build_time_asset_path", SqliteType.Text);
53+
m_InsertSourceAssetCommand.Parameters.Add("@asset_name", SqliteType.Text);
54+
m_InsertSourceAssetCommand.Parameters.Add("@asset_extension", SqliteType.Text);
5255

5356
m_GetSourceAssetIdCommand = db.CreateCommand();
5457
m_GetSourceAssetIdCommand.CommandText = @"SELECT id FROM build_report_source_assets
@@ -117,10 +120,14 @@ public void Process(Context ctx, long objectId, RandomAccessReader reader, out s
117120
var cacheKey = (content.SourceAssetGUID, content.BuildTimeAssetPath);
118121
if (!m_SourceAssetCache.TryGetValue(cacheKey, out long sourceAssetId))
119122
{
120-
// Insert the source asset (will be ignored if it already exists)
123+
// Insert the source asset (will be ignored if it already exists). The name and
124+
// extension are precomputed from the path because extracting them in SQLite
125+
// queries is awkward (no reverse string search).
121126
m_InsertSourceAssetCommand.Transaction = ctx.Transaction;
122127
m_InsertSourceAssetCommand.Parameters["@source_asset_guid"].Value = content.SourceAssetGUID;
123128
m_InsertSourceAssetCommand.Parameters["@build_time_asset_path"].Value = content.BuildTimeAssetPath;
129+
m_InsertSourceAssetCommand.Parameters["@asset_name"].Value = Path.GetFileNameWithoutExtension(content.BuildTimeAssetPath);
130+
m_InsertSourceAssetCommand.Parameters["@asset_extension"].Value = Path.GetExtension(content.BuildTimeAssetPath).TrimStart('.').ToLowerInvariant();
124131
m_InsertSourceAssetCommand.ExecuteNonQuery();
125132

126133
// Get the ID (whether just inserted or already existing)
171 KB
Loading
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
# Example queries for Player build reports
2+
3+
This page shows how to answer common questions about a Player build using the build report data in an [analyze](command-analyze.md) database. The examples reproduce the data views of the **Build** section of the [Project Auditor](https://docs.unity3d.com/6000.6/Documentation/Manual/project-auditor/build-view-reference.html) package.
4+
5+
Each view maps to a short SQL query. Because the queries run against an SQLite database, this approach handles much larger builds than the Project Auditor UI, and the queries can be incorporated into scripts and custom tools. Project Auditor only shows the most recent clean build, while these queries work against any build report in your build history (see [Analyzing multiple build reports](buildreport.md#analyzing-multiple-build-reports)).
6+
7+
See [BuildReport Support](buildreport.md) for the full description of the imported data and the database schema, and [Example usage of Analyze](analyze-examples.md) for general tips about running queries (including from the command line).
8+
9+
The example output on this page comes from a Windows Player build of the [Happy Harvest](https://assetstore.unity.com/packages/essentials/tutorial-projects/happy-harvest-2d-sample-project-259218) sample project, made with Unity 6.6. That build report is included in this repository as a test file, so you can reproduce the output:
10+
11+
```
12+
UnityDataTool analyze TestCommon/Data/BuildReports/happyHarvest.buildreport -o Analysis.db
13+
sqlite3 Analysis.db ".mode column" "<query>"
14+
```
15+
16+
## General build information
17+
18+
The build summary lands in the `build_reports` table, one row per report. The `.mode line` output format of sqlite3 suits this one-row result:
19+
20+
```
21+
sqlite3 Analysis.db ".mode line" "SELECT build_name, platform_name, build_result, start_time, end_time, total_time_seconds, printf('%.1f MB', total_size / 1024.0 / 1024.0) AS total_size, output_path FROM build_reports;"
22+
```
23+
24+
```
25+
build_name = perf.u6.happy-harvest
26+
platform_name = Win64
27+
build_result = Succeeded
28+
start_time = 2026-07-22T18:50:13.1761397Z
29+
end_time = 2026-07-22T18:51:41.7167432Z
30+
total_time_seconds = 89
31+
total_size = 280.0 MB
32+
output_path = D:/UnitySrc/perf.u6.happy-harvest/Build/perf.u6.happy-harvest.exe
33+
```
34+
35+
`SELECT * FROM build_reports` shows the full set of recorded columns (error and warning counts, build GUID, build options, etc.). In Unity 6.6+ the [Build Analysis window](https://docs.unity3d.com/6000.7/Documentation/Manual/build-analysis-window-reference.html) shows similar summary information directly in the Editor.
36+
37+
## Size by runtime type
38+
39+
A breakdown of the content size by Unity object type, ordered by total size. For Unity 6.6+ reports this is precalculated in the ContentSummary:
40+
41+
```sql
42+
SELECT type_name, printf('%.1f MB', size / 1024.0 / 1024.0) AS pretty_size, object_count
43+
FROM build_report_content_type_stats_view
44+
ORDER BY size DESC LIMIT 10;
45+
```
46+
47+
```
48+
type_name pretty_size object_count
49+
-------------- ----------- ------------
50+
Texture2D 101.7 MB 589
51+
AudioClip 9.2 MB 74
52+
Shader 2.5 MB 96
53+
Font 1.5 MB 3
54+
ComputeShader 1.4 MB 104
55+
MonoBehaviour 0.7 MB 1768
56+
ParticleSystem 0.7 MB 91
57+
Sprite 0.6 MB 688
58+
TextAsset 0.4 MB 5
59+
Tilemap 0.4 MB 15
60+
```
61+
62+
Reports from Unity versions before 6.6 have no ContentSummary, but the same breakdown can be computed from the per-object PackedAssets data:
63+
64+
```sql
65+
SELECT type, COUNT(*) AS objects, printf('%.1f MB', SUM(size) / 1024.0 / 1024.0) AS pretty_size
66+
FROM build_report_packed_asset_contents_view
67+
GROUP BY type ORDER BY SUM(size) DESC LIMIT 10;
68+
```
69+
70+
This produces the same numbers for this build (the sizes include the resource data that types like Texture2D and AudioClip store in `.resS` and `.resource` files).
71+
72+
## Objects grouped by source asset
73+
74+
The rest of the examples use `build_report_packed_asset_contents_view`, which has one row per object (or resource blob) in the build output, with its type, size, containing build file, and originating source asset. This is the data behind the object views of Project Auditor, such as this grouping by source asset:
75+
76+
![](ProjectAuditor-ObjectsBySourceAsset.png)
77+
78+
The collapsed group rows correspond to a `GROUP BY` query. The `asset_name` column holds the source asset's filename without extension, matching how the UI names groups. This example also computes each group's share of the total content size, like the UI's "Size % (of Data)" column:
79+
80+
```sql
81+
SELECT asset_name, COUNT(*) AS objects, printf('%.1f MB', SUM(size) / 1024.0 / 1024.0) AS pretty_size,
82+
printf('%.1f%%', 100.0 * SUM(size) / (SELECT SUM(size) FROM build_report_packed_asset_info)) AS data_pct
83+
FROM build_report_packed_asset_contents_view
84+
GROUP BY asset_name ORDER BY SUM(size) DESC LIMIT 5;
85+
```
86+
87+
```
88+
asset_name objects pretty_size data_pct
89+
----------------------------------- ------- ----------- --------
90+
SpriteAtlas_Tiles 7 48.2 MB 40.0%
91+
Sprite_Pinetree_normal 2 5.1 MB 4.2%
92+
Sprite_Pinetree 3 3.8 MB 3.2%
93+
Sprite_Pinetree_mask 2 3.8 MB 3.2%
94+
Background ambience outside - Night 2 3.8 MB 3.1%
95+
```
96+
97+
Expanding a group in the UI corresponds to a filtered query listing the individual objects. Filtering on the full asset path avoids mixing up assets that share a name:
98+
99+
```sql
100+
SELECT type, size, path AS build_file
101+
FROM build_report_packed_asset_contents_view
102+
WHERE build_time_asset_path = 'Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 8.prefab'
103+
ORDER BY size;
104+
```
105+
106+
```
107+
type size build_file
108+
----------------- ---- --------------------
109+
GameObject 35 sharedassets2.assets
110+
GameObject 67 sharedassets2.assets
111+
Transform 68 sharedassets2.assets
112+
Transform 80 sharedassets2.assets
113+
PolygonCollider2D 180 sharedassets2.assets
114+
SpriteRenderer 212 sharedassets2.assets
115+
```
116+
117+
> **Note:** The build report records which source asset each object came from, but not the object's name. The `find-refs` command and the `objects` table can help identify specific objects when the build output itself is also analyzed — see [Cross-referencing with build output](buildreport.md#cross-referencing-with-build-output).
118+
119+
## Objects grouped by source file extension
120+
121+
The `asset_extension` column holds the source asset's file extension (lower-cased, without the dot). Grouping by it shows which kinds of source files contribute the most content. This is also the closest equivalent to Project Auditor's "Importer Type" grouping, because Unity selects the importer based on the file extension:
122+
123+
```sql
124+
SELECT asset_extension, COUNT(*) AS objects, printf('%.1f MB', SUM(size) / 1024.0 / 1024.0) AS pretty_size,
125+
printf('%.1f%%', 100.0 * SUM(size) / (SELECT SUM(size) FROM build_report_packed_asset_info)) AS data_pct
126+
FROM build_report_packed_asset_contents_view
127+
GROUP BY asset_extension ORDER BY SUM(size) DESC LIMIT 8;
128+
```
129+
130+
```
131+
asset_extension objects pretty_size data_pct
132+
--------------- ------- ----------- --------
133+
spriteatlasv2 7 48.2 MB 40.0%
134+
png 1039 37.1 MB 30.8%
135+
psd 154 13.0 MB 10.8%
136+
wav 74 9.2 MB 7.6%
137+
17 3.1 MB 2.6%
138+
vfx 185 2.9 MB 2.4%
139+
unity 7600 2.0 MB 1.7%
140+
ttf 9 1.5 MB 1.3%
141+
```
142+
143+
The row with the empty extension collects content with no normal source asset path, such as built-in resources (e.g. the splash screen logo) and objects with no recorded source asset. The `unity` row is the objects from the built scenes (Unity 6.6+ reports; older reports do not cover scene files).
144+
145+
Drill into one extension the same way as any other group:
146+
147+
```sql
148+
SELECT asset_name, type, printf('%.1f KB', size / 1024.0) AS pretty_size, path AS build_file
149+
FROM build_report_packed_asset_contents_view
150+
WHERE asset_extension = 'wav' ORDER BY size DESC LIMIT 5;
151+
```
152+
153+
```
154+
asset_name type pretty_size build_file
155+
----------------------------------- --------- ----------- ----------------------
156+
Background ambience outside - Night AudioClip 3864.0 KB sharedassets2.resource
157+
Background ambience outside - Day AudioClip 3829.3 KB sharedassets2.resource
158+
Rain AudioClip 1242.2 KB sharedassets2.resource
159+
Thunder AudioClip 197.5 KB sharedassets2.resource
160+
Watering crop-001 AudioClip 26.3 KB resources.resource
161+
```
162+
163+
## Objects grouped by build file
164+
165+
The `path` column is the file in the build output that contains the object: a SerializedFile (`level2`, `sharedassets0.assets`, ...) or a resource file (`.resS` for textures and meshes, `.resource` for audio and video):
166+
167+
```sql
168+
SELECT path AS build_file, COUNT(*) AS objects, printf('%.2f MB', SUM(size) / 1024.0 / 1024.0) AS pretty_size
169+
FROM build_report_packed_asset_contents_view
170+
GROUP BY path ORDER BY path;
171+
```
172+
173+
```
174+
build_file objects pretty_size
175+
------------------------------ ------- -----------
176+
globalgamemanagers.assets 1738 1.16 MB
177+
globalgamemanagers.assets.resS 38 2.95 MB
178+
level0 16 0.00 MB
179+
level1 17 0.00 MB
180+
level2 6521 1.82 MB
181+
level3 1046 0.16 MB
182+
resources.assets 1227 2.23 MB
183+
resources.assets.resS 85 59.32 MB
184+
resources.resource 23 0.17 MB
185+
sharedassets0.assets 72 1.73 MB
186+
...
187+
```
188+
189+
Listing the content of one build file (`ORDER BY offset` shows the objects in their order within the file):
190+
191+
```sql
192+
SELECT asset_name, type, printf('%.1f KB', size / 1024.0) AS pretty_size
193+
FROM build_report_packed_asset_contents_view
194+
WHERE path = 'sharedassets2.resource' ORDER BY asset_name;
195+
```
196+
197+
```
198+
asset_name type pretty_size
199+
----------------------------------- --------- -----------
200+
Background ambience outside - Day AudioClip 3829.3 KB
201+
Background ambience outside - Night AudioClip 3864.0 KB
202+
Chicken-001 AudioClip 8.1 KB
203+
Chicken-002 AudioClip 8.3 KB
204+
Close Window AudioClip 2.2 KB
205+
...
206+
```
207+
208+
## Objects grouped by runtime type
209+
210+
The [Size by runtime type](#size-by-runtime-type) section above shows the aggregate query. To see the individual objects of one type, and where they come from:
211+
212+
```sql
213+
SELECT asset_name, build_time_asset_path, size
214+
FROM build_report_packed_asset_contents_view
215+
WHERE type = 'AudioMixerGroup';
216+
```
217+
218+
```
219+
asset_name build_time_asset_path size
220+
---------- ------------------------------------------------ ----
221+
MainMixer Assets/HappyHarvest/Common/Audio/MainMixer.mixer 40
222+
MainMixer Assets/HappyHarvest/Common/Audio/MainMixer.mixer 68
223+
MainMixer Assets/HappyHarvest/Common/Audio/MainMixer.mixer 40
224+
```
225+
226+
## Objects grouped by source asset path
227+
228+
Grouping by the full `build_time_asset_path` distinguishes same-named assets in different folders, and a `LIKE` filter on the path restricts the report to one part of the project — something a fixed UI grouping cannot do:
229+
230+
```sql
231+
SELECT build_time_asset_path, COUNT(*) AS objects, printf('%.1f KB', SUM(size) / 1024.0) AS pretty_size
232+
FROM build_report_packed_asset_contents_view
233+
WHERE build_time_asset_path LIKE 'Assets/HappyHarvest/Art/Tiles/Fence/%'
234+
GROUP BY build_time_asset_path ORDER BY build_time_asset_path;
235+
```
236+
237+
```
238+
build_time_asset_path objects pretty_size
239+
----------------------------------------------------------- ------- -----------
240+
Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 1.prefab 6 0.6 KB
241+
Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 10.prefab 6 0.6 KB
242+
Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 11.prefab 6 0.6 KB
243+
Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 12.prefab 6 0.6 KB
244+
Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 13.prefab 6 0.6 KB
245+
...
246+
```
247+
248+
Paths outside `Assets/` also appear: package assets (`Packages/...`), built-in resources (`Resources/unity_builtin_extra`), and generated content.
249+
250+
## Beyond a single Player build report
251+
252+
The queries work the same for AssetBundle builds made with `BuildPipeline.BuildAssetBundles`, because those reports populate the same data. For those builds, the `archive` column of `build_report_packed_asset_contents_view` holds the name of the AssetBundle containing each object (for Player builds it is NULL), so you can add it to a query's columns or `GROUP BY` to break the results down by AssetBundle.
253+
254+
The examples above assume a single build report in the database. If you analyze several reports together, add a filter such as `WHERE build_report_filename = '...'` (or `build_report_id`) to the queries — the views expose both columns.

Documentation/analyze-examples.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ Shader Graphs/CustomLightingBuildingsB 113.4 KB 1b2fdfe013c58ffd57d7663
7070

7171
See [buildreport.md](buildreport.md) for information about using analyze to look at BuildReport files.
7272

73+
Example queries against build report data — build summary, size by type, and objects grouped by source asset, file extension, or build file — are collected on a dedicated page: [Example queries for Player build reports](analyze-examples-buildreport.md).
74+
7375

7476

7577
## Example: Using AI tools to help write queries

0 commit comments

Comments
 (0)