blob: 867f74bf9b92921bf3facaabc7e36d5bbe1a6ac4 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/profiling/memlog_connection_manager.h"
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread.h"
#include "chrome/profiling/allocation_tracker.h"
#include "chrome/profiling/json_exporter.h"
#include "chrome/profiling/memlog_receiver_pipe.h"
#include "chrome/profiling/memlog_stream_parser.h"
#include "third_party/zlib/zlib.h"
#if defined(OS_WIN)
#include <io.h>
#endif
namespace profiling {
struct MemlogConnectionManager::Connection {
Connection(AllocationTracker::CompleteCallback complete_cb,
BacktraceStorage* backtrace_storage,
base::ProcessId pid,
scoped_refptr<MemlogReceiverPipe> p)
: thread(base::StringPrintf("Sender %lld thread",
static_cast<long long>(pid))),
pipe(p),
tracker(std::move(complete_cb), backtrace_storage) {}
~Connection() {
// The parser may outlive this class because it's refcounted, make sure no
// callbacks are issued.
parser->DisconnectReceivers();
}
base::Thread thread;
scoped_refptr<MemlogReceiverPipe> pipe;
scoped_refptr<MemlogStreamParser> parser;
AllocationTracker tracker;
};
MemlogConnectionManager::MemlogConnectionManager(
scoped_refptr<base::SequencedTaskRunner> io_runner)
: io_runner_(std::move(io_runner)) {}
MemlogConnectionManager::~MemlogConnectionManager() {}
void MemlogConnectionManager::OnNewConnection(base::ScopedPlatformFile file,
base::ProcessId pid) {
base::AutoLock l(connections_lock_);
DCHECK(connections_.find(pid) == connections_.end());
scoped_refptr<MemlogReceiverPipe> new_pipe =
new MemlogReceiverPipe(std::move(file));
// Task to post to clean up the connection. Don't need to retain |this| since
// it wil be called by objects owned by the MemlogConnectionManager.
AllocationTracker::CompleteCallback complete_cb = base::BindOnce(
&MemlogConnectionManager::OnConnectionCompleteThunk,
base::Unretained(this), base::MessageLoop::current()->task_runner(), pid);
std::unique_ptr<Connection> connection = base::MakeUnique<Connection>(
std::move(complete_cb), &backtrace_storage_, pid, new_pipe);
connection->thread.Start();
connection->parser = new MemlogStreamParser(&connection->tracker);
new_pipe->SetReceiver(connection->thread.task_runner(), connection->parser);
connections_[pid] = std::move(connection);
io_runner_->PostTask(
FROM_HERE,
base::Bind(&MemlogReceiverPipe::StartReadingOnIOThread, new_pipe));
}
void MemlogConnectionManager::OnConnectionComplete(base::ProcessId pid) {
base::AutoLock l(connections_lock_);
auto found = connections_.find(pid);
CHECK(found != connections_.end());
found->second.release();
connections_.erase(found);
}
// Posts back to the given thread the connection complete message.
void MemlogConnectionManager::OnConnectionCompleteThunk(
scoped_refptr<base::SingleThreadTaskRunner> main_loop,
base::ProcessId pid) {
// This code is called by the allocation tracker which is owned by the
// connection manager. When we tell the connection manager a connection is
// done, we know the conncetion manager will still be in scope.
main_loop->PostTask(FROM_HERE,
base::Bind(&MemlogConnectionManager::OnConnectionComplete,
base::Unretained(this), pid));
}
void MemlogConnectionManager::DumpProcess(
base::ProcessId pid,
const std::vector<memory_instrumentation::mojom::VmRegionPtr>& maps,
base::File output_file) {
base::AutoLock l(connections_lock_);
// Lock all connections to prevent deallocations of atoms from
// BacktraceStorage. This only works if no new connections are made, which
// connections_lock_ guarantees.
std::vector<std::unique_ptr<base::AutoLock>> locks;
for (auto& it : connections_) {
Connection* connection = it.second.get();
locks.push_back(
base::MakeUnique<base::AutoLock>(*connection->parser->GetLock()));
}
auto it = connections_.find(pid);
if (it == connections_.end()) {
LOG(ERROR) << "No connections found for memory dump for pid:" << pid;
return;
}
Connection* connection = it->second.get();
std::ostringstream oss;
ExportAllocationEventSetToJSON(pid, connection->tracker.live_allocs(), maps,
oss);
std::string reply = oss.str();
// Pass ownership of the underlying fd/HANDLE to zlib.
base::PlatformFile platform_file = output_file.TakePlatformFile();
#if defined(OS_WIN)
// The underlying handle |platform_file| is also closed when |fd| is closed.
int fd = _open_osfhandle(reinterpret_cast<intptr_t>(platform_file), 0);
#else
int fd = platform_file;
#endif
gzFile gz_file = gzdopen(fd, "w");
gzwrite(gz_file, reply.c_str(), reply.size());
gzclose(gz_file);
}
} // namespace profiling