xref: /aosp_15_r20/external/pigweed/pw_system/crash_snapshot.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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