blob: 6b5b5ac4b5c7c513c6e77ec3a04204323708ca35 [file] [log] [blame]
// Copyright 2024 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_system/crash_snapshot.h"
#include <optional>
#include "pw_multisink/util.h"
#include "pw_snapshot/uuid.h"
#include "pw_status/status.h"
#include "pw_system/device_handler.h"
#include "pw_system/log.h"
namespace pw::system {
namespace {
CrashSnapshot crash_snapshot;
PW_PLACE_IN_SECTION(".noinit")
CrashSnapshotPersistentBuffer persistent_crash_snapshot;
std::byte submessage_scratch_buffer
[snapshot::pwpb::Snapshot::kScratchBufferSizeBytes];
} // namespace
CrashSnapshotPersistentBuffer& GetCrashSnapshotBuffer() {
return persistent_crash_snapshot;
}
bool HasCrashSnapshot() { return persistent_crash_snapshot.has_value(); }
CrashSnapshot::CrashSnapshot()
: writer_(persistent_crash_snapshot.GetWriter()) {}
void CrashSnapshot::Capture(const pw_cpu_exception_State& cpu_state,
const std::string_view reason) {
// clear any old snapshot data prior to populating a new crash snapshot.
persistent_crash_snapshot.clear();
snapshot::pwpb::Snapshot::StreamEncoder snapshot_encoder(
writer_, submessage_scratch_buffer);
// TODO: b/354772694 - handle encoder problems. Potentially write to memory
// and log on boot that the snapshot couldn't be written.
Status status = OkStatus();
status.Update(CaptureMetadata(reason, snapshot_encoder));
status.Update(device_handler::CaptureCpuState(cpu_state, snapshot_encoder));
status.Update(CaptureMainStackThread(cpu_state, snapshot_encoder));
status.Update(CaptureThreads(cpu_state, snapshot_encoder));
// Capture logs last as they can be large and fill
// the crash_snapshot buffer.
status.Update(CaptureLogs(snapshot_encoder));
status.Update(snapshot_encoder.status());
}
Status CrashSnapshot::CaptureMetadata(
const std::string_view reason,
snapshot::pwpb::Snapshot::StreamEncoder& snapshot_encoder) {
snapshot::pwpb::Metadata::StreamEncoder metadata_encoder =
snapshot_encoder.GetMetadataEncoder();
// TODO: b/354770559 - generate a snapshot UUID.
std::optional<snapshot::ConstUuidSpan> snapshot_uuid;
if (snapshot_uuid.has_value()) {
// TODO: https://pwbug.dev/357138320 - Review IgnoreError calls in this
// file.
metadata_encoder.WriteSnapshotUuid(snapshot_uuid.value()).IgnoreError();
}
if (!reason.empty()) {
metadata_encoder.WriteReason(as_bytes(span(reason))).IgnoreError();
}
metadata_encoder.WriteFatal(true).IgnoreError();
// TODO: b/354775975 - populate the metadata with version, build uuid
// and project name.
device_handler::CapturePlatformMetadata(metadata_encoder);
return metadata_encoder.status();
}
Status CrashSnapshot::CaptureMainStackThread(
const pw_cpu_exception_State& cpu_state,
snapshot::pwpb::Snapshot::StreamEncoder& snapshot_encoder) {
thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder* thread_info_encoder =
static_cast<thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder*>(
static_cast<protobuf::StreamEncoder*>(&snapshot_encoder));
return device_handler::CaptureMainStackThread(cpu_state,
*thread_info_encoder);
}
Status CrashSnapshot::CaptureThreads(
const pw_cpu_exception_State& cpu_state,
snapshot::pwpb::Snapshot::StreamEncoder& snapshot_encoder) {
thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder* thread_info_encoder =
static_cast<thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder*>(
static_cast<protobuf::StreamEncoder*>(&snapshot_encoder));
return device_handler::CaptureThreads(cpu_state.extended.psp,
*thread_info_encoder);
}
Status CrashSnapshot::CaptureLogs(
snapshot::pwpb::Snapshot::StreamEncoder& snapshot_encoder) {
log::pwpb::LogEntries::StreamEncoder encoder(writer_, ByteSpan());
// Limit the captured logs to the latest entries that were
// added to log buffer.
size_t remaining_bytes = snapshot_encoder.ConservativeWriteLimit();
multisink::UnsafeDumpMultiSinkLogsFromEnd(
GetMultiSink(), encoder, remaining_bytes)
.IgnoreError();
return snapshot_encoder.status();
}
} // namespace pw::system