Commit 0eb57d23 authored by Erik Chen's avatar Erik Chen Committed by Commit Bot

Remove the DumpProcess profiling_service mojom method.

Previously, DumpProcess would create a full trace, with metadata provided by
ProfilingProcessHost, and write the result to a file. This duplicated
functionality of DumpProcessesForTracing, which uses the Tracing service to
create a fully-fledged trace.

Bug: 
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: Ibca11524f23b7fe8d9808370e05225298a0794a5
Reviewed-on: https://chromium-review.googlesource.com/798152
Commit-Queue: Erik Chen <erikchen@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarBrett Wilson <brettw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#521216}
parent 88cc1869
...@@ -314,13 +314,20 @@ std::unique_ptr<base::Value> ReadDumpFile(const base::FilePath& path) { ...@@ -314,13 +314,20 @@ std::unique_ptr<base::Value> ReadDumpFile(const base::FilePath& path) {
return base::JSONReader::Read(dump_string); return base::JSONReader::Read(dump_string);
} }
void QuitRunLoopAndCheckSecondParameter(base::RunLoop* run_loop, bool result) {
ASSERT_TRUE(result);
run_loop->Quit();
}
void DumpProcess(base::ProcessId pid, const base::FilePath& dumpfile_path) { void DumpProcess(base::ProcessId pid, const base::FilePath& dumpfile_path) {
profiling::ProfilingProcessHost* pph = profiling::ProfilingProcessHost* pph =
profiling::ProfilingProcessHost::GetInstance(); profiling::ProfilingProcessHost::GetInstance();
base::RunLoop run_loop; base::RunLoop run_loop;
pph->RequestProcessDump( pph->SaveTraceWithHeapDumpToFile(
pid, dumpfile_path, dumpfile_path,
base::BindOnce(&base::RunLoop::Quit, base::Unretained(&run_loop))); base::BindOnce(&QuitRunLoopAndCheckSecondParameter,
base::Unretained(&run_loop)),
true);
run_loop.Run(); run_loop.Run();
} }
...@@ -365,10 +372,7 @@ IN_PROC_BROWSER_TEST_P(MemlogBrowserTest, EndToEnd) { ...@@ -365,10 +372,7 @@ IN_PROC_BROWSER_TEST_P(MemlogBrowserTest, EndToEnd) {
GetParam() == switches::kMemlogModeBrowser || GetParam() == switches::kMemlogModeBrowser ||
GetParam() == switches::kMemlogModeMinimal) { GetParam() == switches::kMemlogModeMinimal) {
ASSERT_TRUE(dump_json); ASSERT_TRUE(dump_json);
EXPECT_EQ(0, NumProcessesWithName(dump_json.get(), "Renderer"));
ValidateBrowserAllocations(dump_json.get()); ValidateBrowserAllocations(dump_json.get());
} else {
ASSERT_FALSE(dump_json) << "Browser process unexpectedly profiled.";
} }
} }
...@@ -390,9 +394,6 @@ IN_PROC_BROWSER_TEST_P(MemlogBrowserTest, EndToEnd) { ...@@ -390,9 +394,6 @@ IN_PROC_BROWSER_TEST_P(MemlogBrowserTest, EndToEnd) {
GetParam() == switches::kMemlogModeRendererSampling) { GetParam() == switches::kMemlogModeRendererSampling) {
ASSERT_TRUE(dump_json); ASSERT_TRUE(dump_json);
ValidateRendererAllocations(dump_json.get()); ValidateRendererAllocations(dump_json.get());
EXPECT_EQ(0, NumProcessesWithName(dump_json.get(), "Browser"));
} else {
ASSERT_FALSE(dump_json) << "Renderer process unexpectedly profiled.";
} }
} }
......
...@@ -47,6 +47,11 @@ ...@@ -47,6 +47,11 @@
#include "mojo/edk/embedder/platform_channel_pair.h" #include "mojo/edk/embedder/platform_channel_pair.h"
#include "mojo/edk/embedder/scoped_platform_handle.h" #include "mojo/edk/embedder/scoped_platform_handle.h"
#include "mojo/public/cpp/system/platform_handle.h" #include "mojo/public/cpp/system/platform_handle.h"
#include "third_party/zlib/zlib.h"
#if defined(OS_WIN)
#include <io.h>
#endif
namespace { namespace {
...@@ -100,8 +105,6 @@ bool ProfilingProcessHost::has_started_ = false; ...@@ -100,8 +105,6 @@ bool ProfilingProcessHost::has_started_ = false;
namespace { namespace {
constexpr char kNoTriggerName[] = "";
// This helper class cleans up initialization boilerplate for the callers who // This helper class cleans up initialization boilerplate for the callers who
// need to create ProfilingClients bound to various different things. // need to create ProfilingClients bound to various different things.
class ProfilingClientBinder { class ProfilingClientBinder {
...@@ -440,20 +443,38 @@ void ProfilingProcessHost::ConfigureBackgroundProfilingTriggers() { ...@@ -440,20 +443,38 @@ void ProfilingProcessHost::ConfigureBackgroundProfilingTriggers() {
background_triggers_.StartTimer(); background_triggers_.StartTimer();
} }
void ProfilingProcessHost::RequestProcessDump(base::ProcessId pid, void ProfilingProcessHost::SaveTraceWithHeapDumpToFile(
base::FilePath dest, base::FilePath dest,
base::OnceClosure done) { SaveTraceFinishedCallback done,
if (!connector_) { bool stop_immediately_after_heap_dump_for_tests) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (!profiling_service_.is_bound()) {
DLOG(ERROR) DLOG(ERROR)
<< "Requesting process dump when profiling process hasn't started."; << "Requesting heap dump when profiling process hasn't started.";
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(done), false));
return; return;
} }
base::PostTaskWithTraits( auto finish_trace_callback = base::BindOnce(
FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()}, [](base::FilePath dest, SaveTraceFinishedCallback done, bool success,
base::BindOnce(&ProfilingProcessHost::GetOutputFileOnBlockingThread, std::string trace) {
base::Unretained(this), pid, std::move(dest), if (!success) {
kNoTriggerName, std::move(done))); content::BrowserThread::GetTaskRunnerForThread(
content::BrowserThread::UI)
->PostTask(FROM_HERE, base::BindOnce(std::move(done), false));
return;
}
base::PostTaskWithTraits(
FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
base::BindOnce(
&ProfilingProcessHost::SaveTraceToFileOnBlockingThread,
base::Unretained(ProfilingProcessHost::GetInstance()),
std::move(dest), std::move(trace), std::move(done)));
},
std::move(dest), std::move(done));
RequestTraceWithHeapDump(std::move(finish_trace_callback),
stop_immediately_after_heap_dump_for_tests);
} }
void ProfilingProcessHost::RequestProcessReport(std::string trigger_name) { void ProfilingProcessHost::RequestProcessReport(std::string trigger_name) {
...@@ -569,74 +590,41 @@ void ProfilingProcessHost::LaunchAsService() { ...@@ -569,74 +590,41 @@ void ProfilingProcessHost::LaunchAsService() {
} }
} }
void ProfilingProcessHost::GetOutputFileOnBlockingThread( void ProfilingProcessHost::SaveTraceToFileOnBlockingThread(
base::ProcessId pid,
base::FilePath dest, base::FilePath dest,
std::string trigger_name, std::string trace,
base::OnceClosure done) { SaveTraceFinishedCallback done) {
base::ScopedClosureRunner done_runner(std::move(done));
base::File file(dest, base::File file(dest,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::BindOnce(&ProfilingProcessHost::HandleDumpProcessOnIOThread,
base::Unretained(this), pid, std::move(dest),
std::move(file), std::move(trigger_name),
done_runner.Release()));
}
void ProfilingProcessHost::HandleDumpProcessOnIOThread(base::ProcessId pid,
base::FilePath file_path,
base::File file,
std::string trigger_name,
base::OnceClosure done) {
mojo::ScopedHandle handle = mojo::WrapPlatformFile(file.TakePlatformFile());
profiling_service_->DumpProcess(
pid, std::move(handle), GetMetadataJSONForTrace(),
base::BindOnce(&ProfilingProcessHost::OnProcessDumpComplete,
base::Unretained(this), std::move(file_path),
std::move(trigger_name), std::move(done)));
}
void ProfilingProcessHost::OnProcessDumpComplete(base::FilePath file_path, // Pass ownership of the underlying fd/HANDLE to zlib.
std::string trigger_name, base::PlatformFile platform_file = file.TakePlatformFile();
base::OnceClosure done, #if defined(OS_WIN)
bool success) { // The underlying handle |platform_file| is also closed when |fd| is closed.
base::ScopedClosureRunner done_runner(std::move(done)); int fd = _open_osfhandle(reinterpret_cast<intptr_t>(platform_file), 0);
if (!success) { #else
DLOG(ERROR) << "Cannot dump process."; int fd = platform_file;
// On any errors, the requested trace output file is deleted. #endif
base::PostTaskWithTraits( gzFile gz_file = gzdopen(fd, "w");
FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND}, if (!gz_file) {
base::BindOnce(base::IgnoreResult(&base::DeleteFile), file_path, DLOG(ERROR) << "Cannot compress trace file";
false)); content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::UI)
->PostTask(FROM_HERE, base::BindOnce(std::move(done), false));
return; return;
} }
size_t written_bytes = gzwrite(gz_file, trace.c_str(), trace.size());
gzclose(gz_file);
content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::UI)
->PostTask(FROM_HERE, base::BindOnce(std::move(done),
written_bytes == trace.size()));
} }
void ProfilingProcessHost::SetMode(Mode mode) { void ProfilingProcessHost::SetMode(Mode mode) {
mode_ = mode; mode_ = mode;
} }
std::unique_ptr<base::DictionaryValue>
ProfilingProcessHost::GetMetadataJSONForTrace() {
std::unique_ptr<base::DictionaryValue> metadata_dict(
new base::DictionaryValue);
metadata_dict->SetKey(
"product-version",
base::Value(version_info::GetProductNameAndVersionForUserAgent()));
metadata_dict->SetKey("user-agent", base::Value(GetUserAgent()));
metadata_dict->SetKey("os-name",
base::Value(base::SysInfo::OperatingSystemName()));
metadata_dict->SetKey(
"command_line",
base::Value(
base::CommandLine::ForCurrentProcess()->GetCommandLineString()));
metadata_dict->SetKey(
"os-arch", base::Value(base::SysInfo::OperatingSystemArchitecture()));
return metadata_dict;
}
void ProfilingProcessHost::ReportMetrics() { void ProfilingProcessHost::ReportMetrics() {
UMA_HISTOGRAM_ENUMERATION("OutOfProcessHeapProfiling.ProfilingMode", mode(), UMA_HISTOGRAM_ENUMERATION("OutOfProcessHeapProfiling.ProfilingMode", mode(),
Mode::kCount); Mode::kCount);
......
...@@ -105,11 +105,16 @@ class ProfilingProcessHost : public content::BrowserChildProcessObserver, ...@@ -105,11 +105,16 @@ class ProfilingProcessHost : public content::BrowserChildProcessObserver,
void ConfigureBackgroundProfilingTriggers(); void ConfigureBackgroundProfilingTriggers();
// Sends a message to the profiling process to dump the given process' // Create a trace with a heap dump at the given path.
// memory data to the given file. // This is equivalent to navigating to chrome://tracing, taking a trace with
void RequestProcessDump(base::ProcessId pid, // only the memory-infra category selected, waiting 10 seconds, and saving the
base::FilePath dest, // result to |dest|.
base::OnceClosure done); // |done| will be called on the UI thread.
using SaveTraceFinishedCallback = base::OnceCallback<void(bool success)>;
void SaveTraceWithHeapDumpToFile(
base::FilePath dest,
SaveTraceFinishedCallback done,
bool stop_immediately_after_heap_dump_for_tests);
// Sends a message to the profiling process to report all profiled processes // Sends a message to the profiling process to report all profiled processes
// memory data to the crash server (slow-report). // memory data to the crash server (slow-report).
...@@ -181,23 +186,9 @@ class ProfilingProcessHost : public content::BrowserChildProcessObserver, ...@@ -181,23 +186,9 @@ class ProfilingProcessHost : public content::BrowserChildProcessObserver,
base::ProcessId pid, base::ProcessId pid,
profiling::mojom::ProcessType process_type); profiling::mojom::ProcessType process_type);
void GetOutputFileOnBlockingThread(base::ProcessId pid, void SaveTraceToFileOnBlockingThread(base::FilePath dest,
base::FilePath dest, std::string trace,
std::string trigger_name, SaveTraceFinishedCallback done);
base::OnceClosure done);
void HandleDumpProcessOnIOThread(base::ProcessId pid,
base::FilePath file_path,
base::File file,
std::string trigger_name,
base::OnceClosure done);
void OnProcessDumpComplete(base::FilePath file_path,
std::string trigger_name,
base::OnceClosure done,
bool success);
// Returns the metadata for the trace. This is the minimum amount of metadata
// needed to symbolize the trace.
std::unique_ptr<base::DictionaryValue> GetMetadataJSONForTrace();
// Reports the profiling mode. // Reports the profiling mode.
void ReportMetrics(); void ReportMetrics();
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
margin-bottom: 1em; margin-bottom: 1em;
padding: 5px; padding: 5px;
} }
.refresh { .commands {
padding: 5px; padding: 5px;
} }
</style> </style>
......
...@@ -8,8 +8,8 @@ function requestProcessList() { ...@@ -8,8 +8,8 @@ function requestProcessList() {
chrome.send('requestProcessList'); chrome.send('requestProcessList');
} }
function dumpProcess(pid) { function saveDump() {
chrome.send('dumpProcess', [pid]); chrome.send('saveDump');
} }
function reportProcess(pid) { function reportProcess(pid) {
...@@ -29,6 +29,10 @@ function addListRow(table, celltype, cols) { ...@@ -29,6 +29,10 @@ function addListRow(table, celltype, cols) {
table.appendChild(tr); table.appendChild(tr);
} }
function setSaveDumpMessage(data) {
$('save_dump_text').innerText = data;
}
function returnProcessList(data) { function returnProcessList(data) {
$('message').innerText = data['message']; $('message').innerText = data['message'];
...@@ -39,30 +43,34 @@ function returnProcessList(data) { ...@@ -39,30 +43,34 @@ function returnProcessList(data) {
if (processes.length == 0) if (processes.length == 0)
return; // No processes to dump, don't make the table and refresh button. return; // No processes to dump, don't make the table and refresh button.
// Add the refresh button. // Add the refresh and save-dump buttons.
let refreshDiv = document.createElement('div'); let commandsDiv = document.createElement('div');
refreshDiv.className = 'refresh'; commandsDiv.className = 'commands';
let refreshButton = document.createElement('button'); let refreshButton = document.createElement('button');
refreshButton.innerText = '\u21ba Refresh process list'; refreshButton.innerText = '\u21ba Refresh process list';
refreshButton.onclick = () => requestProcessList(); refreshButton.onclick = () => requestProcessList();
refreshDiv.appendChild(refreshButton); commandsDiv.appendChild(refreshButton);
proclist.appendChild(refreshDiv); let saveDumpButton = document.createElement('button');
saveDumpButton.innerText = '\u21e9 Save dump';
saveDumpButton.onclick = () => saveDump();
commandsDiv.appendChild(saveDumpButton);
let saveDumpText = document.createElement('div');
saveDumpText.id = 'save_dump_text';
commandsDiv.appendChild(saveDumpText);
proclist.appendChild(commandsDiv);
let table = document.createElement('table'); let table = document.createElement('table');
// Heading. // Heading.
addListRow(table, 'th', [ addListRow(table, 'th', [
null, null, document.createTextNode('Process ID'), null, document.createTextNode('Process ID'), document.createTextNode('Name')
document.createTextNode('Name')
]); ]);
for (let proc of processes) { for (let proc of processes) {
let procId = proc[0]; let procId = proc[0];
let saveButton = document.createElement('button');
saveButton.innerText = '\u21e9 Save dump';
saveButton.onclick = () => dumpProcess(procId);
let reportButton = document.createElement('button'); let reportButton = document.createElement('button');
reportButton.innerText = '\uD83D\uDC1E Report'; reportButton.innerText = '\uD83D\uDC1E Report';
reportButton.onclick = () => reportProcess(procId); reportButton.onclick = () => reportProcess(procId);
...@@ -70,8 +78,7 @@ function returnProcessList(data) { ...@@ -70,8 +78,7 @@ function returnProcessList(data) {
let procIdText = document.createTextNode(procId.toString()); let procIdText = document.createTextNode(procId.toString());
let description = document.createTextNode(proc[1]); let description = document.createTextNode(proc[1]);
addListRow( addListRow(table, 'td', [reportButton, procIdText, description]);
table, 'td', [saveButton, reportButton, procIdText, description]);
} }
proclist.appendChild(table); proclist.appendChild(table);
......
...@@ -125,8 +125,8 @@ class MemoryInternalsDOMHandler : public content::WebUIMessageHandler, ...@@ -125,8 +125,8 @@ class MemoryInternalsDOMHandler : public content::WebUIMessageHandler,
// Callback for the "requestProcessList" message. // Callback for the "requestProcessList" message.
void HandleRequestProcessList(const base::ListValue* args); void HandleRequestProcessList(const base::ListValue* args);
// Callback for the "dumpProcess" message. // Callback for the "saveDump" message.
void HandleDumpProcess(const base::ListValue* args); void HandleSaveDump(const base::ListValue* args);
// Callback for the "reportProcess" message. // Callback for the "reportProcess" message.
void HandleReportProcess(const base::ListValue* args); void HandleReportProcess(const base::ListValue* args);
...@@ -144,6 +144,8 @@ class MemoryInternalsDOMHandler : public content::WebUIMessageHandler, ...@@ -144,6 +144,8 @@ class MemoryInternalsDOMHandler : public content::WebUIMessageHandler,
void* params) override; void* params) override;
void FileSelectionCanceled(void* params) override; void FileSelectionCanceled(void* params) override;
void SaveTraceFinished(bool success);
scoped_refptr<ui::SelectFileDialog> select_file_dialog_; scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
content::WebUI* web_ui_; // The WebUI that owns us. content::WebUI* web_ui_; // The WebUI that owns us.
...@@ -168,8 +170,8 @@ void MemoryInternalsDOMHandler::RegisterMessages() { ...@@ -168,8 +170,8 @@ void MemoryInternalsDOMHandler::RegisterMessages() {
base::Bind(&MemoryInternalsDOMHandler::HandleRequestProcessList, base::Bind(&MemoryInternalsDOMHandler::HandleRequestProcessList,
base::Unretained(this))); base::Unretained(this)));
web_ui()->RegisterMessageCallback( web_ui()->RegisterMessageCallback(
"dumpProcess", "saveDump",
base::BindRepeating(&MemoryInternalsDOMHandler::HandleDumpProcess, base::BindRepeating(&MemoryInternalsDOMHandler::HandleSaveDump,
base::Unretained(this))); base::Unretained(this)));
web_ui()->RegisterMessageCallback( web_ui()->RegisterMessageCallback(
"reportProcess", "reportProcess",
...@@ -187,26 +189,27 @@ void MemoryInternalsDOMHandler::HandleRequestProcessList( ...@@ -187,26 +189,27 @@ void MemoryInternalsDOMHandler::HandleRequestProcessList(
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr()));
} }
void MemoryInternalsDOMHandler::HandleDumpProcess(const base::ListValue* args) { void MemoryInternalsDOMHandler::HandleSaveDump(const base::ListValue* args) {
if (!args->is_list() || args->GetList().size() != 1)
return;
const base::Value& pid_value = args->GetList()[0];
if (!pid_value.is_int())
return;
int pid = pid_value.GetInt();
base::FilePath default_file = base::FilePath().AppendASCII( base::FilePath default_file = base::FilePath().AppendASCII(
base::StringPrintf("memlog_%d.json.gz", pid)); base::StringPrintf("trace_with_heap_dump.json.gz"));
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
base::Value result("Saving...");
AllowJavascript();
CallJavascriptFunction("setSaveDumpMessage", result);
// On Android write to the user data dir. // On Android write to the user data dir.
// TODO(bug 757115) Does it make sense to show the Android file picker here // TODO(bug 757115) Does it make sense to show the Android file picker here
// instead? Need to test what that looks like. // instead? Need to test what that looks like.
base::FilePath user_data_dir; base::FilePath user_data_dir;
PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
base::FilePath output_path = user_data_dir.Append(default_file); base::FilePath output_path = user_data_dir.Append(default_file);
ProfilingProcessHost::GetInstance()->RequestProcessDump( ProfilingProcessHost::GetInstance()->SaveTraceWithHeapDumpToFile(
pid, std::move(output_path), base::OnceClosure()); std::move(output_path),
base::BindOnce(&MemoryInternalsDOMHandler::SaveTraceFinished,
weak_factory_.GetWeakPtr()),
false);
(void)web_ui_; // Avoid warning about not using private web_ui_ member. (void)web_ui_; // Avoid warning about not using private web_ui_ member.
#else #else
if (select_file_dialog_) if (select_file_dialog_)
...@@ -215,12 +218,10 @@ void MemoryInternalsDOMHandler::HandleDumpProcess(const base::ListValue* args) { ...@@ -215,12 +218,10 @@ void MemoryInternalsDOMHandler::HandleDumpProcess(const base::ListValue* args) {
this, this,
std::make_unique<ChromeSelectFilePolicy>(web_ui_->GetWebContents())); std::make_unique<ChromeSelectFilePolicy>(web_ui_->GetWebContents()));
// Pass the PID to dump via the "params" for the callback to use.
select_file_dialog_->SelectFile( select_file_dialog_->SelectFile(
ui::SelectFileDialog::SELECT_SAVEAS_FILE, base::string16(), default_file, ui::SelectFileDialog::SELECT_SAVEAS_FILE, base::string16(), default_file,
nullptr, 0, FILE_PATH_LITERAL(".json.gz"), nullptr, 0, FILE_PATH_LITERAL(".json.gz"),
web_ui_->GetWebContents()->GetTopLevelNativeWindow(), web_ui_->GetWebContents()->GetTopLevelNativeWindow(), nullptr);
reinterpret_cast<void*>(pid));
#endif #endif
} }
...@@ -301,16 +302,20 @@ void MemoryInternalsDOMHandler::ReturnProcessListOnUIThread( ...@@ -301,16 +302,20 @@ void MemoryInternalsDOMHandler::ReturnProcessListOnUIThread(
AllowJavascript(); AllowJavascript();
CallJavascriptFunction("returnProcessList", result); CallJavascriptFunction("returnProcessList", result);
DisallowJavascript();
} }
void MemoryInternalsDOMHandler::FileSelected(const base::FilePath& path, void MemoryInternalsDOMHandler::FileSelected(const base::FilePath& path,
int index, int index,
void* params) { void* params) {
// The PID to dump was stashed in the params. base::Value result("Saving...");
int pid = reinterpret_cast<intptr_t>(params); AllowJavascript();
ProfilingProcessHost::GetInstance()->RequestProcessDump(pid, path, CallJavascriptFunction("setSaveDumpMessage", result);
base::OnceClosure());
ProfilingProcessHost::GetInstance()->SaveTraceWithHeapDumpToFile(
path,
base::BindOnce(&MemoryInternalsDOMHandler::SaveTraceFinished,
weak_factory_.GetWeakPtr()),
false);
select_file_dialog_ = nullptr; select_file_dialog_ = nullptr;
} }
...@@ -318,6 +323,12 @@ void MemoryInternalsDOMHandler::FileSelectionCanceled(void* params) { ...@@ -318,6 +323,12 @@ void MemoryInternalsDOMHandler::FileSelectionCanceled(void* params) {
select_file_dialog_ = nullptr; select_file_dialog_ = nullptr;
} }
void MemoryInternalsDOMHandler::SaveTraceFinished(bool success) {
base::Value result(success ? "Save successful." : "Save failure.");
AllowJavascript();
CallJavascriptFunction("setSaveDumpMessage", result);
}
} // namespace } // namespace
MemoryInternalsUI::MemoryInternalsUI(content::WebUI* web_ui) MemoryInternalsUI::MemoryInternalsUI(content::WebUI* web_ui)
......
...@@ -41,12 +41,6 @@ interface ProfilingService { ...@@ -41,12 +41,6 @@ interface ProfilingService {
handle memlog_pipe_receiver, handle memlog_pipe_receiver,
ProcessType process_type); ProcessType process_type);
// Dumps the memory log of the process with the given |pid| into
// |output_file|. |metadata| is a dictionary that should be added to the trace
// under the "metadata" key.
DumpProcess(mojo.common.mojom.ProcessId pid, handle output_file,
mojo.common.mojom.DictionaryValue metadata) => (bool result);
// Dumps the memory log of all profiled processes into shared buffers. The // Dumps the memory log of all profiled processes into shared buffers. The
// contents of each shared buffer is a JSON string compatible with // contents of each shared buffer is a JSON string compatible with
// TRACE_EVENT* macros. Processes that fail to dump will be omitted from // TRACE_EVENT* macros. Processes that fail to dump will be omitted from
......
...@@ -90,55 +90,6 @@ const char* StringForAllocatorType(uint32_t type) { ...@@ -90,55 +90,6 @@ const char* StringForAllocatorType(uint32_t type) {
} }
} }
std::string ProcessNameFromProcessType(mojom::ProcessType process_type) {
switch (process_type) {
case mojom::ProcessType::BROWSER:
return "Browser";
case mojom::ProcessType::RENDERER:
return "Renderer";
case mojom::ProcessType::GPU:
return "Gpu";
case mojom::ProcessType::OTHER:
return "Other";
}
return "Unknown";
}
std::string ProcessMainThreadNameFromProcessType(
mojom::ProcessType process_type) {
switch (process_type) {
case mojom::ProcessType::BROWSER:
return "CrBrowserMain";
case mojom::ProcessType::RENDERER:
return "CrRendererMain";
case mojom::ProcessType::GPU:
return "CrGpuMain";
case mojom::ProcessType::OTHER:
return "CrOtherMain";
}
return "CrUnknownMain";
}
// Writes a dummy process name entry given a PID. When we have more information
// on a process it can be filled in here. But for now the tracing tools expect
// this entry since everything is associated with a PID.
void WriteProcessName(int pid, const ExportParams& params, std::ostream& out) {
out << "{ \"pid\":" << pid << ", \"ph\":\"M\", \"name\":\"process_name\", "
<< "\"args\":{\"name\":\""
<< ProcessNameFromProcessType(params.process_type) << "\"}},";
// Catapult needs a thread named "CrBrowserMain" to recognize Chrome browser.
out << "{ \"pid\":" << pid << ", \"ph\":\"M\", \"name\":\"thread_name\", "
<< "\"tid\": 1,"
<< "\"args\":{\"name\":\""
<< ProcessMainThreadNameFromProcessType(params.process_type) << "\"}},";
// At least, one event must be present on the thread to avoid being pruned.
out << "{ \"name\": \"MemlogTraceEvent\", \"cat\": \"memlog\", "
<< "\"ph\": \"B\", \"ts\": 1, \"pid\": " << pid << ", "
<< "\"tid\": 1, \"args\": {}}";
}
// Writes the top-level allocators section. This section is used by the tracing // Writes the top-level allocators section. This section is used by the tracing
// UI to show a small summary for each allocator. It's necessary as a // UI to show a small summary for each allocator. It's necessary as a
// placeholder to allow the stack-viewing UI to be shown. // placeholder to allow the stack-viewing UI to be shown.
...@@ -197,21 +148,6 @@ void WriteAllocatorsSummary(size_t total_size[], ...@@ -197,21 +148,6 @@ void WriteAllocatorsSummary(size_t total_size[],
out << "},\n"; out << "},\n";
} }
// Writes the dictionary keys to preceed a "dumps" trace argument.
void WriteDumpsHeader(int pid, std::ostream& out) {
out << "{ \"pid\":" << pid << ",";
out << "\"ph\":\"v\",";
out << "\"name\":\"periodic_interval\",";
out << "\"ts\": 1,";
out << "\"id\": \"1\",";
out << "\"args\":{";
out << "\"dumps\":";
}
void WriteDumpsFooter(std::ostream& out) {
out << "}}"; // args, event
}
// Writes the dictionary keys to preceed a "heaps_v2" trace argument inside a // Writes the dictionary keys to preceed a "heaps_v2" trace argument inside a
// "dumps". This is "v2" heap dump format. // "dumps". This is "v2" heap dump format.
void WriteHeapsV2Header(std::ostream& out) { void WriteHeapsV2Header(std::ostream& out) {
...@@ -433,29 +369,6 @@ void WriteAllocatorNodes(const UniqueAllocationMap& allocations, ...@@ -433,29 +369,6 @@ void WriteAllocatorNodes(const UniqueAllocationMap& allocations,
ExportParams::ExportParams() = default; ExportParams::ExportParams() = default;
ExportParams::~ExportParams() = default; ExportParams::~ExportParams() = default;
void ExportAllocationEventSetToJSON(
int pid,
const ExportParams& params,
std::unique_ptr<base::DictionaryValue> metadata_dict,
std::ostream& out) {
out << "{ \"traceEvents\": [";
WriteProcessName(pid, params, out);
out << ",\n";
WriteDumpsHeader(pid, out);
ExportMemoryMapsAndV2StackTraceToJSON(params, out);
WriteDumpsFooter(out);
out << "]";
// Append metadata.
if (metadata_dict) {
std::string metadata;
base::JSONWriter::Write(*metadata_dict, &metadata);
out << ",\"metadata\": " << metadata;
}
out << "}\n";
}
void ExportMemoryMapsAndV2StackTraceToJSON(const ExportParams& params, void ExportMemoryMapsAndV2StackTraceToJSON(const ExportParams& params,
std::ostream& out) { std::ostream& out) {
// Start dictionary. // Start dictionary.
......
...@@ -43,14 +43,6 @@ struct ExportParams { ...@@ -43,14 +43,6 @@ struct ExportParams {
bool is_argument_filtering_enabled = false; bool is_argument_filtering_enabled = false;
}; };
// Creates a JSON-encoded string that is similar in form to traces created by
// TracingControllerImpl. Metadata can be null.
void ExportAllocationEventSetToJSON(
int pid,
const ExportParams& params,
std::unique_ptr<base::DictionaryValue> metadata,
std::ostream& out);
// Creates a JSON string representing a JSON dictionary that contains memory // Creates a JSON string representing a JSON dictionary that contains memory
// maps and v2 format stack traces. // maps and v2 format stack traces.
void ExportMemoryMapsAndV2StackTraceToJSON(const ExportParams& params, void ExportMemoryMapsAndV2StackTraceToJSON(const ExportParams& params,
......
This diff is collapsed.
...@@ -36,17 +36,6 @@ MemlogConnectionManager::DumpArgs::DumpArgs(DumpArgs&& other) noexcept ...@@ -36,17 +36,6 @@ MemlogConnectionManager::DumpArgs::DumpArgs(DumpArgs&& other) noexcept
: backtrace_storage_lock(std::move(other.backtrace_storage_lock)) {} : backtrace_storage_lock(std::move(other.backtrace_storage_lock)) {}
MemlogConnectionManager::DumpArgs::~DumpArgs() = default; MemlogConnectionManager::DumpArgs::~DumpArgs() = default;
MemlogConnectionManager::DumpProcessArgs::DumpProcessArgs() = default;
MemlogConnectionManager::DumpProcessArgs::DumpProcessArgs(
DumpProcessArgs&& other) noexcept
: DumpArgs(std::move(other)),
pid(other.pid),
maps(std::move(other.maps)),
metadata(std::move(other.metadata)),
file(std::move(other.file)),
callback(std::move(other.callback)) {}
MemlogConnectionManager::DumpProcessArgs::~DumpProcessArgs() = default;
// Tracking information for DumpProcessForTracing(). This struct is // Tracking information for DumpProcessForTracing(). This struct is
// refcounted since there will be many background thread calls (one for each // refcounted since there will be many background thread calls (one for each
// AllocationTracker) and the callback is only issued when each has // AllocationTracker) and the callback is only issued when each has
...@@ -189,41 +178,6 @@ void MemlogConnectionManager::OnConnectionCompleteThunk( ...@@ -189,41 +178,6 @@ void MemlogConnectionManager::OnConnectionCompleteThunk(
connection_manager, pid)); connection_manager, pid));
} }
void MemlogConnectionManager::DumpProcess(DumpProcessArgs args) {
// Schedules the given callback to execute after the given process ID has
// been synchronized. If the process ID isn't found, the callback will be
// asynchronously run with "false" as the success parameter.
args.backtrace_storage_lock = BacktraceStorage::Lock(&backtrace_storage_);
base::AutoLock lock(connections_lock_);
auto task_runner = base::MessageLoop::current()->task_runner();
auto it = connections_.find(args.pid);
if (it == connections_.end()) {
DLOG(ERROR) << "No connections found for memory dump for pid:" << args.pid;
task_runner->PostTask(
FROM_HERE,
base::BindOnce(&MemlogConnectionManager::DoDumpProcess,
weak_factory_.GetWeakPtr(), std::move(args),
mojom::ProcessType::OTHER, false, AllocationCountMap(),
AllocationTracker::ContextMap()));
return;
}
int barrier_id = next_barrier_id_++;
// Register for callback before requesting the dump so we don't race for the
// signal. The callback will be issued on the allocation tracker thread so
// need to thunk back to the I/O thread.
Connection* connection = it->second.get();
auto callback = base::BindOnce(&MemlogConnectionManager::DoDumpProcess,
weak_factory_.GetWeakPtr(), std::move(args),
connection->process_type);
connection->tracker.SnapshotOnBarrier(barrier_id, std::move(task_runner),
std::move(callback));
connection->client->FlushMemlogPipe(barrier_id);
}
void MemlogConnectionManager::DumpProcessesForTracing( void MemlogConnectionManager::DumpProcessesForTracing(
mojom::ProfilingService::DumpProcessesForTracingCallback callback, mojom::ProfilingService::DumpProcessesForTracingCallback callback,
memory_instrumentation::mojom::GlobalMemoryDumpPtr dump) { memory_instrumentation::mojom::GlobalMemoryDumpPtr dump) {
...@@ -264,51 +218,6 @@ void MemlogConnectionManager::DumpProcessesForTracing( ...@@ -264,51 +218,6 @@ void MemlogConnectionManager::DumpProcessesForTracing(
} }
} }
void MemlogConnectionManager::DoDumpProcess(
DumpProcessArgs args,
mojom::ProcessType process_type,
bool success,
AllocationCountMap counts,
AllocationTracker::ContextMap context) {
if (!success) {
std::move(args.callback).Run(false);
return;
}
CHECK(args.backtrace_storage_lock.IsLocked());
std::ostringstream oss;
ExportParams params;
params.allocs = std::move(counts);
params.context_map = std::move(context);
params.maps = std::move(args.maps);
params.process_type = process_type;
params.min_size_threshold = kMinSizeThreshold;
params.min_count_threshold = kMinCountThreshold;
ExportAllocationEventSetToJSON(args.pid, params, std::move(args.metadata),
oss);
std::string reply = oss.str();
// Pass ownership of the underlying fd/HANDLE to zlib.
base::PlatformFile platform_file = args.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");
if (!gz_file) {
DLOG(ERROR) << "Cannot compress trace file";
std::move(args.callback).Run(false);
return;
}
size_t written_bytes = gzwrite(gz_file, reply.c_str(), reply.size());
gzclose(gz_file);
std::move(args.callback).Run(written_bytes == reply.size());
}
void MemlogConnectionManager::DoDumpOneProcessForTracing( void MemlogConnectionManager::DoDumpOneProcessForTracing(
scoped_refptr<DumpProcessesForTracingTracking> tracking, scoped_refptr<DumpProcessesForTracingTracking> tracking,
base::ProcessId pid, base::ProcessId pid,
......
...@@ -60,31 +60,9 @@ class MemlogConnectionManager { ...@@ -60,31 +60,9 @@ class MemlogConnectionManager {
DISALLOW_COPY_AND_ASSIGN(DumpArgs); DISALLOW_COPY_AND_ASSIGN(DumpArgs);
}; };
// Parameters to DumpProcess().
struct DumpProcessArgs : public DumpArgs {
DumpProcessArgs();
DumpProcessArgs(DumpProcessArgs&&) noexcept;
~DumpProcessArgs();
// Process ID to dump.
base::ProcessId pid;
// The memory map for the given process for the dumped process must be
// provided here since that is not tracked as part of the normal allocation
// process.
std::vector<memory_instrumentation::mojom::VmRegionPtr> maps;
std::unique_ptr<base::DictionaryValue> metadata;
// File to dump the output to.
base::File file;
mojom::ProfilingService::DumpProcessCallback callback;
};
// Dumping is asynchronous so will not be complete when this function // Dumping is asynchronous so will not be complete when this function
// returns. The dump is complete when the callback provided in the args is // returns. The dump is complete when the callback provided in the args is
// fired. // fired.
void DumpProcess(DumpProcessArgs args);
void DumpProcessesForTracing( void DumpProcessesForTracing(
mojom::ProfilingService::DumpProcessesForTracingCallback callback, mojom::ProfilingService::DumpProcessesForTracingCallback callback,
memory_instrumentation::mojom::GlobalMemoryDumpPtr dump); memory_instrumentation::mojom::GlobalMemoryDumpPtr dump);
...@@ -99,12 +77,6 @@ class MemlogConnectionManager { ...@@ -99,12 +77,6 @@ class MemlogConnectionManager {
struct Connection; struct Connection;
struct DumpProcessesForTracingTracking; struct DumpProcessesForTracingTracking;
// Actually does the dump assuming the given process has been synchronized.
void DoDumpProcess(DumpProcessArgs args,
mojom::ProcessType process_type,
bool success,
AllocationCountMap counts,
AllocationTracker::ContextMap context);
void DoDumpOneProcessForTracing( void DoDumpOneProcessForTracing(
scoped_refptr<DumpProcessesForTracingTracking> tracking, scoped_refptr<DumpProcessesForTracingTracking> tracking,
base::ProcessId pid, base::ProcessId pid,
......
...@@ -69,31 +69,6 @@ void ProfilingService::AddProfilingClient( ...@@ -69,31 +69,6 @@ void ProfilingService::AddProfilingClient(
std::move(memlog_pipe_receiver), process_type); std::move(memlog_pipe_receiver), process_type);
} }
void ProfilingService::DumpProcess(
base::ProcessId pid,
mojo::ScopedHandle output_file,
std::unique_ptr<base::DictionaryValue> metadata,
DumpProcessCallback callback) {
base::PlatformFile platform_file;
MojoResult result =
UnwrapPlatformFile(std::move(output_file), &platform_file);
if (result != MOJO_RESULT_OK) {
DLOG(ERROR) << "Failed to unwrap output file " << result;
std::move(callback).Run(false);
return;
}
base::File file(platform_file);
// Need a memory map to make sense of the dump. The dump will be triggered
// in the memory map global dump callback.
// TODO(brettw) this should be a OnceCallback to avoid base::Passed.
memory_instrumentation::MemoryInstrumentation::GetInstance()
->GetVmRegionsForHeapProfiler(
base::Bind(&ProfilingService::OnGetVmRegionsCompleteForDumpProcess,
weak_factory_.GetWeakPtr(), pid, base::Passed(&metadata),
base::Passed(&file), base::Passed(&callback)));
}
void ProfilingService::DumpProcessesForTracing( void ProfilingService::DumpProcessesForTracing(
DumpProcessesForTracingCallback callback) { DumpProcessesForTracingCallback callback) {
// Need a memory map to make sense of the dump. The dump will be triggered // Need a memory map to make sense of the dump. The dump will be triggered
...@@ -105,44 +80,6 @@ void ProfilingService::DumpProcessesForTracing( ...@@ -105,44 +80,6 @@ void ProfilingService::DumpProcessesForTracing(
weak_factory_.GetWeakPtr(), base::Passed(&callback))); weak_factory_.GetWeakPtr(), base::Passed(&callback)));
} }
void ProfilingService::OnGetVmRegionsCompleteForDumpProcess(
base::ProcessId pid,
std::unique_ptr<base::DictionaryValue> metadata,
base::File file,
DumpProcessCallback callback,
bool success,
memory_instrumentation::mojom::GlobalMemoryDumpPtr dump) {
if (!success) {
DLOG(ERROR) << "Global dump failed";
std::move(callback).Run(false);
return;
}
// Find the process's memory dump we want.
// TODO(bug 752621) we should be asking and getting the memory map of only
// the process we want rather than querying all processes and filtering.
memory_instrumentation::mojom::ProcessMemoryDump* process_dump = nullptr;
for (const auto& proc : dump->process_dumps) {
if (proc->pid == pid) {
process_dump = &*proc;
break;
}
}
if (!process_dump) {
DLOG(ERROR) << "Don't have a memory dump for PID " << pid;
std::move(callback).Run(false);
return;
}
MemlogConnectionManager::DumpProcessArgs args;
args.pid = pid;
args.metadata = std::move(metadata);
args.maps = std::move(process_dump->os_dump->memory_maps_for_heap_profiler);
args.file = std::move(file);
args.callback = std::move(callback);
connection_manager_.DumpProcess(std::move(args));
}
void ProfilingService::OnGetVmRegionsCompleteForDumpProcessesForTracing( void ProfilingService::OnGetVmRegionsCompleteForDumpProcessesForTracing(
mojom::ProfilingService::DumpProcessesForTracingCallback callback, mojom::ProfilingService::DumpProcessesForTracingCallback callback,
bool success, bool success,
......
...@@ -46,10 +46,6 @@ class ProfilingService : public service_manager::Service, ...@@ -46,10 +46,6 @@ class ProfilingService : public service_manager::Service,
mojo::ScopedHandle memlog_pipe_sender, mojo::ScopedHandle memlog_pipe_sender,
mojo::ScopedHandle memlog_pipe_receiver, mojo::ScopedHandle memlog_pipe_receiver,
mojom::ProcessType process_type) override; mojom::ProcessType process_type) override;
void DumpProcess(base::ProcessId pid,
mojo::ScopedHandle output_file,
std::unique_ptr<base::DictionaryValue> metadata,
DumpProcessCallback callback) override;
void DumpProcessesForTracing( void DumpProcessesForTracing(
DumpProcessesForTracingCallback callback) override; DumpProcessesForTracingCallback callback) override;
...@@ -63,13 +59,6 @@ class ProfilingService : public service_manager::Service, ...@@ -63,13 +59,6 @@ class ProfilingService : public service_manager::Service,
service_manager::ServiceContextRefFactory* ref_factory, service_manager::ServiceContextRefFactory* ref_factory,
mojom::ProfilingServiceRequest request); mojom::ProfilingServiceRequest request);
void OnGetVmRegionsCompleteForDumpProcess(
base::ProcessId pid,
std::unique_ptr<base::DictionaryValue> metadata,
base::File file,
DumpProcessCallback callback,
bool success,
memory_instrumentation::mojom::GlobalMemoryDumpPtr dump);
void OnGetVmRegionsCompleteForDumpProcessesForTracing( void OnGetVmRegionsCompleteForDumpProcessesForTracing(
mojom::ProfilingService::DumpProcessesForTracingCallback callback, mojom::ProfilingService::DumpProcessesForTracingCallback callback,
bool success, bool success,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment