1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_system/crash_snapshot.h"
16
17 #include <optional>
18
19 #include "pw_multisink/util.h"
20 #include "pw_snapshot/uuid.h"
21 #include "pw_status/status.h"
22 #include "pw_system/device_handler.h"
23 #include "pw_system/log.h"
24
25 namespace pw::system {
26
27 namespace {
28
29 CrashSnapshot crash_snapshot;
30
31 PW_PLACE_IN_SECTION(".noinit")
32 CrashSnapshotPersistentBuffer persistent_crash_snapshot;
33
34 std::byte submessage_scratch_buffer
35 [snapshot::pwpb::Snapshot::kScratchBufferSizeBytes];
36
37 } // namespace
38
GetCrashSnapshotBuffer()39 CrashSnapshotPersistentBuffer& GetCrashSnapshotBuffer() {
40 return persistent_crash_snapshot;
41 }
42
HasCrashSnapshot()43 bool HasCrashSnapshot() { return persistent_crash_snapshot.has_value(); }
44
CrashSnapshot()45 CrashSnapshot::CrashSnapshot()
46 : writer_(persistent_crash_snapshot.GetWriter()) {}
47
Capture(const pw_cpu_exception_State & cpu_state,const std::string_view reason)48 void CrashSnapshot::Capture(const pw_cpu_exception_State& cpu_state,
49 const std::string_view reason) {
50 // clear any old snapshot data prior to populating a new crash snapshot.
51 persistent_crash_snapshot.clear();
52
53 snapshot::pwpb::Snapshot::StreamEncoder snapshot_encoder(
54 writer_, submessage_scratch_buffer);
55
56 // TODO: b/354772694 - handle encoder problems. Potentially write to memory
57 // and log on boot that the snapshot couldn't be written.
58 Status status = OkStatus();
59 status.Update(CaptureMetadata(reason, snapshot_encoder));
60 status.Update(device_handler::CaptureCpuState(cpu_state, snapshot_encoder));
61 status.Update(CaptureMainStackThread(cpu_state, snapshot_encoder));
62 status.Update(CaptureThreads(cpu_state, snapshot_encoder));
63 // Capture logs last as they can be large and fill
64 // the crash_snapshot buffer.
65 status.Update(CaptureLogs(snapshot_encoder));
66 status.Update(snapshot_encoder.status());
67 }
68
CaptureMetadata(const std::string_view reason,snapshot::pwpb::Snapshot::StreamEncoder & snapshot_encoder)69 Status CrashSnapshot::CaptureMetadata(
70 const std::string_view reason,
71 snapshot::pwpb::Snapshot::StreamEncoder& snapshot_encoder) {
72 snapshot::pwpb::Metadata::StreamEncoder metadata_encoder =
73 snapshot_encoder.GetMetadataEncoder();
74
75 // TODO: b/354770559 - generate a snapshot UUID.
76 std::optional<snapshot::ConstUuidSpan> snapshot_uuid;
77 if (snapshot_uuid.has_value()) {
78 // TODO: https://pwbug.dev/357138320 - Review IgnoreError calls in this
79 // file.
80 metadata_encoder.WriteSnapshotUuid(snapshot_uuid.value()).IgnoreError();
81 }
82
83 if (!reason.empty()) {
84 metadata_encoder.WriteReason(as_bytes(span(reason))).IgnoreError();
85 }
86
87 metadata_encoder.WriteFatal(true).IgnoreError();
88
89 // TODO: b/354775975 - populate the metadata with version, build uuid
90 // and project name.
91
92 device_handler::CapturePlatformMetadata(metadata_encoder);
93
94 return metadata_encoder.status();
95 }
96
CaptureMainStackThread(const pw_cpu_exception_State & cpu_state,snapshot::pwpb::Snapshot::StreamEncoder & snapshot_encoder)97 Status CrashSnapshot::CaptureMainStackThread(
98 const pw_cpu_exception_State& cpu_state,
99 snapshot::pwpb::Snapshot::StreamEncoder& snapshot_encoder) {
100 thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder* thread_info_encoder =
101 static_cast<thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder*>(
102 static_cast<protobuf::StreamEncoder*>(&snapshot_encoder));
103 return device_handler::CaptureMainStackThread(cpu_state,
104 *thread_info_encoder);
105 }
106
CaptureThreads(const pw_cpu_exception_State & cpu_state,snapshot::pwpb::Snapshot::StreamEncoder & snapshot_encoder)107 Status CrashSnapshot::CaptureThreads(
108 const pw_cpu_exception_State& cpu_state,
109 snapshot::pwpb::Snapshot::StreamEncoder& snapshot_encoder) {
110 thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder* thread_info_encoder =
111 static_cast<thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder*>(
112 static_cast<protobuf::StreamEncoder*>(&snapshot_encoder));
113 return device_handler::CaptureThreads(cpu_state.extended.psp,
114 *thread_info_encoder);
115 }
116
CaptureLogs(snapshot::pwpb::Snapshot::StreamEncoder & snapshot_encoder)117 Status CrashSnapshot::CaptureLogs(
118 snapshot::pwpb::Snapshot::StreamEncoder& snapshot_encoder) {
119 log::pwpb::LogEntries::StreamEncoder encoder(writer_, ByteSpan());
120 // Limit the captured logs to the latest entries that were
121 // added to log buffer.
122 size_t remaining_bytes = snapshot_encoder.ConservativeWriteLimit();
123 multisink::UnsafeDumpMultiSinkLogsFromEnd(
124 GetMultiSink(), encoder, remaining_bytes)
125 .IgnoreError();
126 return snapshot_encoder.status();
127 }
128
129 } // namespace pw::system
130