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 AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ CLI entry point is `UnityDataTool/Program.cs` using System.CommandLine. Per-comm
### Extending Analyze

* New Unity types can be added by following the same pattern as the existing types, for example MonoScripts.
* 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.
* Analysis of additional file formats could be added, for example AssetBundle manifest files by following the pattern of Addressables build layout files are handled.

### Other Extensions
Expand Down
6 changes: 4 additions & 2 deletions Analyzer/Resources/Init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,10 @@ INSERT INTO types (id, name) VALUES (-1, 'Scene');
-- assetbundle_assets/preload_dependencies (issue #82); 3 = renamed asset_bundles table to archives
-- and the asset_bundle column/alias to archive (issue #68); 4 = build_report_packed_asset_contents_view
-- type column changed from numeric id to type name (issue #55); 5 = added dangling_refs table/view
-- (issue #85); 6 = archives.name is unique (issue #51); databases produced before versioning report 0.
PRAGMA user_version = 6;
-- (issue #85); 6 = archives.name is unique (issue #51); 7 = Unity 6.6 build_reports columns and
-- build_report_content_* tables (issue #107), asset_name/asset_extension columns on
-- build_report_source_assets (issue #110); databases produced before versioning report 0.
PRAGMA user_version = 7;

PRAGMA synchronous = OFF;
PRAGMA journal_mode = MEMORY;
6 changes: 6 additions & 0 deletions Analyzer/Resources/PackedAssets.sql
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ CREATE TABLE IF NOT EXISTS build_report_source_assets(
id INTEGER PRIMARY KEY AUTOINCREMENT,
source_asset_guid TEXT NOT NULL,
build_time_asset_path TEXT NOT NULL,
-- Derived from build_time_asset_path for convenient grouping: the filename without its
-- extension, and the lower-cased extension without the dot (empty when there is none).
asset_name TEXT NOT NULL,
asset_extension TEXT NOT NULL,
UNIQUE(source_asset_guid, build_time_asset_path)
);

Expand Down Expand Up @@ -52,6 +56,8 @@ SELECT
pac.offset,
sa.source_asset_guid,
sa.build_time_asset_path,
sa.asset_name,
sa.asset_extension,
br_obj.id as build_report_id
FROM build_report_packed_asset_info pac
LEFT JOIN build_report_packed_assets pa ON pac.packed_assets_id = pa.id
Expand Down
13 changes: 10 additions & 3 deletions Analyzer/SQLite/Handlers/PackedAssetsHandler.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Data.Sqlite;
using UnityDataTools.Analyzer.SerializedObjects;
using UnityDataTools.FileSystem;
Expand Down Expand Up @@ -43,12 +44,14 @@ public void Init(SqliteConnection db)

m_InsertSourceAssetCommand = db.CreateCommand();
m_InsertSourceAssetCommand.CommandText = @"INSERT OR IGNORE INTO build_report_source_assets(
source_asset_guid, build_time_asset_path
source_asset_guid, build_time_asset_path, asset_name, asset_extension
) VALUES(
@source_asset_guid, @build_time_asset_path
@source_asset_guid, @build_time_asset_path, @asset_name, @asset_extension
)";
m_InsertSourceAssetCommand.Parameters.Add("@source_asset_guid", SqliteType.Text);
m_InsertSourceAssetCommand.Parameters.Add("@build_time_asset_path", SqliteType.Text);
m_InsertSourceAssetCommand.Parameters.Add("@asset_name", SqliteType.Text);
m_InsertSourceAssetCommand.Parameters.Add("@asset_extension", SqliteType.Text);

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

// Get the ID (whether just inserted or already existing)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
254 changes: 254 additions & 0 deletions Documentation/analyze-examples-buildreport.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
# Example queries for Player build reports

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.

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)).

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).

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:

```
UnityDataTool analyze TestCommon/Data/BuildReports/happyHarvest.buildreport -o Analysis.db
sqlite3 Analysis.db ".mode column" "<query>"
```

## General build information

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:

```
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;"
```

```
build_name = perf.u6.happy-harvest
platform_name = Win64
build_result = Succeeded
start_time = 2026-07-22T18:50:13.1761397Z
end_time = 2026-07-22T18:51:41.7167432Z
total_time_seconds = 89
total_size = 280.0 MB
output_path = D:/UnitySrc/perf.u6.happy-harvest/Build/perf.u6.happy-harvest.exe
```

`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.

## Size by runtime type

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:

```sql
SELECT type_name, printf('%.1f MB', size / 1024.0 / 1024.0) AS pretty_size, object_count
FROM build_report_content_type_stats_view
ORDER BY size DESC LIMIT 10;
```

```
type_name pretty_size object_count
-------------- ----------- ------------
Texture2D 101.7 MB 589
AudioClip 9.2 MB 74
Shader 2.5 MB 96
Font 1.5 MB 3
ComputeShader 1.4 MB 104
MonoBehaviour 0.7 MB 1768
ParticleSystem 0.7 MB 91
Sprite 0.6 MB 688
TextAsset 0.4 MB 5
Tilemap 0.4 MB 15
```

Reports from Unity versions before 6.6 have no ContentSummary, but the same breakdown can be computed from the per-object PackedAssets data:

```sql
SELECT type, COUNT(*) AS objects, printf('%.1f MB', SUM(size) / 1024.0 / 1024.0) AS pretty_size
FROM build_report_packed_asset_contents_view
GROUP BY type ORDER BY SUM(size) DESC LIMIT 10;
```

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).

## Objects grouped by source asset

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:

![](ProjectAuditor-ObjectsBySourceAsset.png)

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:

```sql
SELECT asset_name, COUNT(*) AS objects, printf('%.1f MB', SUM(size) / 1024.0 / 1024.0) AS pretty_size,
printf('%.1f%%', 100.0 * SUM(size) / (SELECT SUM(size) FROM build_report_packed_asset_info)) AS data_pct
FROM build_report_packed_asset_contents_view
GROUP BY asset_name ORDER BY SUM(size) DESC LIMIT 5;
```

```
asset_name objects pretty_size data_pct
----------------------------------- ------- ----------- --------
SpriteAtlas_Tiles 7 48.2 MB 40.0%
Sprite_Pinetree_normal 2 5.1 MB 4.2%
Sprite_Pinetree 3 3.8 MB 3.2%
Sprite_Pinetree_mask 2 3.8 MB 3.2%
Background ambience outside - Night 2 3.8 MB 3.1%
```

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:

```sql
SELECT type, size, path AS build_file
FROM build_report_packed_asset_contents_view
WHERE build_time_asset_path = 'Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 8.prefab'
ORDER BY size;
```

```
type size build_file
----------------- ---- --------------------
GameObject 35 sharedassets2.assets
GameObject 67 sharedassets2.assets
Transform 68 sharedassets2.assets
Transform 80 sharedassets2.assets
PolygonCollider2D 180 sharedassets2.assets
SpriteRenderer 212 sharedassets2.assets
```

> **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).

## Objects grouped by source file extension

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:

```sql
SELECT asset_extension, COUNT(*) AS objects, printf('%.1f MB', SUM(size) / 1024.0 / 1024.0) AS pretty_size,
printf('%.1f%%', 100.0 * SUM(size) / (SELECT SUM(size) FROM build_report_packed_asset_info)) AS data_pct
FROM build_report_packed_asset_contents_view
GROUP BY asset_extension ORDER BY SUM(size) DESC LIMIT 8;
```

```
asset_extension objects pretty_size data_pct
--------------- ------- ----------- --------
spriteatlasv2 7 48.2 MB 40.0%
png 1039 37.1 MB 30.8%
psd 154 13.0 MB 10.8%
wav 74 9.2 MB 7.6%
17 3.1 MB 2.6%
vfx 185 2.9 MB 2.4%
unity 7600 2.0 MB 1.7%
ttf 9 1.5 MB 1.3%
```

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).

Drill into one extension the same way as any other group:

```sql
SELECT asset_name, type, printf('%.1f KB', size / 1024.0) AS pretty_size, path AS build_file
FROM build_report_packed_asset_contents_view
WHERE asset_extension = 'wav' ORDER BY size DESC LIMIT 5;
```

```
asset_name type pretty_size build_file
----------------------------------- --------- ----------- ----------------------
Background ambience outside - Night AudioClip 3864.0 KB sharedassets2.resource
Background ambience outside - Day AudioClip 3829.3 KB sharedassets2.resource
Rain AudioClip 1242.2 KB sharedassets2.resource
Thunder AudioClip 197.5 KB sharedassets2.resource
Watering crop-001 AudioClip 26.3 KB resources.resource
```

## Objects grouped by build file

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):

```sql
SELECT path AS build_file, COUNT(*) AS objects, printf('%.2f MB', SUM(size) / 1024.0 / 1024.0) AS pretty_size
FROM build_report_packed_asset_contents_view
GROUP BY path ORDER BY path;
```

```
build_file objects pretty_size
------------------------------ ------- -----------
globalgamemanagers.assets 1738 1.16 MB
globalgamemanagers.assets.resS 38 2.95 MB
level0 16 0.00 MB
level1 17 0.00 MB
level2 6521 1.82 MB
level3 1046 0.16 MB
resources.assets 1227 2.23 MB
resources.assets.resS 85 59.32 MB
resources.resource 23 0.17 MB
sharedassets0.assets 72 1.73 MB
...
```

Listing the content of one build file (`ORDER BY offset` shows the objects in their order within the file):

```sql
SELECT asset_name, type, printf('%.1f KB', size / 1024.0) AS pretty_size
FROM build_report_packed_asset_contents_view
WHERE path = 'sharedassets2.resource' ORDER BY asset_name;
```

```
asset_name type pretty_size
----------------------------------- --------- -----------
Background ambience outside - Day AudioClip 3829.3 KB
Background ambience outside - Night AudioClip 3864.0 KB
Chicken-001 AudioClip 8.1 KB
Chicken-002 AudioClip 8.3 KB
Close Window AudioClip 2.2 KB
...
```

## Objects grouped by runtime type

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:

```sql
SELECT asset_name, build_time_asset_path, size
FROM build_report_packed_asset_contents_view
WHERE type = 'AudioMixerGroup';
```

```
asset_name build_time_asset_path size
---------- ------------------------------------------------ ----
MainMixer Assets/HappyHarvest/Common/Audio/MainMixer.mixer 40
MainMixer Assets/HappyHarvest/Common/Audio/MainMixer.mixer 68
MainMixer Assets/HappyHarvest/Common/Audio/MainMixer.mixer 40
```

## Objects grouped by source asset path

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:

```sql
SELECT build_time_asset_path, COUNT(*) AS objects, printf('%.1f KB', SUM(size) / 1024.0) AS pretty_size
FROM build_report_packed_asset_contents_view
WHERE build_time_asset_path LIKE 'Assets/HappyHarvest/Art/Tiles/Fence/%'
GROUP BY build_time_asset_path ORDER BY build_time_asset_path;
```

```
build_time_asset_path objects pretty_size
----------------------------------------------------------- ------- -----------
Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 1.prefab 6 0.6 KB
Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 10.prefab 6 0.6 KB
Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 11.prefab 6 0.6 KB
Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 12.prefab 6 0.6 KB
Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 13.prefab 6 0.6 KB
...
```

Paths outside `Assets/` also appear: package assets (`Packages/...`), built-in resources (`Resources/unity_builtin_extra`), and generated content.

## Beyond a single Player build report

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.

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.
2 changes: 2 additions & 0 deletions Documentation/analyze-examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ Shader Graphs/CustomLightingBuildingsB 113.4 KB 1b2fdfe013c58ffd57d7663

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

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).



## Example: Using AI tools to help write queries
Expand Down
Loading
Loading