xref: /aosp_15_r20/external/pigweed/pw_snapshot/setup.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker.. _module-pw_snapshot-setup:
2*61c4878aSAndroid Build Coastguard Worker
3*61c4878aSAndroid Build Coastguard Worker==============================
4*61c4878aSAndroid Build Coastguard WorkerSetting up a Snapshot Pipeline
5*61c4878aSAndroid Build Coastguard Worker==============================
6*61c4878aSAndroid Build Coastguard Worker
7*61c4878aSAndroid Build Coastguard Worker-------------------
8*61c4878aSAndroid Build Coastguard WorkerCrash Handler Setup
9*61c4878aSAndroid Build Coastguard Worker-------------------
10*61c4878aSAndroid Build Coastguard WorkerThe Snapshot proto was designed first and foremost as a crash reporting format.
11*61c4878aSAndroid Build Coastguard WorkerThis section covers how to set up a crash handler to capture Snapshots.
12*61c4878aSAndroid Build Coastguard Worker
13*61c4878aSAndroid Build Coastguard Worker.. image:: images/generic_crash_flow.svg
14*61c4878aSAndroid Build Coastguard Worker  :width: 600
15*61c4878aSAndroid Build Coastguard Worker  :alt: Generic crash handler flow
16*61c4878aSAndroid Build Coastguard Worker
17*61c4878aSAndroid Build Coastguard WorkerA typical crash handler has two entry points:
18*61c4878aSAndroid Build Coastguard Worker
19*61c4878aSAndroid Build Coastguard Worker1. A software entry path through developer-written ASSERT() or CHECK() calls
20*61c4878aSAndroid Build Coastguard Worker   that indicate a device should go down for a crash if a condition is not met.
21*61c4878aSAndroid Build Coastguard Worker2. A hardware-triggered exception handler path that is initiated when a CPU
22*61c4878aSAndroid Build Coastguard Worker   encounters a fault signal (invalid memory access, bad instruction, etc.).
23*61c4878aSAndroid Build Coastguard Worker
24*61c4878aSAndroid Build Coastguard WorkerBefore deferring to a common crash handler, these entry paths should disable
25*61c4878aSAndroid Build Coastguard Workerinterrupts to force the system into a single-threaded execution mode. This
26*61c4878aSAndroid Build Coastguard Workerprevents other threads from operating on potentially bad data or clobbering
27*61c4878aSAndroid Build Coastguard Workersystem state that could be useful for debugging.
28*61c4878aSAndroid Build Coastguard Worker
29*61c4878aSAndroid Build Coastguard WorkerThe first step in a crash handler should always be a check for nested crashes to
30*61c4878aSAndroid Build Coastguard Workerprevent infinitely recursive crashes. Once it's deemed it's safe to continue,
31*61c4878aSAndroid Build Coastguard Workerthe crash handler can re-initialize logging, initialize storage for crash report
32*61c4878aSAndroid Build Coastguard Workercapture, and then build a snapshot to later be retrieved from the device. Once
33*61c4878aSAndroid Build Coastguard Workerthe crash report collection process is complete, some post-crash callbacks can
34*61c4878aSAndroid Build Coastguard Workerbe run on a best-effort basis to clean up the system before rebooting. For
35*61c4878aSAndroid Build Coastguard Workerdevices with debug port access, it's helpful to optionally hold the device in
36*61c4878aSAndroid Build Coastguard Workeran infinite loop rather than resetting to allow developers to access the device
37*61c4878aSAndroid Build Coastguard Workervia a hardware debugger.
38*61c4878aSAndroid Build Coastguard Worker
39*61c4878aSAndroid Build Coastguard WorkerAssert Handler Setup
40*61c4878aSAndroid Build Coastguard Worker====================
41*61c4878aSAndroid Build Coastguard Worker:ref:`pw_assert <module-pw_assert>` is Pigweed's entry point for software
42*61c4878aSAndroid Build Coastguard Workercrashes. Route any existing assert functions through pw_assert to centralize the
43*61c4878aSAndroid Build Coastguard Workersoftware crash path. You’ll need to create a :ref:`pw_assert backend
44*61c4878aSAndroid Build Coastguard Worker<module-pw_assert-backend_api>` or a custom :ref:`pw_assert_basic handler
45*61c4878aSAndroid Build Coastguard Worker<module-pw_assert_basic-custom_handler>` to pass collected information to a more
46*61c4878aSAndroid Build Coastguard Workersophisticated crash handler. One way to do this is to collect the data into a
47*61c4878aSAndroid Build Coastguard Workerstatically allocated struct that is passed to a common crash handler. It’s
48*61c4878aSAndroid Build Coastguard Workerimportant to immediately disable interrupts to prevent the system from doing
49*61c4878aSAndroid Build Coastguard Workerother things while in an impacted state.
50*61c4878aSAndroid Build Coastguard Worker
51*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
52*61c4878aSAndroid Build Coastguard Worker
53*61c4878aSAndroid Build Coastguard Worker   // This can be be directly accessed by a crash handler
54*61c4878aSAndroid Build Coastguard Worker   static CrashData crash_data;
55*61c4878aSAndroid Build Coastguard Worker   extern "C" void pw_assert_basic_HandleFailure(const char* file_name,
56*61c4878aSAndroid Build Coastguard Worker                                                 int line_number,
57*61c4878aSAndroid Build Coastguard Worker                                                 const char* format,
58*61c4878aSAndroid Build Coastguard Worker                                                 ...) {
59*61c4878aSAndroid Build Coastguard Worker     // Always disable interrupts first! How this is done depends
60*61c4878aSAndroid Build Coastguard Worker     // on your platform.
61*61c4878aSAndroid Build Coastguard Worker     __disable_irq();
62*61c4878aSAndroid Build Coastguard Worker
63*61c4878aSAndroid Build Coastguard Worker     va_list args;
64*61c4878aSAndroid Build Coastguard Worker     va_start(args, format);
65*61c4878aSAndroid Build Coastguard Worker     crash_data.file_name = file_name;
66*61c4878aSAndroid Build Coastguard Worker     crash_data.line_number = line_number;
67*61c4878aSAndroid Build Coastguard Worker     crash_data.reason_fmt = format;
68*61c4878aSAndroid Build Coastguard Worker     crash_data.reason_args = &args;
69*61c4878aSAndroid Build Coastguard Worker     crash_data.cpu_state = nullptr;
70*61c4878aSAndroid Build Coastguard Worker
71*61c4878aSAndroid Build Coastguard Worker     HandleCrash(crash_data);
72*61c4878aSAndroid Build Coastguard Worker     PW_UNREACHABLE;
73*61c4878aSAndroid Build Coastguard Worker   }
74*61c4878aSAndroid Build Coastguard Worker
75*61c4878aSAndroid Build Coastguard WorkerException Handler Setup
76*61c4878aSAndroid Build Coastguard Worker=======================
77*61c4878aSAndroid Build Coastguard Worker:ref:`pw_cpu_exception <module-pw_cpu_exception>` is Pigweed's recommended entry
78*61c4878aSAndroid Build Coastguard Workerpoint for CPU-triggered faults (divide by zero, invalid memory access, etc.).
79*61c4878aSAndroid Build Coastguard WorkerYou will need to provide a definition for pw_cpu_exception_DefaultHandler() that
80*61c4878aSAndroid Build Coastguard Workerpasses the exception state produced by pw_cpu_exception to your common crash
81*61c4878aSAndroid Build Coastguard Workerhandler.
82*61c4878aSAndroid Build Coastguard Worker
83*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
84*61c4878aSAndroid Build Coastguard Worker
85*61c4878aSAndroid Build Coastguard Worker   static CrashData crash_data;
86*61c4878aSAndroid Build Coastguard Worker   // This helper turns a format string to a va_list that can be used by the
87*61c4878aSAndroid Build Coastguard Worker   // common crash handling path.
88*61c4878aSAndroid Build Coastguard Worker   void HandleExceptionWithString(pw_cpu_exception_State& state,
89*61c4878aSAndroid Build Coastguard Worker                                  const char* fmt,
90*61c4878aSAndroid Build Coastguard Worker                                  ...) {
91*61c4878aSAndroid Build Coastguard Worker     va_list args;
92*61c4878aSAndroid Build Coastguard Worker     va_start(args, fmt);
93*61c4878aSAndroid Build Coastguard Worker     crash_data.cpu_state = state;
94*61c4878aSAndroid Build Coastguard Worker     crash_data.file_name = nullptr;
95*61c4878aSAndroid Build Coastguard Worker     crash_data.reason_fmt = fmt;
96*61c4878aSAndroid Build Coastguard Worker     crash_data.reason_args = &args;
97*61c4878aSAndroid Build Coastguard Worker
98*61c4878aSAndroid Build Coastguard Worker     HandleCrash(crash_data);
99*61c4878aSAndroid Build Coastguard Worker     PW_UNREACHABLE;
100*61c4878aSAndroid Build Coastguard Worker   }
101*61c4878aSAndroid Build Coastguard Worker
102*61c4878aSAndroid Build Coastguard Worker   extern "C" void pw_cpu_exception_DefaultHandler(
103*61c4878aSAndroid Build Coastguard Worker       pw_cpu_exception_State* state) {
104*61c4878aSAndroid Build Coastguard Worker     // Always disable interrupts first! How this is done depends
105*61c4878aSAndroid Build Coastguard Worker     // on your platform.
106*61c4878aSAndroid Build Coastguard Worker     __disable_irq();
107*61c4878aSAndroid Build Coastguard Worker
108*61c4878aSAndroid Build Coastguard Worker     crash_data.state = cpu_state;
109*61c4878aSAndroid Build Coastguard Worker     // The CFSR is an extremely useful register for understanding ARMv7-M and
110*61c4878aSAndroid Build Coastguard Worker     // ARMv8-M CPU faults. Other architectures should put something else here.
111*61c4878aSAndroid Build Coastguard Worker     HandleExceptionWithString(crash_data,
112*61c4878aSAndroid Build Coastguard Worker                               "Exception encountered, cfsr=0x%",
113*61c4878aSAndroid Build Coastguard Worker                               cpu_state->extended.cfsr);
114*61c4878aSAndroid Build Coastguard Worker   }
115*61c4878aSAndroid Build Coastguard Worker
116*61c4878aSAndroid Build Coastguard WorkerCommon Crash Handler Setup
117*61c4878aSAndroid Build Coastguard Worker==========================
118*61c4878aSAndroid Build Coastguard WorkerTo minimize duplication of crash handling logic, it's good practice to route the
119*61c4878aSAndroid Build Coastguard Workerpw_assert and pw_cpu_exception handlers to a common crash handling codepath.
120*61c4878aSAndroid Build Coastguard WorkerEnsure you can pass both pw_cpu_exception's CPU state and pw_assert's assert
121*61c4878aSAndroid Build Coastguard Workerinformation to the shared handler.
122*61c4878aSAndroid Build Coastguard Worker
123*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
124*61c4878aSAndroid Build Coastguard Worker
125*61c4878aSAndroid Build Coastguard Worker   struct CrashData {
126*61c4878aSAndroid Build Coastguard Worker     pw_cpu_exception_State *cpu_state;
127*61c4878aSAndroid Build Coastguard Worker     const char *reason_fmt;
128*61c4878aSAndroid Build Coastguard Worker     const va_list *reason_args;
129*61c4878aSAndroid Build Coastguard Worker     const char *file_name;
130*61c4878aSAndroid Build Coastguard Worker     int line_number;
131*61c4878aSAndroid Build Coastguard Worker   };
132*61c4878aSAndroid Build Coastguard Worker
133*61c4878aSAndroid Build Coastguard Worker   // This function assumes interrupts are properly disabled BEFORE it is called.
134*61c4878aSAndroid Build Coastguard Worker   [[noreturn]] void HandleCrash(CrashData& crash_info) {
135*61c4878aSAndroid Build Coastguard Worker     // Handle crash
136*61c4878aSAndroid Build Coastguard Worker   }
137*61c4878aSAndroid Build Coastguard Worker
138*61c4878aSAndroid Build Coastguard WorkerIn the crash handler your project can re-initialize a minimal subset of the
139*61c4878aSAndroid Build Coastguard Workersystem needed to safely capture a snapshot before rebooting the device. The
140*61c4878aSAndroid Build Coastguard Workerremainder of this section focuses on ways you can improve the reliability and
141*61c4878aSAndroid Build Coastguard Workerusability of your project's crash handler.
142*61c4878aSAndroid Build Coastguard Worker
143*61c4878aSAndroid Build Coastguard WorkerCheck for Nested Crashes
144*61c4878aSAndroid Build Coastguard Worker------------------------
145*61c4878aSAndroid Build Coastguard WorkerIt’s important to include crash handler checks that prevent infinite recursive
146*61c4878aSAndroid Build Coastguard Workernesting of crashes. Maintain a static variable that checks the crash nesting
147*61c4878aSAndroid Build Coastguard Workerdepth. After one or two nested crashes, abort crash handling entirely and reset
148*61c4878aSAndroid Build Coastguard Workerthe device or sit in an infinite loop to wait for a hardware debugger to attach.
149*61c4878aSAndroid Build Coastguard WorkerIt’s simpler to put this logic at the beginning of the shared crash handler, but
150*61c4878aSAndroid Build Coastguard Workerif your assert/exception handlers are complex it might be safer to inject the
151*61c4878aSAndroid Build Coastguard Workerchecks earlier in both codepaths.
152*61c4878aSAndroid Build Coastguard Worker
153*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
154*61c4878aSAndroid Build Coastguard Worker
155*61c4878aSAndroid Build Coastguard Worker   [[noreturn]] void HandleCrash(CrashData &crash_info) {
156*61c4878aSAndroid Build Coastguard Worker     static size_t crash_depth = 0;
157*61c4878aSAndroid Build Coastguard Worker     if (crash_depth > kMaxCrashDepth) {
158*61c4878aSAndroid Build Coastguard Worker       Abort(/*run_callbacks=*/false);
159*61c4878aSAndroid Build Coastguard Worker     }
160*61c4878aSAndroid Build Coastguard Worker     crash_depth++;
161*61c4878aSAndroid Build Coastguard Worker     ...
162*61c4878aSAndroid Build Coastguard Worker   }
163*61c4878aSAndroid Build Coastguard Worker
164*61c4878aSAndroid Build Coastguard WorkerRe-initialize Logging (Optional)
165*61c4878aSAndroid Build Coastguard Worker--------------------------------
166*61c4878aSAndroid Build Coastguard WorkerLogging can be helpful for debugging your crash handler, but depending on your
167*61c4878aSAndroid Build Coastguard Workerdevice/system design may be challenging to safely support at crash time. To
168*61c4878aSAndroid Build Coastguard Workerre-initialize logging, you’ll need to re-construct C++ objects and re-initialize
169*61c4878aSAndroid Build Coastguard Workerany systems/hardware in the logging codepath. You may even need an entirely
170*61c4878aSAndroid Build Coastguard Workerseparate logging pipeline that is single-threaded and interrupt-safe. Depending
171*61c4878aSAndroid Build Coastguard Workeron your system’s design, this may be difficult to set up.
172*61c4878aSAndroid Build Coastguard Worker
173*61c4878aSAndroid Build Coastguard WorkerReinitialize Dependencies
174*61c4878aSAndroid Build Coastguard Worker-------------------------
175*61c4878aSAndroid Build Coastguard WorkerIt's good practice to design a crash handler that can run before C++ static
176*61c4878aSAndroid Build Coastguard Workerconstructors have run. This means any initialization (whether manual or through
177*61c4878aSAndroid Build Coastguard Workerconstructors) that your crash handler depends on should be manually invoked at
178*61c4878aSAndroid Build Coastguard Workercrash time. If an initialization step might not be safe, evaluate if it's
179*61c4878aSAndroid Build Coastguard Workerpossible to omit the dependency.
180*61c4878aSAndroid Build Coastguard Worker
181*61c4878aSAndroid Build Coastguard WorkerSystem Cleanup
182*61c4878aSAndroid Build Coastguard Worker--------------
183*61c4878aSAndroid Build Coastguard WorkerAfter collecting a snapshot, some parts of your system may benefit from some
184*61c4878aSAndroid Build Coastguard Workercleanup before explicitly resetting a device. This might include flushing
185*61c4878aSAndroid Build Coastguard Workerbuffers or safely shutting down attached hardware. The order of shutdown should
186*61c4878aSAndroid Build Coastguard Workerbe deterministic, keeping in mind that any of these steps may have the potential
187*61c4878aSAndroid Build Coastguard Workerof causing a nested crash that skips the remainder of the handlers and forces
188*61c4878aSAndroid Build Coastguard Workerthe device to immediately reset.
189*61c4878aSAndroid Build Coastguard Worker
190*61c4878aSAndroid Build Coastguard Worker----------------------
191*61c4878aSAndroid Build Coastguard WorkerSnapshot Storage Setup
192*61c4878aSAndroid Build Coastguard Worker----------------------
193*61c4878aSAndroid Build Coastguard WorkerUse a storage class with a ``pw::stream::Writer`` interface to simplify
194*61c4878aSAndroid Build Coastguard Workercapturing a pw_snapshot proto. This can be a :ref:`pw::BlobStore
195*61c4878aSAndroid Build Coastguard Worker<module-pw_blob_store>`, an in-memory buffer that is flushed to flash, or a
196*61c4878aSAndroid Build Coastguard Worker:ref:`pw::PersistentBuffer <module-pw_persistent_ram-persistent_buffer>` that
197*61c4878aSAndroid Build Coastguard Workerlives in persistent memory. It's good practice to use lazy initialization for
198*61c4878aSAndroid Build Coastguard Workerstorage objects used by your Snapshot capture codepath.
199*61c4878aSAndroid Build Coastguard Worker
200*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
201*61c4878aSAndroid Build Coastguard Worker
202*61c4878aSAndroid Build Coastguard Worker   // Persistent RAM objects are highly available. They don't rely on
203*61c4878aSAndroid Build Coastguard Worker   // their constructor being run, and require no initialization.
204*61c4878aSAndroid Build Coastguard Worker   PW_PLACE_IN_SECTION(".noinit")
205*61c4878aSAndroid Build Coastguard Worker   pw::persistent_ram::PersistentBuffer<2048> persistent_snapshot;
206*61c4878aSAndroid Build Coastguard Worker
207*61c4878aSAndroid Build Coastguard Worker   void CaptureSnapshot(CrashInfo& crash_info) {
208*61c4878aSAndroid Build Coastguard Worker     ...
209*61c4878aSAndroid Build Coastguard Worker     persistent_snapshot.clear();
210*61c4878aSAndroid Build Coastguard Worker     PersistentBufferWriter& writer = persistent_snapshot.GetWriter();
211*61c4878aSAndroid Build Coastguard Worker     ...
212*61c4878aSAndroid Build Coastguard Worker   }
213*61c4878aSAndroid Build Coastguard Worker
214*61c4878aSAndroid Build Coastguard Worker----------------------
215*61c4878aSAndroid Build Coastguard WorkerSnapshot Capture Setup
216*61c4878aSAndroid Build Coastguard Worker----------------------
217*61c4878aSAndroid Build Coastguard Worker
218*61c4878aSAndroid Build Coastguard Worker.. note::
219*61c4878aSAndroid Build Coastguard Worker
220*61c4878aSAndroid Build Coastguard Worker  These instructions do not yet use the ``pw::protobuf::StreamEncoder``.
221*61c4878aSAndroid Build Coastguard Worker
222*61c4878aSAndroid Build Coastguard WorkerCapturing a snapshot is as simple as encoding any other proto message. Some
223*61c4878aSAndroid Build Coastguard Workermodules provide helper functions that will populate parts of a Snapshot, which
224*61c4878aSAndroid Build Coastguard Workereases the burden of custom work that must be set up uniquely for each project.
225*61c4878aSAndroid Build Coastguard Worker
226*61c4878aSAndroid Build Coastguard WorkerCapture Reason
227*61c4878aSAndroid Build Coastguard Worker==============
228*61c4878aSAndroid Build Coastguard WorkerA snapshot's "reason" should be considered the single most important field in a
229*61c4878aSAndroid Build Coastguard Workercaptured snapshot. If a snapshot capture was triggered by a crash, this should
230*61c4878aSAndroid Build Coastguard Workerbe the assert string. Other entry paths should describe here why the snapshot
231*61c4878aSAndroid Build Coastguard Workerwas captured ("Host communication buffer full!", "Exception encountered at
232*61c4878aSAndroid Build Coastguard Worker0x00000004", etc.).
233*61c4878aSAndroid Build Coastguard Worker
234*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
235*61c4878aSAndroid Build Coastguard Worker
236*61c4878aSAndroid Build Coastguard Worker   Status CaptureSnapshot(CrashData& crash_info) {
237*61c4878aSAndroid Build Coastguard Worker     // Temporary buffer for encoding "reason" to.
238*61c4878aSAndroid Build Coastguard Worker     static std::byte temp_buffer[500];
239*61c4878aSAndroid Build Coastguard Worker     // Temporary buffer to encode serialized proto to before dumping to the
240*61c4878aSAndroid Build Coastguard Worker     // final ``pw::stream::Writer``.
241*61c4878aSAndroid Build Coastguard Worker     static std::byte proto_encode_buffer[512];
242*61c4878aSAndroid Build Coastguard Worker     ...
243*61c4878aSAndroid Build Coastguard Worker     pw::protobuf::NestedEncoder<kMaxDepth> proto_encoder(proto_encode_buffer);
244*61c4878aSAndroid Build Coastguard Worker     pw::snapshot::Snapshot::Encoder snapshot_encoder(&proto_encoder);
245*61c4878aSAndroid Build Coastguard Worker     size_t length = snprintf(temp_buffer,
246*61c4878aSAndroid Build Coastguard Worker                              sizeof(temp_buffer,
247*61c4878aSAndroid Build Coastguard Worker                              crash_info.reason_fmt),
248*61c4878aSAndroid Build Coastguard Worker                              *crash_info.reason_args);
249*61c4878aSAndroid Build Coastguard Worker     snapshot_encoder.WriteReason(temp_buffer, length));
250*61c4878aSAndroid Build Coastguard Worker
251*61c4878aSAndroid Build Coastguard Worker     // Final encode and write.
252*61c4878aSAndroid Build Coastguard Worker     Result<ConstByteSpan> encoded_proto = proto_encoder.Encode();
253*61c4878aSAndroid Build Coastguard Worker     PW_TRY(encoded_proto.status());
254*61c4878aSAndroid Build Coastguard Worker     PW_TRY(writer.Write(encoded_proto.value()));
255*61c4878aSAndroid Build Coastguard Worker     ...
256*61c4878aSAndroid Build Coastguard Worker   }
257*61c4878aSAndroid Build Coastguard Worker
258*61c4878aSAndroid Build Coastguard WorkerCapture CPU State
259*61c4878aSAndroid Build Coastguard Worker=================
260*61c4878aSAndroid Build Coastguard WorkerWhen using pw_cpu_exception, exceptions will automatically collect CPU state
261*61c4878aSAndroid Build Coastguard Workerthat can be directly dumped into a snapshot. As it's not always easy to describe
262*61c4878aSAndroid Build Coastguard Workera CPU exception in a single "reason" string, this captures the information
263*61c4878aSAndroid Build Coastguard Workerneeded to more verbosely automatically generate a descriptive reason at analysis
264*61c4878aSAndroid Build Coastguard Workertime once the snapshot is retrieved from the device.
265*61c4878aSAndroid Build Coastguard Worker
266*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
267*61c4878aSAndroid Build Coastguard Worker
268*61c4878aSAndroid Build Coastguard Worker   Status CaptureSnapshot(CrashData& crash_info) {
269*61c4878aSAndroid Build Coastguard Worker     ...
270*61c4878aSAndroid Build Coastguard Worker
271*61c4878aSAndroid Build Coastguard Worker     proto_encoder.clear();
272*61c4878aSAndroid Build Coastguard Worker
273*61c4878aSAndroid Build Coastguard Worker     // Write CPU state.
274*61c4878aSAndroid Build Coastguard Worker     if (crash_info.cpu_state) {
275*61c4878aSAndroid Build Coastguard Worker       PW_TRY(DumpCpuStateProto(snapshot_encoder.GetArmv7mCpuStateEncoder(),
276*61c4878aSAndroid Build Coastguard Worker                                *crash_info.cpu_state));
277*61c4878aSAndroid Build Coastguard Worker
278*61c4878aSAndroid Build Coastguard Worker       // Final encode and write.
279*61c4878aSAndroid Build Coastguard Worker       Result<ConstByteSpan> encoded_proto = proto_encoder.Encode();
280*61c4878aSAndroid Build Coastguard Worker       PW_TRY(encoded_proto.status());
281*61c4878aSAndroid Build Coastguard Worker       PW_TRY(writer.Write(encoded_proto.value()));
282*61c4878aSAndroid Build Coastguard Worker     }
283*61c4878aSAndroid Build Coastguard Worker   }
284*61c4878aSAndroid Build Coastguard Worker
285*61c4878aSAndroid Build Coastguard Worker-----------------------
286*61c4878aSAndroid Build Coastguard WorkerSnapshot Transfer Setup
287*61c4878aSAndroid Build Coastguard Worker-----------------------
288*61c4878aSAndroid Build Coastguard WorkerPigweed’s pw_rpc system is well suited for retrieving a snapshot from a device.
289*61c4878aSAndroid Build Coastguard WorkerPigweed does not yet provide a generalized transfer service for moving files
290*61c4878aSAndroid Build Coastguard Workerto/from a device. When this feature is added to Pigweed, this section will be
291*61c4878aSAndroid Build Coastguard Workerupdated to include guidance for connecting a storage system to a transfer
292*61c4878aSAndroid Build Coastguard Workerservice.
293*61c4878aSAndroid Build Coastguard Worker
294*61c4878aSAndroid Build Coastguard Worker----------------------
295*61c4878aSAndroid Build Coastguard WorkerSnapshot Tooling Setup
296*61c4878aSAndroid Build Coastguard Worker----------------------
297*61c4878aSAndroid Build Coastguard WorkerWhen using the upstream ``Snapshot`` proto, you can directly use
298*61c4878aSAndroid Build Coastguard Worker``pw_snapshot.process`` to process snapshots into human-readable dumps. If
299*61c4878aSAndroid Build Coastguard Workeryou've opted to extend Pigweed's snapshot proto, you'll likely want to extend
300*61c4878aSAndroid Build Coastguard Workerthe processing tooling to handle custom project data as well. This can be done
301*61c4878aSAndroid Build Coastguard Workerby creating a light wrapper around
302*61c4878aSAndroid Build Coastguard Worker``pw_snapshot.processor.process_snapshots()``.
303*61c4878aSAndroid Build Coastguard Worker
304*61c4878aSAndroid Build Coastguard Worker.. code-block:: python
305*61c4878aSAndroid Build Coastguard Worker
306*61c4878aSAndroid Build Coastguard Worker   def _process_hw_failures(serialized_snapshot: bytes) -> str:
307*61c4878aSAndroid Build Coastguard Worker       """Custom handler that checks wheel state."""
308*61c4878aSAndroid Build Coastguard Worker       wheel_state = wheel_state_pb2.WheelStateSnapshot()
309*61c4878aSAndroid Build Coastguard Worker       output = []
310*61c4878aSAndroid Build Coastguard Worker       wheel_state.ParseFromString(serialized_snapshot)
311*61c4878aSAndroid Build Coastguard Worker
312*61c4878aSAndroid Build Coastguard Worker       if len(wheel_state.wheels) != 2:
313*61c4878aSAndroid Build Coastguard Worker           output.append(f'Expected 2 wheels, found {len(wheel_state.wheels)}')
314*61c4878aSAndroid Build Coastguard Worker
315*61c4878aSAndroid Build Coastguard Worker       if len(wheel_state.wheels) < 2:
316*61c4878aSAndroid Build Coastguard Worker           output.append('Wheels fell off!')
317*61c4878aSAndroid Build Coastguard Worker
318*61c4878aSAndroid Build Coastguard Worker       # And more...
319*61c4878aSAndroid Build Coastguard Worker
320*61c4878aSAndroid Build Coastguard Worker       return '\n'.join(output)
321*61c4878aSAndroid Build Coastguard Worker
322*61c4878aSAndroid Build Coastguard Worker
323*61c4878aSAndroid Build Coastguard Worker   def process_my_snapshots(serialized_snapshot: bytes) -> str:
324*61c4878aSAndroid Build Coastguard Worker       """Runs the snapshot processor with a custom callback."""
325*61c4878aSAndroid Build Coastguard Worker       return pw_snapshot.processor.process_snapshots(
326*61c4878aSAndroid Build Coastguard Worker           serialized_snapshot, user_processing_callback=_process_hw_failures)
327