aboutsummaryrefslogtreecommitdiff
path: root/src/profiling/memory/unwinding.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/profiling/memory/unwinding.cc')
-rw-r--r--src/profiling/memory/unwinding.cc105
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;