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