Diagnostics Persistence is a service that stores specific Inspect data on device across one or more reboots. If you need to track failure states, historical metrics, or other telemetry that must survive a restart, configure this service to save your data automatically.
Behavior and use cases
Persistence is particularly useful for recovering diagnostic data after:
- Unexpected reboots: Persistence saves data periodically based on your configured frequency and survives device restarts.
- Component crashes: Persistence never drops a value once it has been saved. For example, if your component publishes data in the first sample, but drops that data or crashes prior to the next sample, Persistence retains the original data in the persistence file.
Quickstart guide
To configure Persistence to save your component's Inspect data, follow these steps:
1. Identify your Inspect data
Select the specific data you need to save. You will need the exact INSPECT:
selector for your component's data
(e.g., INSPECT:core/pkg-resolver:root/resolver_service/active_package_resolves:*).
2. Create a configuration file
Create a new .persist file in either //src/diagnostics/config/persistence
or //vendor/*/diagnostics/config/persistence.
The file uses JSON5. Define the parameters for the data you wish to persist:
[
{
tag: "cache-fallbacks", // Unique name
service_name: "pkg-resolver", // Grouping for tags
max_bytes: 500, // Max size of the persisted data
min_seconds_between_fetch: 3600, // How frequently to sample the data
selectors: [
"INSPECT:core/pkg-resolver:root/resolver_service:cache_fallbacks_due_to_not_found",
"INSPECT:core/pkg-resolver:root/resolver_service/active_package_resolves:*",
],
},
]
3. Estimate max_bytes
The size of the persisted data is enforced at runtime. If your selectors fetch
more data than max_bytes, all of the saved data for this tag will be
permanently dropped and replaced with a single error string instead.
To estimate the correct max_bytes limit:
- Run your component on a device and populate the Inspect data you wish to persist. For more information on populating Inspect data, see Codelab: Using Inspect.
- Run
ffx inspect showlocally with your exact selectors, and pipe the output throughjqto strip away un-persisted data. Finally, count the bytes usingwc -c. For example:
ffx --machine json inspect show \
'core/pkg-resolver:root/resolver_service:cache_fallbacks_due_to_not_found' \
'core/pkg-resolver:root/resolver_service/active_package_resolves:*' \
| jq -c '.[] | pick(.moniker, .payload.root)' \
| wc -c
- Add a generous buffer (e.g., 20-50%) to this total to account for string length variations, future field additions, and JSON formatting overhead.
4. Update the build
Add your new configuration file to the build by adding it to the diagnostics-persistence
package configuration in //bundles/assembly/BUILD.gn.
Find the package_name = "diagnostics-persistence" block and add your .persist file
to the files list:
package_name = "diagnostics-persistence"
files = [
{
source = "//src/diagnostics/config/persistence/netstack.persist"
destination = "netstack.persist"
},
+ {
+ source = "//src/sys/pkg/bin/pkg-resolver/pkg-resolver.persist"
+ destination = "pkg-resolver.persist"
+ },
]
Reading persisted data
On the next boot (after the software update check completes), the saved data is re-published into Inspect.
The data is hosted by the diagnostics-persistence component. The original path
is prefixed with your configured service_name and tag.
$ ffx inspect show core/diagnostics/persistence
core/diagnostics/persistence:
root:
persist:
pkg-resolver:
cache-fallbacks:
core/pkg-resolver:
resolver_service:
cache_fallbacks_due_to_not_found: 2
Configuration reference
The .persist JSON5 file format expects an array of objects, where each object
defines a Persistence Tag. Each tag accepts the following fields:
| Field | Type | Description |
|---|---|---|
tag |
string | The unique identifier for this data collection within the service_name. Must be lowercase and hyphens only (e.g., "my-feature-stats"). |
service_name |
string | A grouping identifier for related tags. Must be lowercase and hyphens only (e.g., "my-service"). |
selectors |
[]string | A list of exact INSPECT: selectors to harvest and save. |
max_bytes |
integer | The maximum allowed size of the fetched Inspect payload in bytes. If the sampled data exceeds this limit, the saved data will be replaced with an error string. |
min_seconds_between_fetch |
integer | How frequently Archivist should sample these selectors. |
persist_across_boot |
boolean (default false) |
If true, saved data is not cleared on the next boot and will continue to accumulate historical boot data. |
Privacy considerations
Persistence is a powerful tool for debugging, but it can also be a privacy risk if not used carefully.
Cross-boot linkage: Enabling
persist_across_bootpreserves saved data across boots, accumulating historical data. This creates a long-term record of device usage, which could be used to track users across multiple boots. This can also violate other privacy safeguards, such as fingerprinting a device or user across a time-limited pseudonymous ID.Data retention: Persistence data is stored on the device and can be accessed by anyone with physical access to the device. It is important to consider the sensitivity of the data you are persisting and whether it should be protected with additional security measures.
Data minimization: Only persist the data that you need to debug your component. Avoid persisting unnecessary data, as this can increase the privacy risk.
FAQ
Does Persistence work with Lazy Nodes?
Yes. Persistence provides built-in support for Inspect Lazy Nodes. Persistence registers its required selectors and fetch frequencies with the Archivist. At each interval, the Archivist actively queries the component's selectors, which triggers the evaluation of any Lazy Nodes, allowing the system to save their dynamically generated data.