blob: 2bdc1ddd388c22dafaf245f385ceaa73a5f28968 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_LIB_ZXDUMP_INCLUDE_LIB_ZXDUMP_DUMP_H_
#define SRC_LIB_ZXDUMP_INCLUDE_LIB_ZXDUMP_DUMP_H_
#include <lib/fit/function.h>
#include <lib/fitx/result.h>
#include <lib/zx/process.h>
#include <cstdint>
#include <limits>
#include <memory>
#include <optional>
#include <type_traits>
#include "types.h"
namespace zxdump {
// This is the default limit on ET_CORE file size (in bytes), i.e. unlimited.
inline constexpr size_t DefaultLimit() { return std::numeric_limits<size_t>::max(); }
// zxdump::ProcessDump<zx::process> or zxdump::ProcessDump<zx::unowned_process>
// represents one dump being made from the process whose handle is transferred
// or borrowed in the constructor argument. The same object can be reset and
// used again to make another dump from the same process, but most often this
// object is only kept alive while one dump is being collected and written out.
// These are move-only, move-assignable, and default-constructible types.
//
// The ProcessDumpBase class defines the public API for dumping even though the
// class isn't used directly. The ProcessDump template class below adapts to
// using this API with either an owned or an unowned process handle.
//
// The methods to produce the dump output work with any callable object that
// accepts a monotonically-increasing size_t offset in the "dump file" (really,
// stream position) and a zxdump::ByteView chunk of output. That call should
// return some `fitx::result<error_type>` type. The methods here propagate any
// error result by returning `fitx::result<error_type, ...>` values.
// zxdump::FdWriter (<lib/zxdump/fd-writer.h>) and similar objects return
// callable objects meant to be passed in here.
class ProcessDumpBase {
public:
ProcessDumpBase(ProcessDumpBase&&) = default;
ProcessDumpBase& operator=(ProcessDumpBase&&) = default;
~ProcessDumpBase();
// Reset to initial state, except that if the process is already suspended,
// it stays that way.
void clear();
// This must be called first.
//
// This collects information about memory and other process-wide state. The
// return value gives the total size of the ET_CORE file to be written.
// Collection is cut short without error if the ET_CORE file would already
// exceed the size limit without even including the memory. See above for
// how the callback is used.
//
// When this is complete, all data has been collected and all ET_CORE layout
// has been done and the live data from the process won't be consulted again.
// The only state still left to be collected from the process is the contents
// of its memory.
fitx::result<Error, size_t> CollectProcess(size_t limit = DefaultLimit());
// This must be called after CollectProcess.
//
// Accumulate header and note data to be written out, by repeatedly calling
// `dump(size_t offset, ByteView data)`. The callback returns some
// `fitx::result<error_type>` type. DumpHeaders returns a result of type
// `fitx::result<error_type, size_t>` with the result of the first failing
// callback, or with the total number of bytes dumped (i.e. the ending value
// of `offset`).
//
// This can be used to collect data in place or to stream it out. The
// callbacks supply a stream of data where the first chunk has offset 0 and
// later chunks always increase the offset. This streams out the ELF file
// and program headers, and then the note data that collects all the
// process-wide, and (optionally) thread, state. The views point into
// storage helds inside the DumpProcess object. They can be used freely
// until the object is destroyed or clear()'d.
template <typename Dump>
auto DumpHeaders(Dump&& dump, size_t limit) {
return CallImpl(&ProcessDumpBase::DumpHeadersImpl, std::forward<Dump>(dump), limit);
}
protected:
class Collector;
using DumpCallback = fit::function<bool(size_t offset, ByteView data)>;
ProcessDumpBase() = default;
void Emplace(zx::unowned_process process);
private:
template <typename Dump>
using DumpResult = std::decay_t<decltype(std::declval<Dump>()(size_t{}, ByteView{}))>;
template <typename Dump>
using DumpError = std::decay_t<decltype(std::declval<DumpResult<Dump>>().error_value())>;
template <typename Dump>
using DumpMemoryResult = fitx::result<Error, fitx::result<DumpError<Dump>, size_t>>;
template <typename Impl>
using ImplResult = std::invoke_result_t<Impl, ProcessDumpBase*, DumpCallback, size_t>;
template <typename Impl, typename Dump>
auto CallImpl(Impl&& impl, Dump&& dump, size_t limit)
-> fitx::result<DumpError<Dump>, ImplResult<Impl>> {
using Result = std::decay_t<std::invoke_result_t<Dump, size_t, ByteView>>;
Result result = fitx::ok();
auto dump_callback = [&](size_t offset, ByteView data) {
result = dump(offset, data);
return result.is_error();
};
auto value = std::invoke(impl, this, dump_callback, limit);
if (result.is_error()) {
return std::move(result).take_error();
}
return fitx::ok(std::move(value));
}
size_t DumpHeadersImpl(DumpCallback dump, size_t limit);
fitx::result<Error, size_t> DumpMemoryImpl(DumpCallback callback, size_t limit);
std::unique_ptr<Collector> collector_;
};
template <typename Handle>
class ProcessDump : public ProcessDumpBase {
public:
static_assert(std::is_same_v<Handle, zx::process> || std::is_same_v<Handle, zx::unowned_process>);
ProcessDump() = default;
// This takes ownership of the handle in the zx::process instantiation.
explicit ProcessDump(Handle process) noexcept;
private:
Handle process_;
};
static_assert(std::is_default_constructible_v<ProcessDump<zx::process>>);
static_assert(std::is_move_constructible_v<ProcessDump<zx::process>>);
static_assert(std::is_move_assignable_v<ProcessDump<zx::process>>);
static_assert(std::is_default_constructible_v<ProcessDump<zx::unowned_process>>);
static_assert(std::is_move_constructible_v<ProcessDump<zx::unowned_process>>);
static_assert(std::is_move_assignable_v<ProcessDump<zx::unowned_process>>);
} // namespace zxdump
#endif // SRC_LIB_ZXDUMP_INCLUDE_LIB_ZXDUMP_DUMP_H_