diff options
Diffstat (limited to 'src/profiling/memory/unwinding.cc')
-rw-r--r-- | src/profiling/memory/unwinding.cc | 105 |
1 files changed, 55 insertions, 50 deletions
diff --git a/src/profiling/memory/unwinding.cc b/src/profiling/memory/unwinding.cc index 6b030c09c..31b9a9c96 100644 --- a/src/profiling/memory/unwinding.cc +++ b/src/profiling/memory/unwinding.cc @@ -44,14 +44,12 @@ #include <procinfo/process_map.h> +#include "perfetto/base/file_utils.h" #include "perfetto/base/logging.h" +#include "perfetto/base/scoped_file.h" +#include "perfetto/base/string_utils.h" #include "perfetto/base/task_runner.h" -#include "perfetto/ext/base/file_utils.h" -#include "perfetto/ext/base/scoped_file.h" -#include "perfetto/ext/base/string_utils.h" -#include "perfetto/ext/base/thread_task_runner.h" - -#include "src/profiling/memory/utils.h" +#include "perfetto/base/thread_task_runner.h" #include "src/profiling/memory/wire_protocol.h" namespace perfetto { @@ -72,49 +70,54 @@ constexpr size_t kUnwindBatchSize = 1000; static std::vector<std::string> kSkipMaps{"heapprofd_client.so"}; #pragma GCC diagnostic pop -size_t GetRegsSize(unwindstack::Regs* regs) { - if (regs->Is32Bit()) - return sizeof(uint32_t) * regs->total_regs(); - return sizeof(uint64_t) * regs->total_regs(); -} - -void ReadFromRawData(unwindstack::Regs* regs, void* raw_data) { - memcpy(regs->RawData(), raw_data, GetRegsSize(regs)); -} - -} // namespace - -std::unique_ptr<unwindstack::Regs> CreateRegsFromRawData( - unwindstack::ArchEnum arch, - void* raw_data) { +std::unique_ptr<unwindstack::Regs> CreateFromRawData(unwindstack::ArchEnum arch, + void* raw_data) { std::unique_ptr<unwindstack::Regs> ret; + // unwindstack::RegsX::Read returns a raw ptr which we are expected to free. switch (arch) { case unwindstack::ARCH_X86: - ret.reset(new unwindstack::RegsX86()); + ret.reset(unwindstack::RegsX86::Read(raw_data)); break; case unwindstack::ARCH_X86_64: - ret.reset(new unwindstack::RegsX86_64()); + ret.reset(unwindstack::RegsX86_64::Read(raw_data)); break; case unwindstack::ARCH_ARM: - ret.reset(new unwindstack::RegsArm()); + ret.reset(unwindstack::RegsArm::Read(raw_data)); break; case unwindstack::ARCH_ARM64: - ret.reset(new unwindstack::RegsArm64()); + ret.reset(unwindstack::RegsArm64::Read(raw_data)); break; case unwindstack::ARCH_MIPS: - ret.reset(new unwindstack::RegsMips()); + ret.reset(unwindstack::RegsMips::Read(raw_data)); break; case unwindstack::ARCH_MIPS64: - ret.reset(new unwindstack::RegsMips64()); + ret.reset(unwindstack::RegsMips64::Read(raw_data)); break; case unwindstack::ARCH_UNKNOWN: + ret.reset(nullptr); break; } - if (ret) - ReadFromRawData(ret.get(), raw_data); return ret; } +// Behaves as a pread64, emulating it if not already exposed by the standard +// library. Safe to use on 32bit platforms for addresses with the top bit set. +// Clobbers the |fd| seek position if emulating. +ssize_t ReadAtOffsetClobberSeekPos(int fd, + void* buf, + size_t count, + uint64_t addr) { +#ifdef __BIONIC__ + return pread64(fd, buf, count, static_cast<off64_t>(addr)); +#else + if (lseek64(fd, static_cast<off64_t>(addr), SEEK_SET) == -1) + return -1; + return read(fd, buf, count); +#endif +} + +} // namespace + StackOverlayMemory::StackOverlayMemory(std::shared_ptr<unwindstack::Memory> mem, uint64_t sp, uint8_t* stack, @@ -134,8 +137,7 @@ size_t StackOverlayMemory::Read(uint64_t addr, void* dst, size_t size) { FDMemory::FDMemory(base::ScopedFile mem_fd) : mem_fd_(std::move(mem_fd)) {} size_t FDMemory::Read(uint64_t addr, void* dst, size_t size) { - ssize_t rd = ReadAtOffsetClobberSeekPos(*mem_fd_, dst, size, - static_cast<off64_t>(addr)); + ssize_t rd = ReadAtOffsetClobberSeekPos(*mem_fd_, dst, size, addr); if (rd == -1) { PERFETTO_DPLOG("read of %zu at offset %" PRIu64, size, addr); return 0; @@ -176,8 +178,8 @@ void FileDescriptorMaps::Reset() { bool DoUnwind(WireMessage* msg, UnwindingMetadata* metadata, AllocRecord* out) { AllocMetadata* alloc_metadata = msg->alloc_header; - std::unique_ptr<unwindstack::Regs> regs(CreateRegsFromRawData( - alloc_metadata->arch, alloc_metadata->register_data)); + std::unique_ptr<unwindstack::Regs> regs( + CreateFromRawData(alloc_metadata->arch, alloc_metadata->register_data)); if (regs == nullptr) { PERFETTO_DLOG("Unable to construct unwindstack::Regs"); unwindstack::FrameData frame_data{}; @@ -206,9 +208,6 @@ bool DoUnwind(WireMessage* msg, UnwindingMetadata* metadata, AllocRecord* out) { if (attempt > 0) { PERFETTO_DLOG("Reparsing maps"); metadata->ReparseMaps(); - // Regs got invalidated by libuwindstack's speculative jump. - // Reset. - ReadFromRawData(regs.get(), alloc_metadata->register_data); out->reparsed_map = true; #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) unwinder.SetJitDebug(metadata->jit_debug.get(), regs->Arch()); @@ -248,12 +247,22 @@ void UnwindingWorker::OnDisconnect(base::UnixSocket* self) { // TODO(fmayer): Maybe try to drain shmem one last time. auto it = client_data_.find(self->peer_pid()); if (it == client_data_.end()) { - PERFETTO_DFATAL_OR_ELOG("Disconnected unexpected socket."); + PERFETTO_DFATAL("Disconnected unexpecter socket."); return; } ClientData& client_data = it->second; SharedRingBuffer& shmem = client_data.shmem; + // Currently, these stats are used to determine whether the application + // disconnected due to an error condition (i.e. buffer overflow) or + // volutarily. Because a buffer overflow leads to an immediate disconnect, we + // do not need these stats when heapprofd tears down the tracing session. + // + // TODO(fmayer): We should make it that normal disconnects also go through + // this code path, so we can write other stats to the result. This will also + // allow us to free the bookkeeping data earlier for processes that exit + // during the session. See TODO in + // HeapprofdProducer::HandleSocketDisconnected. SharedRingBuffer::Stats stats = {}; { auto lock = shmem.AcquireLock(ScopedSpinlock::Mode::Try); @@ -294,6 +303,8 @@ void UnwindingWorker::HandleUnwindBatch(pid_t peer_pid) { bool repost_task = false; for (i = 0; i < kUnwindBatchSize; ++i) { uint64_t reparses_before = client_data.metadata.reparses; + // TODO(fmayer): Allow spinlock acquisition to fail and repost Task if it + // did. buf = shmem.BeginRead(); if (!buf) break; @@ -330,7 +341,7 @@ void UnwindingWorker::HandleBuffer(const SharedRingBuffer::Buffer& buf, // char* has stronger guarantees regarding aliasing. // see https://timsong-cpp.github.io/cppwp/n3337/basic.lval#10.8 if (!ReceiveWireMessage(reinterpret_cast<char*>(buf.data), buf.size, &msg)) { - PERFETTO_DFATAL_OR_ELOG("Failed to receive wire message."); + PERFETTO_DFATAL("Failed to receive wire message."); return; } @@ -352,7 +363,7 @@ void UnwindingWorker::HandleBuffer(const SharedRingBuffer::Buffer& buf, memcpy(&rec.free_batch, msg.free_header, sizeof(*msg.free_header)); delegate->PostFreeRecord(std::move(rec)); } else { - PERFETTO_DFATAL_OR_ELOG("Invalid record type."); + PERFETTO_DFATAL("Invalid record type."); } } @@ -372,11 +383,12 @@ void UnwindingWorker::PostHandoffSocket(HandoffData handoff_data) { void UnwindingWorker::HandleHandoffSocket(HandoffData handoff_data) { auto sock = base::UnixSocket::AdoptConnected( handoff_data.sock.ReleaseFd(), this, this->thread_task_runner_.get(), - base::SockFamily::kUnix, base::SockType::kStream); + base::SockType::kStream); pid_t peer_pid = sock->peer_pid(); - UnwindingMetadata metadata(peer_pid, std::move(handoff_data.maps_fd), - std::move(handoff_data.mem_fd)); + UnwindingMetadata metadata(peer_pid, + std::move(handoff_data.fds[kHandshakeMaps]), + std::move(handoff_data.fds[kHandshakeMem])); ClientData client_data{ handoff_data.data_source_instance_id, std::move(sock), @@ -395,14 +407,7 @@ void UnwindingWorker::PostDisconnectSocket(pid_t pid) { } void UnwindingWorker::HandleDisconnectSocket(pid_t pid) { - auto it = client_data_.find(pid); - if (it == client_data_.end()) { - PERFETTO_DFATAL_OR_ELOG("Trying to disconnect unknown socket."); - return; - } - ClientData& client_data = it->second; - // Shutdown and call OnDisconnect handler. - client_data.sock->Shutdown(/* notify= */ true); + client_data_.erase(pid); } UnwindingWorker::Delegate::~Delegate() = default; |