Merge "logd: drop logs about pruning and compression to VERBOSE"
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index c52c6f7..5280121 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -597,8 +597,8 @@
}
// TODO: Use seccomp to lock ourselves down.
- unwindstack::UnwinderFromPid unwinder(256, vm_pid);
- if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
+ unwindstack::UnwinderFromPid unwinder(256, vm_pid, unwindstack::Regs::CurrentArch());
+ if (!unwinder.Init()) {
LOG(FATAL) << "Failed to init unwinder object.";
}
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 9bcbdb3..abcb2c4 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -82,16 +82,12 @@
thread.pid = getpid();
thread.tid = gettid();
thread.thread_name = get_thread_name(gettid());
- unwindstack::ArchEnum arch = unwindstack::Regs::CurrentArch();
- thread.registers.reset(unwindstack::Regs::CreateFromUcontext(arch, ucontext));
+ thread.registers.reset(
+ unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
// TODO: Create this once and store it in a global?
unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid());
- if (unwinder.Init(arch)) {
- dump_backtrace_thread(output_fd, &unwinder, thread);
- } else {
- async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Unable to init unwinder.");
- }
+ dump_backtrace_thread(output_fd, &unwinder, thread);
}
__linker_disable_fallback_allocator();
}
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index f5a873c..c543a83 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -18,8 +18,9 @@
#include "libdebuggerd/backtrace.h"
-#include <errno.h>
#include <dirent.h>
+#include <errno.h>
+#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
@@ -65,7 +66,11 @@
unwinder->SetRegs(thread.registers.get());
unwinder->Unwind();
if (unwinder->NumFrames() == 0) {
- _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d", thread.tid);
+ _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d\n", thread.tid);
+ if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
+ _LOG(&log, logtype::THREAD, " Error code: %s\n", unwinder->LastErrorCodeString());
+ _LOG(&log, logtype::THREAD, " Error address: 0x%" PRIx64 "\n", unwinder->LastErrorAddress());
+ }
return;
}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index e1fe82b..d88c5a9 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -407,7 +407,11 @@
unwinder->SetRegs(regs_copy.get());
unwinder->Unwind();
if (unwinder->NumFrames() == 0) {
- _LOG(log, logtype::THREAD, "Failed to unwind");
+ _LOG(log, logtype::THREAD, "Failed to unwind\n");
+ if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
+ _LOG(log, logtype::THREAD, " Error code: %s\n", unwinder->LastErrorCodeString());
+ _LOG(log, logtype::THREAD, " Error address: 0x%" PRIx64 "\n", unwinder->LastErrorAddress());
+ }
} else {
_LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
log_backtrace(log, unwinder, " ");
@@ -578,8 +582,8 @@
.siginfo = siginfo,
};
- unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid);
- if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
+ unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch());
+ if (!unwinder.Init()) {
LOG(FATAL) << "Failed to init unwinder object.";
}
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 81ebf43..e6f9ffa 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -147,6 +147,7 @@
static_libs: [
"libgtest_prod",
"libhealthhalutils",
+ "libsnapshot_cow",
"libsnapshot_nobinder",
"update_metadata-protos",
],
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index aa41be3..046ea74 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -74,9 +74,11 @@
"android/snapshot/snapshot.proto",
"device_info.cpp",
"snapshot.cpp",
+ "snapshot_reader.cpp",
"snapshot_stats.cpp",
"snapshot_stub.cpp",
"snapshot_metadata_updater.cpp",
+ "snapshot_writer.cpp",
"partition_cow_creator.cpp",
"return.cpp",
"utility.cpp",
@@ -215,6 +217,7 @@
"libgmock",
"liblp",
"libsnapshot",
+ "libsnapshot_cow",
"libsnapshot_test_helpers",
"libsparse",
],
@@ -249,6 +252,7 @@
static_libs: [
"libfstab",
"libsnapshot",
+ "libsnapshot_cow",
"update_metadata-protos",
],
shared_libs: [
@@ -312,6 +316,7 @@
"libgmock", // from libsnapshot_test_helpers
"liblog",
"liblp",
+ "libsnapshot_cow",
"libsnapshot_test_helpers",
"libprotobuf-mutator",
],
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 4cf2119..f96f174 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -32,6 +32,48 @@
static_assert(sizeof(off_t) == sizeof(uint64_t));
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
+bool ICowWriter::AddCopy(uint64_t new_block, uint64_t old_block) {
+ if (!ValidateNewBlock(new_block)) {
+ return false;
+ }
+ return EmitCopy(new_block, old_block);
+}
+
+bool ICowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+ if (size % options_.block_size != 0) {
+ LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+ << options_.block_size;
+ return false;
+ }
+
+ uint64_t num_blocks = size / options_.block_size;
+ uint64_t last_block = new_block_start + num_blocks - 1;
+ if (!ValidateNewBlock(last_block)) {
+ return false;
+ }
+ return EmitRawBlocks(new_block_start, data, size);
+}
+
+bool ICowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+ uint64_t last_block = new_block_start + num_blocks - 1;
+ if (!ValidateNewBlock(last_block)) {
+ return false;
+ }
+ return EmitZeroBlocks(new_block_start, num_blocks);
+}
+
+bool ICowWriter::ValidateNewBlock(uint64_t new_block) {
+ if (options_.max_blocks && new_block >= options_.max_blocks.value()) {
+ LOG(ERROR) << "New block " << new_block << " exceeds maximum block count "
+ << options_.max_blocks.value();
+ return false;
+ }
+ return true;
+}
+
CowWriter::CowWriter(const CowOptions& options) : ICowWriter(options), fd_(-1) {
SetupHeaders();
}
@@ -59,12 +101,12 @@
return true;
}
-bool CowWriter::Initialize(android::base::unique_fd&& fd, OpenMode mode) {
+bool CowWriter::Initialize(unique_fd&& fd, OpenMode mode) {
owned_fd_ = std::move(fd);
- return Initialize(android::base::borrowed_fd{owned_fd_}, mode);
+ return Initialize(borrowed_fd{owned_fd_}, mode);
}
-bool CowWriter::Initialize(android::base::borrowed_fd fd, OpenMode mode) {
+bool CowWriter::Initialize(borrowed_fd fd, OpenMode mode) {
fd_ = fd;
if (!ParseOptions()) {
@@ -134,7 +176,7 @@
return true;
}
-bool CowWriter::AddCopy(uint64_t new_block, uint64_t old_block) {
+bool CowWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
CowOperation op = {};
op.type = kCowCopyOp;
op.new_block = new_block;
@@ -143,13 +185,7 @@
return true;
}
-bool CowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
- if (size % header_.block_size != 0) {
- LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
- << header_.block_size;
- return false;
- }
-
+bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
uint64_t pos;
if (!GetDataPos(&pos)) {
return false;
@@ -195,7 +231,7 @@
return true;
}
-bool CowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+bool CowWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
for (uint64_t i = 0; i < num_blocks; i++) {
CowOperation op = {};
op.type = kCowZeroOp;
@@ -291,7 +327,7 @@
return true;
}
-size_t CowWriter::GetCowSize() {
+uint64_t CowWriter::GetCowSize() {
return header_.ops_offset + header_.num_ops * sizeof(CowOperation);
}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index 8569161..2bc0171 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -16,6 +16,7 @@
#include <stdint.h>
+#include <optional>
#include <string>
#include <android-base/unique_fd.h>
@@ -27,6 +28,9 @@
struct CowOptions {
uint32_t block_size = 4096;
std::string compression;
+
+ // Maximum number of blocks that can be written.
+ std::optional<uint64_t> max_blocks;
};
// Interface for writing to a snapuserd COW. All operations are ordered; merges
@@ -39,20 +43,32 @@
// Encode an operation that copies the contents of |old_block| to the
// location of |new_block|.
- virtual bool AddCopy(uint64_t new_block, uint64_t old_block) = 0;
+ bool AddCopy(uint64_t new_block, uint64_t old_block);
// Encode a sequence of raw blocks. |size| must be a multiple of the block size.
- virtual bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
+ bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size);
// Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.
- virtual bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
+ bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks);
// Flush all pending writes. This must be called before closing the writer
// to ensure that the correct headers and footers are written.
virtual bool Flush() = 0;
// Return number of bytes the cow image occupies on disk.
- virtual size_t GetCowSize() = 0;
+ virtual uint64_t GetCowSize() = 0;
+
+ // Returns true if AddCopy() operations are supported.
+ virtual bool SupportsCopyOperation() const { return true; }
+
+ const CowOptions& options() { return options_; }
+
+ protected:
+ virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) = 0;
+ virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
+ virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
+
+ bool ValidateNewBlock(uint64_t new_block);
protected:
CowOptions options_;
@@ -68,13 +84,14 @@
bool Initialize(android::base::unique_fd&& fd, OpenMode mode = OpenMode::WRITE);
bool Initialize(android::base::borrowed_fd fd, OpenMode mode = OpenMode::WRITE);
- bool AddCopy(uint64_t new_block, uint64_t old_block) override;
- bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
- bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
-
bool Flush() override;
- size_t GetCowSize() override;
+ uint64_t GetCowSize() override;
+
+ protected:
+ virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
+ virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
private:
void SetupHeaders();
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index eb6ad05..13f19aa 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -38,9 +38,7 @@
(const android::fs_mgr::CreateLogicalPartitionParams& params,
std::string* snapshot_path),
(override));
- MOCK_METHOD(std::unique_ptr<ICowWriter>, OpenSnapshotWriter,
- (const android::fs_mgr::CreateLogicalPartitionParams& params), (override));
- MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenSnapshotReader,
+ MOCK_METHOD(std::unique_ptr<ISnapshotWriter>, OpenSnapshotWriter,
(const android::fs_mgr::CreateLogicalPartitionParams& params), (override));
MOCK_METHOD(bool, UnmapUpdateSnapshot, (const std::string& target_partition_name), (override));
MOCK_METHOD(bool, NeedSnapshotsInFirstStageMount, (), (override));
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 6fef58a..1bc972e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -35,8 +35,8 @@
#include <update_engine/update_metadata.pb.h>
#include <libsnapshot/auto_device.h>
-#include <libsnapshot/cow_writer.h>
#include <libsnapshot/return.h>
+#include <libsnapshot/snapshot_writer.h>
#ifndef FRIEND_TEST
#define FRIEND_TEST(test_set_name, individual_test) \
@@ -44,10 +44,6 @@
#define DEFINED_FRIEND_TEST
#endif
-namespace chromeos_update_engine {
-class FileDescriptor;
-} // namespace chromeos_update_engine
-
namespace android {
namespace fiemap {
@@ -110,8 +106,6 @@
};
virtual ~ISnapshotManager() = default;
- using FileDescriptor = chromeos_update_engine::FileDescriptor;
-
// Begin an update. This must be called before creating any snapshots. It
// will fail if GetUpdateState() != None.
virtual bool BeginUpdate() = 0;
@@ -187,19 +181,14 @@
virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
std::string* snapshot_path) = 0;
- // Create an ICowWriter to build a snapshot against a target partition. The partition name must
- // be suffixed.
- virtual std::unique_ptr<ICowWriter> OpenSnapshotWriter(
- const android::fs_mgr::CreateLogicalPartitionParams& params) = 0;
-
- // Open a snapshot for reading. A file-like interface is provided through the FileDescriptor.
- // In this mode, writes are not supported. The partition name must be suffixed.
- virtual std::unique_ptr<FileDescriptor> OpenSnapshotReader(
+ // Create an ISnapshotWriter to build a snapshot against a target partition. The partition name
+ // must be suffixed.
+ virtual std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
const android::fs_mgr::CreateLogicalPartitionParams& params) = 0;
// Unmap a snapshot device or CowWriter that was previously opened with MapUpdateSnapshot,
- // OpenSnapshotWriter, or OpenSnapshotReader. All outstanding open descriptors, writers,
- // or readers must be deleted before this is called.
+ // OpenSnapshotWriter. All outstanding open descriptors, writers, or
+ // readers must be deleted before this is called.
virtual bool UnmapUpdateSnapshot(const std::string& target_partition_name) = 0;
// If this returns true, first-stage mount must call
@@ -310,9 +299,7 @@
Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) override;
bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
std::string* snapshot_path) override;
- std::unique_ptr<ICowWriter> OpenSnapshotWriter(
- const android::fs_mgr::CreateLogicalPartitionParams& params) override;
- std::unique_ptr<FileDescriptor> OpenSnapshotReader(
+ std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
const android::fs_mgr::CreateLogicalPartitionParams& params) override;
bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
bool NeedSnapshotsInFirstStageMount() override;
@@ -532,9 +519,39 @@
std::string GetSnapshotDeviceName(const std::string& snapshot_name,
const SnapshotStatus& status);
+ // Reason for calling MapPartitionWithSnapshot.
+ enum class SnapshotContext {
+ // For writing or verification (during update_engine).
+ Update,
+
+ // For mounting a full readable device.
+ Mount,
+ };
+
+ struct SnapshotPaths {
+ // Target/base device (eg system_b), always present.
+ std::string target_device;
+
+ // COW path (eg system_cow). Not present if no COW is needed.
+ std::string cow_device;
+
+ // dm-snapshot instance. Not present in Update mode for VABC.
+ std::string snapshot_device;
+ };
+
+ // Helpers for OpenSnapshotWriter.
+ std::unique_ptr<ISnapshotWriter> OpenCompressedSnapshotWriter(LockedFile* lock,
+ const std::string& partition_name,
+ const SnapshotStatus& status,
+ const SnapshotPaths& paths);
+ std::unique_ptr<ISnapshotWriter> OpenKernelSnapshotWriter(LockedFile* lock,
+ const std::string& partition_name,
+ const SnapshotStatus& status,
+ const SnapshotPaths& paths);
+
// Map the base device, COW devices, and snapshot device.
bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params,
- std::string* path);
+ SnapshotContext context, SnapshotPaths* paths);
// Map the COW devices, including the partition in super and the images.
// |params|:
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index 149f463..cda2bee 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -36,9 +36,7 @@
const chromeos_update_engine::DeltaArchiveManifest& manifest) override;
bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
std::string* snapshot_path) override;
- std::unique_ptr<ICowWriter> OpenSnapshotWriter(
- const android::fs_mgr::CreateLogicalPartitionParams& params) override;
- std::unique_ptr<FileDescriptor> OpenSnapshotReader(
+ std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
const android::fs_mgr::CreateLogicalPartitionParams& params) override;
bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
bool NeedSnapshotsInFirstStageMount() override;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
new file mode 100644
index 0000000..bf57a00
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+
+#include <libsnapshot/cow_writer.h>
+
+namespace chromeos_update_engine {
+class FileDescriptor;
+} // namespace chromeos_update_engine
+
+namespace android {
+namespace snapshot {
+
+class ISnapshotWriter : public ICowWriter {
+ public:
+ using FileDescriptor = chromeos_update_engine::FileDescriptor;
+
+ explicit ISnapshotWriter(const CowOptions& options);
+
+ // Set the source device. This is used for AddCopy() operations, if the
+ // underlying writer needs the original bytes (for example if backed by
+ // dm-snapshot or if writing directly to an unsnapshotted region).
+ void SetSourceDevice(android::base::unique_fd&& source_fd);
+
+ virtual std::unique_ptr<FileDescriptor> OpenReader() = 0;
+
+ protected:
+ android::base::unique_fd source_fd_;
+};
+
+// Write directly to a dm-snapshot device.
+class OnlineKernelSnapshotWriter : public ISnapshotWriter {
+ public:
+ OnlineKernelSnapshotWriter(const CowOptions& options);
+
+ // Set the device used for all writes.
+ void SetSnapshotDevice(android::base::unique_fd&& snapshot_fd, uint64_t cow_size);
+
+ bool Flush() override;
+ uint64_t GetCowSize() override { return cow_size_; }
+ virtual std::unique_ptr<FileDescriptor> OpenReader() override;
+
+ protected:
+ bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+ bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
+
+ private:
+ android::base::unique_fd snapshot_fd_;
+ uint64_t cow_size_ = 0;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 8e369b0..197aeaa 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -144,6 +144,7 @@
// Expect space of |path| is multiple of 4K.
bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt,
std::string* hash = nullptr);
+bool WriteRandomData(ICowWriter* writer, std::string* hash = nullptr);
std::optional<std::string> GetHash(const std::string& path);
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 0904fc7..b672d0e 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -15,6 +15,7 @@
#include <libsnapshot/snapshot.h>
#include <dirent.h>
+#include <fcntl.h>
#include <math.h>
#include <sys/file.h>
#include <sys/types.h>
@@ -43,6 +44,7 @@
#include "device_info.h"
#include "partition_cow_creator.h"
#include "snapshot_metadata_updater.h"
+#include "snapshot_reader.h"
#include "utility.h"
namespace android {
@@ -1570,7 +1572,8 @@
.timeout_ms = timeout_ms,
};
std::string ignore_path;
- if (!MapPartitionWithSnapshot(lock.get(), std::move(params), &ignore_path)) {
+ if (!MapPartitionWithSnapshot(lock.get(), std::move(params), SnapshotContext::Mount,
+ nullptr)) {
return false;
}
}
@@ -1598,11 +1601,10 @@
bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
CreateLogicalPartitionParams params,
- std::string* path) {
+ SnapshotContext context, SnapshotPaths* paths) {
auto begin = std::chrono::steady_clock::now();
CHECK(lock);
- path->clear();
if (params.GetPartitionName() != params.GetDeviceName()) {
LOG(ERROR) << "Mapping snapshot with a different name is unsupported: partition_name = "
@@ -1683,8 +1685,11 @@
}
created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName());
+ if (paths) {
+ paths->target_device = base_path;
+ }
+
if (!live_snapshot_status.has_value()) {
- *path = base_path;
created_devices.Release();
return true;
}
@@ -1711,21 +1716,33 @@
LOG(ERROR) << "Could not determine major/minor for: " << cow_name;
return false;
}
+ if (paths) {
+ paths->cow_device = cow_device;
+ }
remaining_time = GetRemainingTime(params.timeout_ms, begin);
if (remaining_time.count() < 0) return false;
+ if (context == SnapshotContext::Update && IsCompressionEnabled()) {
+ // Stop here, we can't run dm-user yet, the COW isn't built.
+ return true;
+ }
+
+ std::string path;
if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
- path)) {
+ &path)) {
LOG(ERROR) << "Could not map snapshot for partition: " << params.GetPartitionName();
return false;
}
// No need to add params.GetPartitionName() to created_devices since it is immediately released.
+ if (paths) {
+ paths->snapshot_device = path;
+ }
+
created_devices.Release();
- LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << *path;
-
+ LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << path;
return true;
}
@@ -2438,23 +2455,86 @@
<< params.GetPartitionName();
return false;
}
- return MapPartitionWithSnapshot(lock.get(), params, snapshot_path);
+
+ SnapshotPaths paths;
+ if (!MapPartitionWithSnapshot(lock.get(), params, SnapshotContext::Update, &paths)) {
+ return false;
+ }
+
+ if (paths.snapshot_device.empty()) {
+ *snapshot_path = paths.snapshot_device;
+ } else {
+ *snapshot_path = paths.target_device;
+ }
+ return true;
}
-std::unique_ptr<ICowWriter> SnapshotManager::OpenSnapshotWriter(
+std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenSnapshotWriter(
const android::fs_mgr::CreateLogicalPartitionParams& params) {
- (void)params;
+ // First unmap any existing mapping.
+ auto lock = LockShared();
+ if (!lock) return nullptr;
+ if (!UnmapPartitionWithSnapshot(lock.get(), params.GetPartitionName())) {
+ LOG(ERROR) << "Cannot unmap existing snapshot before re-mapping it: "
+ << params.GetPartitionName();
+ return nullptr;
+ }
- LOG(ERROR) << "OpenSnapshotWriter not yet implemented";
+ SnapshotPaths paths;
+ if (!MapPartitionWithSnapshot(lock.get(), params, SnapshotContext::Update, &paths)) {
+ return nullptr;
+ }
+
+ SnapshotStatus status;
+ if (!paths.cow_device.empty()) {
+ if (!ReadSnapshotStatus(lock.get(), params.GetPartitionName(), &status)) {
+ return nullptr;
+ }
+ } else {
+ // Currently, partition_cow_creator always creates snapshots. The
+ // reason is that if partition X shrinks while partition Y grows, we
+ // cannot bindly write to the newly freed extents in X. This would
+ // make the old slot unusable. So, the entire size of the target
+ // partition is currently considered snapshottable.
+ LOG(ERROR) << "No snapshot available for partition " << params.GetPartitionName();
+ return nullptr;
+ }
+
+ if (IsCompressionEnabled()) {
+ return OpenCompressedSnapshotWriter(lock.get(), params.GetPartitionName(), status, paths);
+ } else {
+ return OpenKernelSnapshotWriter(lock.get(), params.GetPartitionName(), status, paths);
+ }
+}
+
+std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenCompressedSnapshotWriter(
+ LockedFile*, const std::string&, const SnapshotStatus&, const SnapshotPaths&) {
+ LOG(ERROR) << "OpenSnapshotWriter not yet implemented for compression";
return nullptr;
}
-std::unique_ptr<FileDescriptor> SnapshotManager::OpenSnapshotReader(
- const android::fs_mgr::CreateLogicalPartitionParams& params) {
- (void)params;
+std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenKernelSnapshotWriter(
+ LockedFile* lock, [[maybe_unused]] const std::string& partition_name,
+ const SnapshotStatus& status, const SnapshotPaths& paths) {
+ CHECK(lock);
- LOG(ERROR) << "OpenSnapshotReader not yet implemented";
- return nullptr;
+ CowOptions cow_options;
+ cow_options.max_blocks = {status.device_size() / cow_options.block_size};
+
+ auto writer = std::make_unique<OnlineKernelSnapshotWriter>(cow_options);
+
+ std::string_view path =
+ paths.snapshot_device.empty() ? paths.target_device : paths.snapshot_device;
+ unique_fd fd(open(path.data(), O_RDWR | O_CLOEXEC));
+ if (fd < 0) {
+ PLOG(ERROR) << "open failed: " << path;
+ return nullptr;
+ }
+
+ uint64_t cow_size = status.cow_partition_size() + status.cow_file_size();
+ writer->SetSnapshotDevice(std::move(fd), cow_size);
+
+ return writer;
}
bool SnapshotManager::UnmapUpdateSnapshot(const std::string& target_partition_name) {
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/snapshot_reader.cpp
new file mode 100644
index 0000000..0d47468
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_reader.cpp
@@ -0,0 +1,77 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// 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
+//
+// http://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 <ext4_utils/ext4_utils.h>
+
+#include "snapshot_reader.h"
+
+namespace android {
+namespace snapshot {
+
+// Not supported.
+bool ReadOnlyFileDescriptor::Open(const char*, int, mode_t) {
+ errno = EINVAL;
+ return false;
+}
+
+bool ReadOnlyFileDescriptor::Open(const char*, int) {
+ errno = EINVAL;
+ return false;
+}
+
+ssize_t ReadOnlyFileDescriptor::Write(const void*, size_t) {
+ errno = EINVAL;
+ return false;
+}
+
+bool ReadOnlyFileDescriptor::BlkIoctl(int, uint64_t, uint64_t, int*) {
+ errno = EINVAL;
+ return false;
+}
+
+ReadFdFileDescriptor::ReadFdFileDescriptor(android::base::unique_fd&& fd) : fd_(std::move(fd)) {}
+
+ssize_t ReadFdFileDescriptor::Read(void* buf, size_t count) {
+ return read(fd_.get(), buf, count);
+}
+
+off64_t ReadFdFileDescriptor::Seek(off64_t offset, int whence) {
+ return lseek(fd_.get(), offset, whence);
+}
+
+uint64_t ReadFdFileDescriptor::BlockDevSize() {
+ return get_block_device_size(fd_.get());
+}
+
+bool ReadFdFileDescriptor::Close() {
+ fd_ = {};
+ return true;
+}
+
+bool ReadFdFileDescriptor::IsSettingErrno() {
+ return true;
+}
+
+bool ReadFdFileDescriptor::IsOpen() {
+ return fd_ >= 0;
+}
+
+bool ReadFdFileDescriptor::Flush() {
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_reader.h b/fs_mgr/libsnapshot/snapshot_reader.h
new file mode 100644
index 0000000..1f2ffe2
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_reader.h
@@ -0,0 +1,50 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// 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
+//
+// http://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.
+//
+
+#pragma once
+
+#include <android-base/file.h>
+#include <payload_consumer/file_descriptor.h>
+
+namespace android {
+namespace snapshot {
+
+class ReadOnlyFileDescriptor : public chromeos_update_engine::FileDescriptor {
+ public:
+ bool Open(const char* path, int flags, mode_t mode) override;
+ bool Open(const char* path, int flags) override;
+ ssize_t Write(const void* buf, size_t count) override;
+ bool BlkIoctl(int request, uint64_t start, uint64_t length, int* result) override;
+};
+
+class ReadFdFileDescriptor : public ReadOnlyFileDescriptor {
+ public:
+ explicit ReadFdFileDescriptor(android::base::unique_fd&& fd);
+
+ ssize_t Read(void* buf, size_t count) override;
+ off64_t Seek(off64_t offset, int whence) override;
+ uint64_t BlockDevSize() override;
+ bool Close() override;
+ bool IsSettingErrno() override;
+ bool IsOpen() override;
+ bool Flush() override;
+
+ private:
+ android::base::unique_fd fd_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 8ae6305..41f5da4 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -130,13 +130,7 @@
return &snapshot_merge_stats;
}
-std::unique_ptr<ICowWriter> SnapshotManagerStub::OpenSnapshotWriter(
- const CreateLogicalPartitionParams&) {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return nullptr;
-}
-
-std::unique_ptr<FileDescriptor> SnapshotManagerStub::OpenSnapshotReader(
+std::unique_ptr<ISnapshotWriter> SnapshotManagerStub::OpenSnapshotWriter(
const CreateLogicalPartitionParams&) {
LOG(ERROR) << __FUNCTION__ << " should never be called.";
return nullptr;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 6ff935b..f2caaa4 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -80,6 +80,7 @@
std::string fake_super;
void MountMetadata();
+bool IsCompressionEnabled();
class SnapshotTest : public ::testing::Test {
public:
@@ -892,42 +893,39 @@
return AssertionSuccess();
}
- AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path = nullptr) {
- std::string real_path;
- if (!sm->MapUpdateSnapshot(
- CreateLogicalPartitionParams{
- .block_device = fake_super,
- .metadata_slot = 1,
- .partition_name = name,
- .timeout_ms = 10s,
- .partition_opener = opener_.get(),
- },
- &real_path)) {
- return AssertionFailure() << "Unable to map snapshot " << name;
+ AssertionResult MapUpdateSnapshot(const std::string& name,
+ std::unique_ptr<ICowWriter>* writer = nullptr) {
+ CreateLogicalPartitionParams params{
+ .block_device = fake_super,
+ .metadata_slot = 1,
+ .partition_name = name,
+ .timeout_ms = 10s,
+ .partition_opener = opener_.get(),
+ };
+
+ auto result = sm->OpenSnapshotWriter(params);
+ if (!result) {
+ return AssertionFailure() << "Cannot open snapshot for writing: " << name;
}
- if (path) {
- *path = real_path;
+
+ if (writer) {
+ *writer = std::move(result);
}
- return AssertionSuccess() << "Mapped snapshot " << name << " to " << real_path;
+ return AssertionSuccess();
}
- AssertionResult WriteSnapshotAndHash(const std::string& name,
- std::optional<size_t> size = std::nullopt) {
- std::string path;
- auto res = MapUpdateSnapshot(name, &path);
+ AssertionResult WriteSnapshotAndHash(const std::string& name) {
+ std::unique_ptr<ICowWriter> writer;
+ auto res = MapUpdateSnapshot(name, &writer);
if (!res) {
return res;
}
- std::string size_string = size ? (std::to_string(*size) + " bytes") : "";
-
- if (!WriteRandomData(path, size, &hashes_[name])) {
- return AssertionFailure() << "Unable to write " << size_string << " to " << path
- << " for partition " << name;
+ if (!WriteRandomData(writer.get(), &hashes_[name])) {
+ return AssertionFailure() << "Unable to write random data to snapshot " << name;
}
- return AssertionSuccess() << "Written " << size_string << " to " << path
- << " for snapshot partition " << name
+ return AssertionSuccess() << "Written random data to snapshot " << name
<< ", hash: " << hashes_[name];
}
@@ -1003,7 +1001,7 @@
// Write some data to target partitions.
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name, partition_size));
+ ASSERT_TRUE(WriteSnapshotAndHash(name));
}
// Assert that source partitions aren't affected.
@@ -1406,6 +1404,10 @@
MetadataMountedTest().TearDown();
}
+bool IsCompressionEnabled() {
+ return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
+}
+
TEST_F(MetadataMountedTest, Android) {
auto device = sm->EnsureMetadataMounted();
EXPECT_NE(nullptr, device);
@@ -1623,7 +1625,7 @@
// Map and write some data to target partition.
ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
- ASSERT_TRUE(WriteSnapshotAndHash("sys_b", partition_size));
+ ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
// Finish update.
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
@@ -1655,7 +1657,7 @@
// Map and write some data to target partitions.
ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
- ASSERT_TRUE(WriteSnapshotAndHash("sys_b", actual_write_size));
+ ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
std::vector<android::dm::DeviceMapper::TargetInfo> table;
ASSERT_TRUE(DeviceMapper::Instance().GetTableStatus("sys_b", &table));
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
new file mode 100644
index 0000000..584f15e
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -0,0 +1,97 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// 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
+//
+// http://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 <libsnapshot/snapshot_writer.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <payload_consumer/file_descriptor.h>
+#include "snapshot_reader.h"
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+using chromeos_update_engine::FileDescriptor;
+
+ISnapshotWriter::ISnapshotWriter(const CowOptions& options) : ICowWriter(options) {}
+
+void ISnapshotWriter::SetSourceDevice(android::base::unique_fd&& source_fd) {
+ source_fd_ = std::move(source_fd);
+}
+
+OnlineKernelSnapshotWriter::OnlineKernelSnapshotWriter(const CowOptions& options)
+ : ISnapshotWriter(options) {}
+
+void OnlineKernelSnapshotWriter::SetSnapshotDevice(android::base::unique_fd&& snapshot_fd,
+ uint64_t cow_size) {
+ snapshot_fd_ = std::move(snapshot_fd);
+ cow_size_ = cow_size;
+}
+
+bool OnlineKernelSnapshotWriter::Flush() {
+ if (fsync(snapshot_fd_.get()) < 0) {
+ PLOG(ERROR) << "fsync";
+ return false;
+ }
+ return true;
+}
+
+bool OnlineKernelSnapshotWriter::EmitRawBlocks(uint64_t new_block_start, const void* data,
+ size_t size) {
+ uint64_t offset = new_block_start * options_.block_size;
+ if (lseek(snapshot_fd_.get(), offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "EmitRawBlocks lseek to offset " << offset;
+ return false;
+ }
+ if (!android::base::WriteFully(snapshot_fd_, data, size)) {
+ PLOG(ERROR) << "EmitRawBlocks write";
+ return false;
+ }
+ return true;
+}
+
+bool OnlineKernelSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+ std::string zeroes(options_.block_size, 0);
+ for (uint64_t i = 0; i < num_blocks; i++) {
+ if (!EmitRawBlocks(new_block_start + i, zeroes.data(), zeroes.size())) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool OnlineKernelSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
+ std::string buffer(options_.block_size, 0);
+ uint64_t offset = old_block * options_.block_size;
+ if (!android::base::ReadFullyAtOffset(source_fd_, buffer.data(), buffer.size(), offset)) {
+ PLOG(ERROR) << "EmitCopy read";
+ return false;
+ }
+ return EmitRawBlocks(new_block, buffer.data(), buffer.size());
+}
+
+std::unique_ptr<FileDescriptor> OnlineKernelSnapshotWriter::OpenReader() {
+ unique_fd fd(dup(snapshot_fd_.get()));
+ if (fd < 0) {
+ PLOG(ERROR) << "dup2 failed in OpenReader";
+ return nullptr;
+ }
+ return std::make_unique<ReadFdFileDescriptor>(std::move(fd));
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index b07bf91..6104c82 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -127,6 +127,48 @@
return true;
}
+bool WriteRandomData(ICowWriter* writer, std::string* hash) {
+ unique_fd rand(open("/dev/urandom", O_RDONLY));
+ if (rand < 0) {
+ PLOG(ERROR) << "open /dev/urandom";
+ return false;
+ }
+
+ SHA256_CTX ctx;
+ if (hash) {
+ SHA256_Init(&ctx);
+ }
+
+ if (!writer->options().max_blocks) {
+ LOG(ERROR) << "CowWriter must specify maximum number of blocks";
+ return false;
+ }
+ uint64_t num_blocks = writer->options().max_blocks.value();
+
+ size_t block_size = writer->options().block_size;
+ std::string block(block_size, '\0');
+ for (uint64_t i = 0; i < num_blocks; i++) {
+ if (!ReadFully(rand, block.data(), block.size())) {
+ PLOG(ERROR) << "read /dev/urandom";
+ return false;
+ }
+ if (!writer->AddRawBlocks(i, block.data(), block.size())) {
+ LOG(ERROR) << "Failed to add raw block " << i;
+ return false;
+ }
+ if (hash) {
+ SHA256_Update(&ctx, block.data(), block.size());
+ }
+ }
+
+ if (hash) {
+ uint8_t out[32];
+ SHA256_Final(out, &ctx);
+ *hash = ToHexString(out, sizeof(out));
+ }
+ return true;
+}
+
std::optional<std::string> GetHash(const std::string& path) {
std::string content;
if (!android::base::ReadFileToString(path, &content, true)) {
diff --git a/init/Android.bp b/init/Android.bp
index 3f2cd07..c3dd7f6 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -129,6 +129,7 @@
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
+ "libsnapshot_cow",
"libsnapshot_init",
"libxml2",
"lib_apex_manifest_proto_lite",
diff --git a/init/Android.mk b/init/Android.mk
index da94daf..998e0fd 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -112,6 +112,7 @@
libmodprobe \
libext2_uuid \
libprotobuf-cpp-lite \
+ libsnapshot_cow \
libsnapshot_init \
update_metadata-protos \
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index 038b59e..a506575 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -37,6 +37,12 @@
#include "ThreadEntry.h"
bool BacktraceCurrent::ReadWord(uint64_t ptr, word_t* out_value) {
+#if defined(__aarch64__)
+ // Tagged pointer after Android R would lead top byte to have random values
+ // https://source.android.com/devices/tech/debug/tagged-pointers
+ ptr &= (1ULL << 56) - 1;
+#endif
+
if (!VerifyReadWordArgs(ptr, out_value)) {
return false;
}
@@ -54,6 +60,12 @@
}
size_t BacktraceCurrent::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
+#if defined(__aarch64__)
+ // Tagged pointer after Android R would lead top byte to have random values
+ // https://source.android.com/devices/tech/debug/tagged-pointers
+ addr &= (1ULL << 56) - 1;
+#endif
+
backtrace_map_t map;
FillInMap(addr, &map);
if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 624711f..82ff21c 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -52,11 +52,11 @@
unwinder.SetResolveNames(stack_map->ResolveNames());
stack_map->SetArch(regs->Arch());
if (stack_map->GetJitDebug() != nullptr) {
- unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
+ unwinder.SetJitDebug(stack_map->GetJitDebug());
}
#if !defined(NO_LIBDEXFILE_SUPPORT)
if (stack_map->GetDexFiles() != nullptr) {
- unwinder.SetDexFiles(stack_map->GetDexFiles(), regs->Arch());
+ unwinder.SetDexFiles(stack_map->GetDexFiles());
}
#endif
unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore());
@@ -180,5 +180,10 @@
}
size_t UnwindStackPtrace::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
+#if defined(__aarch64__)
+ // Tagged pointer after Android R would lead top byte to have random values
+ // https://source.android.com/devices/tech/debug/tagged-pointers
+ addr &= (1ULL << 56) - 1;
+#endif
return memory_->Read(addr, buffer, bytes);
}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 8cc780a..75a419c 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -131,7 +131,6 @@
support_system_process: true,
},
defaults: ["libunwindstack_defaults"],
-
srcs: ["DexFile.cpp"],
cflags: ["-DDEXFILE_SUPPORT"],
shared_libs: ["libdexfile_support"],
@@ -168,6 +167,7 @@
defaults: ["libunwindstack_defaults"],
visibility: [
+ "//external/gwp_asan",
"//system/core/debuggerd",
"//system/core/init",
"//system/core/libbacktrace",
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 57806c1..bcdbde8 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -27,6 +27,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <unwindstack/DexFiles.h>
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/MapInfo.h>
@@ -34,7 +35,7 @@
#include <unwindstack/Memory.h>
#include <unwindstack/Unwinder.h>
-#include <unwindstack/DexFiles.h>
+#include "Check.h"
// Use the demangler from libc++.
extern "C" char* __cxa_demangle(const char*, char*, size_t*, int* status);
@@ -142,13 +143,11 @@
void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
const std::vector<std::string>* map_suffixes_to_ignore) {
- frames_.clear();
- warnings_ = WARNING_NONE;
- last_error_.code = ERROR_NONE;
- last_error_.address = 0;
- elf_from_memory_not_file_ = false;
+ CHECK(arch_ != ARCH_UNKNOWN);
+ ClearErrors();
- ArchEnum arch = regs_->Arch();
+ frames_.clear();
+ elf_from_memory_not_file_ = false;
bool return_address_attempt = false;
bool adjust_pc = false;
@@ -169,7 +168,7 @@
if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
break;
}
- elf = map_info->GetElf(process_memory_, arch);
+ elf = map_info->GetElf(process_memory_, arch_);
// If this elf is memory backed, and there is a valid file, then set
// an indicator that we couldn't open the file.
if (!elf_from_memory_not_file_ && map_info->memory_backed_elf && !map_info->name.empty() &&
@@ -183,7 +182,7 @@
step_pc = rel_pc;
}
if (adjust_pc) {
- pc_adjustment = GetPcAdjustment(rel_pc, elf, arch);
+ pc_adjustment = GetPcAdjustment(rel_pc, elf, arch_);
} else {
pc_adjustment = 0;
}
@@ -311,7 +310,7 @@
std::string Unwinder::FormatFrame(const FrameData& frame) const {
std::string data;
- if (regs_->Is32Bit()) {
+ if (ArchIs32Bit(arch_)) {
data += android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
} else {
data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
@@ -362,23 +361,33 @@
return FormatFrame(frames_[frame_num]);
}
-void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) {
- jit_debug->SetArch(arch);
+void Unwinder::SetJitDebug(JitDebug* jit_debug) {
+ CHECK(arch_ != ARCH_UNKNOWN);
+ jit_debug->SetArch(arch_);
jit_debug_ = jit_debug;
}
-void Unwinder::SetDexFiles(DexFiles* dex_files, ArchEnum arch) {
- dex_files->SetArch(arch);
+void Unwinder::SetDexFiles(DexFiles* dex_files) {
+ CHECK(arch_ != ARCH_UNKNOWN);
+ dex_files->SetArch(arch_);
dex_files_ = dex_files;
}
-bool UnwinderFromPid::Init(ArchEnum arch) {
+bool UnwinderFromPid::Init() {
+ CHECK(arch_ != ARCH_UNKNOWN);
+ if (initted_) {
+ return true;
+ }
+ initted_ = true;
+
if (pid_ == getpid()) {
maps_ptr_.reset(new LocalMaps());
} else {
maps_ptr_.reset(new RemoteMaps(pid_));
}
if (!maps_ptr_->Parse()) {
+ ClearErrors();
+ last_error_.code = ERROR_INVALID_MAP;
return false;
}
maps_ = maps_ptr_.get();
@@ -387,16 +396,24 @@
jit_debug_ptr_.reset(new JitDebug(process_memory_));
jit_debug_ = jit_debug_ptr_.get();
- SetJitDebug(jit_debug_, arch);
+ SetJitDebug(jit_debug_);
#if defined(DEXFILE_SUPPORT)
dex_files_ptr_.reset(new DexFiles(process_memory_));
dex_files_ = dex_files_ptr_.get();
- SetDexFiles(dex_files_, arch);
+ SetDexFiles(dex_files_);
#endif
return true;
}
+void UnwinderFromPid::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
+ const std::vector<std::string>* map_suffixes_to_ignore) {
+ if (!Init()) {
+ return;
+ }
+ Unwinder::Unwind(initial_map_names_to_skip, map_suffixes_to_ignore);
+}
+
FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc, ArchEnum arch, Maps* maps,
JitDebug* jit_debug,
std::shared_ptr<Memory> process_memory,
@@ -449,8 +466,7 @@
}
FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc) {
- return BuildFrameFromPcOnly(pc, regs_ ? regs_->Arch() : ARCH_UNKNOWN, maps_, jit_debug_,
- process_memory_, resolve_names_);
+ return BuildFrameFromPcOnly(pc, arch_, maps_, jit_debug_, process_memory_, resolve_names_);
}
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Arch.h b/libunwindstack/include/unwindstack/Arch.h
new file mode 100644
index 0000000..7060004
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Arch.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * 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
+ *
+ * http://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.
+ */
+
+#ifndef _LIBUNWINDSTACK_ARCH_H
+#define _LIBUNWINDSTACK_ARCH_H
+
+#include <stddef.h>
+
+namespace unwindstack {
+
+enum ArchEnum : uint8_t {
+ ARCH_UNKNOWN = 0,
+ ARCH_ARM,
+ ARCH_ARM64,
+ ARCH_X86,
+ ARCH_X86_64,
+ ARCH_MIPS,
+ ARCH_MIPS64,
+};
+
+static inline bool ArchIs32Bit(ArchEnum arch) {
+ switch (arch) {
+ case ARCH_ARM:
+ case ARCH_X86:
+ case ARCH_MIPS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_ARCH_H
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 472ed92..97614b1 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -25,6 +25,7 @@
#include <unordered_map>
#include <utility>
+#include <unwindstack/Arch.h>
#include <unwindstack/ElfInterface.h>
#include <unwindstack/Memory.h>
@@ -38,16 +39,6 @@
struct MapInfo;
class Regs;
-enum ArchEnum : uint8_t {
- ARCH_UNKNOWN = 0,
- ARCH_ARM,
- ARCH_ARM64,
- ARCH_X86,
- ARCH_X86_64,
- ARCH_MIPS,
- ARCH_MIPS64,
-};
-
class Elf {
public:
Elf(Memory* memory) : memory_(memory) {}
diff --git a/libunwindstack/include/unwindstack/Error.h b/libunwindstack/include/unwindstack/Error.h
index 66fefe7..0be4572 100644
--- a/libunwindstack/include/unwindstack/Error.h
+++ b/libunwindstack/include/unwindstack/Error.h
@@ -39,6 +39,27 @@
ERROR_INVALID_ELF, // Unwind in an invalid elf.
};
+static inline const char* GetErrorCodeString(ErrorCode error) {
+ switch (error) {
+ case ERROR_NONE:
+ return "None";
+ case ERROR_MEMORY_INVALID:
+ return "Memory Invalid";
+ case ERROR_UNWIND_INFO:
+ return "Unwind Info";
+ case ERROR_UNSUPPORTED:
+ return "Unsupported";
+ case ERROR_INVALID_MAP:
+ return "Invalid Map";
+ case ERROR_MAX_FRAMES_EXCEEDED:
+ return "Maximum Frames Exceeded";
+ case ERROR_REPEATED_FRAME:
+ return "Repeated Frame";
+ case ERROR_INVALID_ELF:
+ return "Invalid Elf";
+ }
+}
+
struct ErrorData {
ErrorCode code;
uint64_t address; // Only valid when code is ERROR_MEMORY_INVALID.
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 5f42565..1a2a704 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -24,11 +24,12 @@
#include <string>
#include <vector>
+#include <unwindstack/Arch.h>
+
namespace unwindstack {
// Forward declarations.
class Elf;
-enum ArchEnum : uint8_t;
class Memory;
class Regs {
@@ -52,7 +53,7 @@
virtual ArchEnum Arch() = 0;
- virtual bool Is32Bit() = 0;
+ bool Is32Bit() { return ArchIs32Bit(Arch()); }
virtual void* RawData() = 0;
virtual uint64_t pc() = 0;
@@ -96,8 +97,6 @@
: Regs(total_regs, return_loc), regs_(total_regs) {}
virtual ~RegsImpl() = default;
- bool Is32Bit() override { return sizeof(AddressType) == sizeof(uint32_t); }
-
inline AddressType& operator[](size_t reg) { return regs_[reg]; }
void* RawData() override { return regs_.data(); }
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 3df8aad..b274c4c 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -24,6 +24,7 @@
#include <string>
#include <vector>
+#include <unwindstack/Arch.h>
#include <unwindstack/DexFiles.h>
#include <unwindstack/Error.h>
#include <unwindstack/JitDebug.h>
@@ -35,7 +36,6 @@
// Forward declarations.
class Elf;
-enum ArchEnum : uint8_t;
struct FrameData {
size_t num;
@@ -64,7 +64,11 @@
class Unwinder {
public:
Unwinder(size_t max_frames, Maps* maps, Regs* regs, std::shared_ptr<Memory> process_memory)
- : max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) {
+ : max_frames_(max_frames),
+ maps_(maps),
+ regs_(regs),
+ process_memory_(process_memory),
+ arch_(regs->Arch()) {
frames_.reserve(max_frames);
}
Unwinder(size_t max_frames, Maps* maps, std::shared_ptr<Memory> process_memory)
@@ -74,8 +78,8 @@
virtual ~Unwinder() = default;
- void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
- const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
+ virtual void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
+ const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
size_t NumFrames() const { return frames_.size(); }
@@ -90,9 +94,14 @@
std::string FormatFrame(size_t frame_num) const;
std::string FormatFrame(const FrameData& frame) const;
- void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
+ void SetArch(ArchEnum arch) { arch_ = arch; };
- void SetRegs(Regs* regs) { regs_ = regs; }
+ void SetJitDebug(JitDebug* jit_debug);
+
+ void SetRegs(Regs* regs) {
+ regs_ = regs;
+ arch_ = regs_ != nullptr ? regs->Arch() : ARCH_UNKNOWN;
+ }
Maps* GetMaps() { return maps_; }
std::shared_ptr<Memory>& GetProcessMemory() { return process_memory_; }
@@ -107,11 +116,12 @@
void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; }
- void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
+ void SetDexFiles(DexFiles* dex_files);
bool elf_from_memory_not_file() { return elf_from_memory_not_file_; }
ErrorCode LastErrorCode() { return last_error_.code; }
+ const char* LastErrorCodeString() { return GetErrorCodeString(last_error_.code); }
uint64_t LastErrorAddress() { return last_error_.address; }
uint64_t warnings() { return warnings_; }
@@ -126,6 +136,15 @@
protected:
Unwinder(size_t max_frames) : max_frames_(max_frames) { frames_.reserve(max_frames); }
+ Unwinder(size_t max_frames, ArchEnum arch) : max_frames_(max_frames), arch_(arch) {
+ frames_.reserve(max_frames);
+ }
+
+ void ClearErrors() {
+ warnings_ = WARNING_NONE;
+ last_error_.code = ERROR_NONE;
+ last_error_.address = 0;
+ }
void FillInDexFrame();
FrameData* FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t pc_adjustment);
@@ -145,20 +164,27 @@
bool elf_from_memory_not_file_ = false;
ErrorData last_error_;
uint64_t warnings_;
+ ArchEnum arch_ = ARCH_UNKNOWN;
};
class UnwinderFromPid : public Unwinder {
public:
UnwinderFromPid(size_t max_frames, pid_t pid) : Unwinder(max_frames), pid_(pid) {}
+ UnwinderFromPid(size_t max_frames, pid_t pid, ArchEnum arch)
+ : Unwinder(max_frames, arch), pid_(pid) {}
virtual ~UnwinderFromPid() = default;
- bool Init(ArchEnum arch);
+ bool Init();
+
+ void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
+ const std::vector<std::string>* map_suffixes_to_ignore = nullptr) override;
private:
pid_t pid_;
std::unique_ptr<Maps> maps_ptr_;
std::unique_ptr<JitDebug> jit_debug_ptr_;
std::unique_ptr<DexFiles> dex_files_ptr_;
+ bool initted_ = false;
};
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index c2bd836..0c6f9f8 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -314,7 +314,7 @@
JitDebug jit_debug(process_memory_);
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
- unwinder.SetJitDebug(&jit_debug, regs_->Arch());
+ unwinder.SetJitDebug(&jit_debug);
unwinder.Unwind();
std::string frame_info(DumpFrames(unwinder));
@@ -616,7 +616,7 @@
JitDebug jit_debug(process_memory_);
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
- unwinder.SetJitDebug(&jit_debug, regs_->Arch());
+ unwinder.SetJitDebug(&jit_debug);
unwinder.Unwind();
std::string frame_info(DumpFrames(unwinder));
@@ -939,7 +939,7 @@
std::unique_ptr<Regs> regs_copy(leak_data->regs->Clone());
JitDebug jit_debug(leak_data->process_memory);
Unwinder unwinder(128, leak_data->maps, regs_copy.get(), leak_data->process_memory);
- unwinder.SetJitDebug(&jit_debug, regs_copy->Arch());
+ unwinder.SetJitDebug(&jit_debug);
unwinder.Unwind();
ASSERT_EQ(76U, unwinder.NumFrames());
}
@@ -1062,7 +1062,7 @@
JitDebug jit_debug(process_memory_);
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
- unwinder.SetJitDebug(&jit_debug, regs_->Arch());
+ unwinder.SetJitDebug(&jit_debug);
unwinder.Unwind();
std::string frame_info(DumpFrames(unwinder));
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index f76a101..b11d213 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -170,7 +170,6 @@
unwinder.reset(new Unwinder(512, maps.get(), regs.get(), process_memory));
} else {
UnwinderFromPid* unwinder_from_pid = new UnwinderFromPid(512, getpid());
- ASSERT_TRUE(unwinder_from_pid->Init(regs->Arch()));
unwinder_from_pid->SetRegs(regs.get());
unwinder.reset(unwinder_from_pid);
}
@@ -283,7 +282,6 @@
ASSERT_TRUE(regs.get() != nullptr);
UnwinderFromPid unwinder(512, pid);
- ASSERT_TRUE(unwinder.Init(regs->Arch()));
unwinder.SetRegs(regs.get());
VerifyUnwind(&unwinder, kFunctionOrder);
@@ -335,7 +333,6 @@
ASSERT_TRUE(regs.get() != nullptr);
UnwinderFromPid unwinder(512, *pid);
- ASSERT_TRUE(unwinder.Init(regs->Arch()));
unwinder.SetRegs(regs.get());
VerifyUnwind(&unwinder, kFunctionOrder);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 915f248..8bae242 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -1182,7 +1182,7 @@
DexFiles dex_files(process_memory_);
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
- unwinder.SetDexFiles(&dex_files, ARCH_ARM);
+ unwinder.SetDexFiles(&dex_files);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_DEX_PC_NOT_IN_MAP, unwinder.warnings());
@@ -1735,7 +1735,7 @@
regs.FakeSetArch(ARCH_ARM);
JitDebug jit_debug(process_memory_);
Unwinder unwinder(10, maps_.get(), ®s, process_memory_);
- unwinder.SetJitDebug(&jit_debug, ARCH_ARM);
+ unwinder.SetJitDebug(&jit_debug);
FrameData frame = unwinder.BuildFrameFromPcOnly(0x100310);
EXPECT_EQ(0x10030eU, frame.pc);
@@ -1751,4 +1751,21 @@
EXPECT_EQ(0xeU, frame.function_offset);
}
+TEST_F(UnwinderTest, unwinder_from_pid_init_error) {
+ UnwinderFromPid unwinder(10, getpid());
+ ASSERT_DEATH(unwinder.Init(), "");
+}
+
+TEST_F(UnwinderTest, set_jit_debug_error) {
+ Unwinder unwinder(10, maps_.get(), process_memory_);
+ JitDebug jit_debug(process_memory_);
+ ASSERT_DEATH(unwinder.SetJitDebug(&jit_debug), "");
+}
+
+TEST_F(UnwinderTest, set_dex_files_error) {
+ Unwinder unwinder(10, maps_.get(), process_memory_);
+ DexFiles dex_files(process_memory_);
+ ASSERT_DEATH(unwinder.SetDexFiles(&dex_files), "");
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/VerifyBionicTerminationTest.cpp b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
index eb2b01d..3e67dc9 100644
--- a/libunwindstack/tests/VerifyBionicTerminationTest.cpp
+++ b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
@@ -94,7 +94,6 @@
std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
UnwinderFromPid unwinder(512, getpid());
- ASSERT_TRUE(unwinder.Init(regs->Arch()));
unwinder.SetRegs(regs.get());
RegsGetLocal(regs.get());
diff --git a/libunwindstack/tests/fuzz/UnwinderFuzz.cpp b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
index 2f4986a..1600547 100644
--- a/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
+++ b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
@@ -85,7 +85,7 @@
// Create instance
Unwinder unwinder(max_frames, maps.get(), regs.get(), memory);
- unwinder.SetJitDebug(jit_debug_ptr.get(), arch);
+ unwinder.SetJitDebug(jit_debug_ptr.get());
unwinder.SetResolveNames(data_provider.ConsumeBool());
// Call unwind
PerformUnwind(&data_provider, &unwinder);
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index 1812e50..ae45f06 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -90,11 +90,6 @@
printf("\n");
unwindstack::UnwinderFromPid unwinder(1024, pid);
- if (!unwinder.Init(regs->Arch())) {
- printf("Failed to init unwinder object.\n");
- return;
- }
-
unwinder.SetRegs(regs);
unwinder.Unwind();
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 64b58a8..c44a121 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -248,10 +248,6 @@
// Do an unwind so we know how much of the stack to save, and what
// elf files are involved.
unwindstack::UnwinderFromPid unwinder(1024, pid);
- if (!unwinder.Init(regs->Arch())) {
- printf("Unable to init unwinder object.\n");
- return 1;
- }
unwinder.SetRegs(regs);
uint64_t sp = regs->sp();
unwinder.Unwind();
diff --git a/libutils/FuzzFormatTypes.h b/libutils/FuzzFormatTypes.h
new file mode 100644
index 0000000..5d58a1a
--- /dev/null
+++ b/libutils/FuzzFormatTypes.h
@@ -0,0 +1,45 @@
+#pragma once
+#include <string>
+
+static const std::string kFormatChars = std::string("duoxXfFeEgGaAcsp");
+static constexpr int32_t kMaxFormatFlagValue = INT16_MAX;
+enum FormatChar : uint8_t {
+ SIGNED_DECIMAL = 0,
+ UNSIGNED_DECIMAL = 1,
+ UNSIGNED_OCTAL = 2,
+ UNSIGNED_HEX_LOWER = 3,
+ UNSIGNED_HEX_UPPER = 4,
+ // Uppercase/lowercase floating point impacts 'inf', 'infinity', and 'nan'
+ FLOAT_LOWER = 5,
+ FLOAT_UPPER = 6,
+ // Upper/lower impacts the "e" in exponents.
+ EXPONENT_LOWER = 7,
+ EXPONENT_UPPER = 8,
+ // %g will use %e or %f, whichever is shortest
+ SHORT_EXP_LOWER = 9,
+ // %G will use %E or %F, whichever is shortest
+ SHORT_EXP_UPPER = 10,
+ HEX_FLOAT_LOWER = 11,
+ HEX_FLOAT_UPPER = 12,
+ CHAR = 13,
+ STRING = 14,
+ POINTER = 15,
+ // Used by libfuzzer
+ kMaxValue = POINTER
+};
+
+bool canApplyFlag(FormatChar formatChar, char modifier) {
+ if (modifier == '#') {
+ return formatChar == UNSIGNED_OCTAL || formatChar == UNSIGNED_HEX_LOWER ||
+ formatChar == UNSIGNED_HEX_UPPER || formatChar == FLOAT_LOWER ||
+ formatChar == FLOAT_UPPER || formatChar == SHORT_EXP_LOWER ||
+ formatChar == SHORT_EXP_UPPER;
+ } else if (modifier == '.') {
+ return formatChar == SIGNED_DECIMAL || formatChar == UNSIGNED_DECIMAL ||
+ formatChar == UNSIGNED_OCTAL || formatChar == UNSIGNED_HEX_LOWER ||
+ formatChar == UNSIGNED_HEX_UPPER || formatChar == FLOAT_LOWER ||
+ formatChar == FLOAT_UPPER || formatChar == SHORT_EXP_LOWER ||
+ formatChar == SHORT_EXP_UPPER || formatChar == STRING;
+ }
+ return true;
+}
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
index 2adfe98..b02683c 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/String8_fuzz.cpp
@@ -15,97 +15,199 @@
*/
#include <functional>
#include <iostream>
+#include <memory>
+#include "FuzzFormatTypes.h"
#include "fuzzer/FuzzedDataProvider.h"
#include "utils/String8.h"
static constexpr int MAX_STRING_BYTES = 256;
static constexpr uint8_t MAX_OPERATIONS = 50;
+// Interestingly, 2147483614 (INT32_MAX - 33) seems to be the max value that is handled for format
+// flags. Unfortunately we need to use a smaller value so we avoid consuming too much memory.
-std::vector<std::function<void(FuzzedDataProvider&, android::String8, android::String8)>>
+void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend);
+std::vector<std::function<void(FuzzedDataProvider*, android::String8*, android::String8*)>>
operations = {
-
// Bytes and size
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.bytes();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->bytes();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.isEmpty();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->isEmpty();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.length();
- },
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.size();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->length();
},
// Casing
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.toUpper();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->toUpper();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.toLower();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->toLower();
},
-
- [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
- str1.removeAll(str2.c_str());
+ [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
+ str1->removeAll(str2->c_str());
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
- str1.compare(str2);
+ [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
+ const android::String8& constRef(*str2);
+ str1->compare(constRef);
},
// Append and format
- [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
- str1.append(str2);
+ [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
+ str1->append(str2->c_str());
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
- str1.appendFormat(str1.c_str(), str2.c_str());
- },
- [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void {
- str1.format(str1.c_str(), str2.c_str());
- },
+ [](FuzzedDataProvider* dataProvider, android::String8* str1, android::String8*)
+ -> void { fuzzFormat(dataProvider, str1, dataProvider->ConsumeBool()); },
// Find operation
- [](FuzzedDataProvider& dataProvider, android::String8 str1,
- android::String8) -> void {
+ [](FuzzedDataProvider* dataProvider, android::String8* str1,
+ android::String8* str2) -> void {
// We need to get a value from our fuzzer here.
- int start_index = dataProvider.ConsumeIntegralInRange<int>(0, str1.size());
- str1.find(str1.c_str(), start_index);
+ int start_index = dataProvider->ConsumeIntegralInRange<int>(0, str1->size());
+ str1->find(str2->c_str(), start_index);
},
// Path handling
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.getBasePath();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->getBasePath();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.getPathExtension();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->getPathExtension();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.getPathLeaf();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->getPathLeaf();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.getPathDir();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->getPathDir();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- str1.convertToResPath();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ str1->convertToResPath();
},
- [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void {
- android::String8 path_out_str = android::String8();
- str1.walkPath(&path_out_str);
- path_out_str.clear();
+ [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
+ std::shared_ptr<android::String8> path_out_str =
+ std::make_shared<android::String8>();
+ str1->walkPath(path_out_str.get());
+ path_out_str->clear();
},
- [](FuzzedDataProvider& dataProvider, android::String8 str1,
- android::String8) -> void {
- str1.setPathName(dataProvider.ConsumeBytesWithTerminator<char>(5).data());
+ [](FuzzedDataProvider* dataProvider, android::String8* str1,
+ android::String8*) -> void {
+ str1->setPathName(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
},
- [](FuzzedDataProvider& dataProvider, android::String8 str1,
- android::String8) -> void {
- str1.appendPath(dataProvider.ConsumeBytesWithTerminator<char>(5).data());
+ [](FuzzedDataProvider* dataProvider, android::String8* str1,
+ android::String8*) -> void {
+ str1->appendPath(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
},
};
-void callFunc(uint8_t index, FuzzedDataProvider& dataProvider, android::String8 str1,
- android::String8 str2) {
+void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend) {
+ FormatChar formatType = dataProvider->ConsumeEnum<FormatChar>();
+
+ std::string formatString("%");
+ // Width specifier
+ if (dataProvider->ConsumeBool()) {
+ // Left pad with zeroes
+ if (dataProvider->ConsumeBool()) {
+ formatString.push_back('0');
+ }
+ // Right justify (or left justify if negative)
+ int32_t justify = dataProvider->ConsumeIntegralInRange<int32_t>(-kMaxFormatFlagValue,
+ kMaxFormatFlagValue);
+ formatString += std::to_string(justify);
+ }
+
+ // The # specifier only works with o, x, X, a, A, e, E, f, F, g, and G
+ if (canApplyFlag(formatType, '#') && dataProvider->ConsumeBool()) {
+ formatString.push_back('#');
+ }
+
+ // Precision specifier
+ if (canApplyFlag(formatType, '.') && dataProvider->ConsumeBool()) {
+ formatString.push_back('.');
+ formatString +=
+ std::to_string(dataProvider->ConsumeIntegralInRange<int>(0, kMaxFormatFlagValue));
+ }
+
+ formatString.push_back(kFormatChars.at(static_cast<uint8_t>(formatType)));
+
+ switch (formatType) {
+ case SIGNED_DECIMAL: {
+ int val = dataProvider->ConsumeIntegral<int>();
+ if (shouldAppend) {
+ str1->appendFormat(formatString.c_str(), val);
+ } else {
+ str1->format(formatString.c_str(), dataProvider->ConsumeIntegral<int>());
+ }
+ break;
+ }
+
+ case UNSIGNED_DECIMAL:
+ case UNSIGNED_OCTAL:
+ case UNSIGNED_HEX_LOWER:
+ case UNSIGNED_HEX_UPPER: {
+ // Unsigned integers for u, o, x, and X
+ uint val = dataProvider->ConsumeIntegral<uint>();
+ if (shouldAppend) {
+ str1->appendFormat(formatString.c_str(), val);
+ } else {
+ str1->format(formatString.c_str(), val);
+ }
+ break;
+ }
+
+ case FLOAT_LOWER:
+ case FLOAT_UPPER:
+ case EXPONENT_LOWER:
+ case EXPONENT_UPPER:
+ case SHORT_EXP_LOWER:
+ case SHORT_EXP_UPPER:
+ case HEX_FLOAT_LOWER:
+ case HEX_FLOAT_UPPER: {
+ // Floating points for f, F, e, E, g, G, a, and A
+ float val = dataProvider->ConsumeFloatingPoint<float>();
+ if (shouldAppend) {
+ str1->appendFormat(formatString.c_str(), val);
+ } else {
+ str1->format(formatString.c_str(), val);
+ }
+ break;
+ }
+
+ case CHAR: {
+ char val = dataProvider->ConsumeIntegral<char>();
+ if (shouldAppend) {
+ str1->appendFormat(formatString.c_str(), val);
+ } else {
+ str1->format(formatString.c_str(), val);
+ }
+ break;
+ }
+
+ case STRING: {
+ std::string val = dataProvider->ConsumeRandomLengthString(MAX_STRING_BYTES);
+ if (shouldAppend) {
+ str1->appendFormat(formatString.c_str(), val.c_str());
+ } else {
+ str1->format(formatString.c_str(), val.c_str());
+ }
+ break;
+ }
+ case POINTER: {
+ uintptr_t val = dataProvider->ConsumeIntegral<uintptr_t>();
+ if (shouldAppend) {
+ str1->appendFormat(formatString.c_str(), val);
+ } else {
+ str1->format(formatString.c_str(), val);
+ }
+ break;
+ }
+ }
+}
+
+void callFunc(uint8_t index, FuzzedDataProvider* dataProvider, android::String8* str1,
+ android::String8* str2) {
operations[index](dataProvider, str1, str2);
}
@@ -120,14 +222,12 @@
// Create UTF-8 pointers
android::String8 str_one_utf8 = android::String8(vec.data());
android::String8 str_two_utf8 = android::String8(vec_two.data());
-
// Run operations against strings
int opsRun = 0;
while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
- callFunc(op, dataProvider, str_one_utf8, str_two_utf8);
+ operations[op](&dataProvider, &str_one_utf8, &str_two_utf8);
}
-
// Just to be extra sure these can be freed, we're going to explicitly clear
// them
str_one_utf8.clear();