blob: 52fb42d53dac3d946f0e9c7ad7370ac07064c6e3 [file] [log] [blame]
.. _module-pw_snapshot-module_usage:
============
Module Usage
============
Right now, pw_snapshot just dictates a *format*. That means there is no provided
system information collection integration, underlying storage, or transport
mechanism to fetch a snapshot from a device. These must be set up independently
by your project.
-------------------
Building a Snapshot
-------------------
Even though a Snapshot is just a proto message, the potential size of the proto
makes it important to consider the encoder.
Nanopb is a popular encoder for embedded devices, it's impractical to use
with the pw_snapshot proto. Nanopb works by generating in-memory structs that
represent the protobuf message. Repeated, optional, and variable-length fields
increase the size of the in-memory struct. The struct representation
of snapshot-like protos can quickly near 10KB in size. Allocating 10KB
Pigweed's pw_protobuf is a better choice as its design is centered around
incrementally writing a proto directly to the final wire format. If you only
write a few fields in a snapshot, you can do so with minimal memory overhead.
.. code-block:: cpp
#include "pw_protobuf/encoder.h"
#include "pw_snapshot_protos/snapshot.pwpb.h"
Result<ConstByteSpan> EncodeSnapshot(pw::ByteSpan encode_buffer,
const CrashInfo &crash_info) {
// Instantiate a generic proto encoder.
pw::protobuf::NestedEncoder<kMaxNestedProtoDepth> proto_encoder(
encode_buffer);
// Get a proto-specific wrapper for the encoder.
pw::snapshot::Snapshot::Encoder snapshot_encoder(&proto_encoder);
{ // This scope is required to handle RAII behavior of the submessage.
// Start writing the Metadata submessage.
pw::snapshot::Metadata::Encoder metadata_encoder =
snapshot_encoder.GetMetadataEncoder();
metadata_encoder.WriteReason(EncodeReasonLog(crash_info));
metadata_encoder.WriteFatal(true);
metadata_encoder.WriteProjectName(std::as_bytes(std::span("smart-shoe")));
metadata_encoder.WriteDeviceName(
std::as_bytes(std::span("smart-shoe-p1")));
metadata_encoder.WriteUptime(
std::chrono::time_point_cast<std::chrono::milliseconds>(
pw::chrono::SystemClock::now()));
}
// Finalize the proto encode so it can be flushed somewhere.
return proto_encoder.Encode();
}
-------------------
Custom Project Data
-------------------
There are two main ways to add custom project-specific data to a snapshot. Tags
are the simplest way to capture small snippets of information that require
no or minimal post-processing. For more complex data, it's usually more
practical to extend the Snapshot proto.
Tags
====
Adding a key/value pair to the tags map is straightforward when using
pw_protobuf.
.. code-block:: cpp
{
pw::Snapshot::TagsEntry::Encoder tags_encoder =
snapshot_encoder.GetTagsEncoder();
tags_encoder.WriteKey("BtState");
tags_encoder.WriteValue("connected");
}
Extending the Proto
===================
Extending the Snapshot proto relies on proto behavior details that are explained
in the :ref:`Snapshot Proto Format<module-pw_snapshot-proto_format>`. Extending
the snapshot proto is as simple as defining a proto message that **only**
declares fields with numbers that are reserved by the Snapshot proto for
downstream projects. When encoding your snapshot, you can then write both the
upstream Snapshot proto and your project's custom extension proto message to the
same proto encoder.
The upstream snapshot tooling will ignore any project-specific proto data,
the proto data can be decoded a second time using a project-specific proto. At
that point, any handling logic of the project-specific data would have to be
done as part of project-specific tooling.
-------------------
Analyzing Snapshots
-------------------
Snapshots can be processed for analysis using the ``pw_snapshot.process`` python
tool. This tool turns a binary snapshot proto into human readable, actionable
information. As some snapshot fields may optionally be tokenized, a
pw_tokenizer database or ELF file with embedded pw_tokenizer tokens may
optionally be passed to the tool to detokenize applicable fields.
.. code-block:: sh
# Example invocation, which dumps to stdout by default.
$ python -m pw_snapshot.processor path/to/serialized_snapshot.bin
____ _ __ _____ _ _____ ____ _____ __ ______ ______
/ __ \ | / / / ___// | / / | / __ \/ ___// / / / __ \/_ __/
/ /_/ / | /| / / \__ \/ |/ / /| | / /_/ /\__ \/ /_/ / / / / / /
/ ____/| |/ |/ / ___/ / /| / ___ |/ ____/___/ / __ / /_/ / / /
/_/ |__/|__/____/____/_/ |_/_/ |_/_/ /____/_/ /_/\____/ /_/
/_____/
▪▄▄▄ ▄▄▄· ▄▄▄▄▄ ▄▄▄· ·
█▄▄▄▐█ ▀█ █▌ ▐█ ▀█
▄█▀▀█ █. ▄█▀▀█
▐▌ .▐█ ▪▐▌ ▪▐▌·▐█ ▪▐▌▐▌
· .▀▀
Device crash cause:
Assert failed: 1+1 == 42
Project name: gShoe
Device: GSHOE-QUANTUM_CORE-REV_0.1
Device FW version: QUANTUM_CORE-0.1.325-e4a84b1a
FW build UUID: ad2d39258c1bc487f07ca7e04991a836fdf7d0a0
Snapshot UUID: 8481bb12a162164f5c74855f6d94ea1a
Thread State
2 threads running, Main/Handler active at the time of capture.
~~~~~~~~~~~~
Thread (INTERRUPT_HANDLER): Main Stack (Handler Mode) <-- [ACTIVE]
Stack info
Stack used: 0x2001b000 - 0x2001ae20 (480 bytes)
Stack limits: 0x2001b000 - 0x???????? (size unknown)
Thread (RUNNING): Idle
Stack info
Stack used: 0x2001ac00 - 0x2001ab0c (244 bytes, 47.66%)
Stack limits: 0x2001ac00 - 0x2001aa00 (512 bytes)