From 58ec892398147db78be4e29e3104fa2f48499fde Mon Sep 17 00:00:00 2001 From: Milan Burda Date: Tue, 4 Sep 2018 23:46:10 +0200 Subject: [PATCH] feat: add process.getHeapSnapshot() --- atom/browser/api/atom_api_web_contents.cc | 23 +++++++++++ atom/browser/api/atom_api_web_contents.h | 3 ++ atom/common/api/api_messages.h | 5 +++ atom/common/api/atom_bindings.cc | 41 +++++++++++++++++++ atom/common/api/atom_bindings.h | 2 + atom/common/api/file_output_stream.cc | 29 +++++++++++++ atom/common/api/file_output_stream.h | 32 +++++++++++++++ .../atom_sandboxed_renderer_client.cc | 2 + filenames.gypi | 2 + lib/sandboxed_renderer/init.js | 1 + 10 files changed, 140 insertions(+) create mode 100644 atom/common/api/file_output_stream.cc create mode 100644 atom/common/api/file_output_stream.h diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index ebf57235333a0..4bbe4aa84bc4c 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -308,6 +308,10 @@ struct WebContents::FrameDispatchHelper { IPC::Message* message) { api_web_contents->OnRendererMessageSync(rfh, channel, args, message); } + + void OnCreateTemporaryFile(IPC::Message* message) { + api_web_contents->OnCreateTemporaryFile(rfh, message); + } }; WebContents::WebContents(v8::Isolate* isolate, @@ -1032,6 +1036,9 @@ bool WebContents::OnMessageReceived(const IPC::Message& message, IPC_MESSAGE_HANDLER(AtomAutofillFrameHostMsg_ShowPopup, ShowAutofillPopup) IPC_MESSAGE_HANDLER(AtomAutofillFrameHostMsg_HidePopup, HideAutofillPopup) #endif + IPC_MESSAGE_FORWARD_DELAY_REPLY(AtomFrameHostMsg_CreateTemporaryFile, + &helper, + FrameDispatchHelper::OnCreateTemporaryFile) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -2101,6 +2108,22 @@ void WebContents::OnRendererMessageTo(content::RenderFrameHost* frame_host, } } +void WebContents::OnCreateTemporaryFile(content::RenderFrameHost* frame_host, + IPC::Message* message) { + base::ScopedAllowBlockingForTesting allow_blocking; + + base::FilePath file_path; + base::CreateTemporaryFile(&file_path); + + base::File file(file_path, + base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); + + AtomFrameHostMsg_CreateTemporaryFile::WriteReplyParams( + message, file_path, IPC::TakePlatformFileForTransit(std::move(file))); + + frame_host->Send(message); +} + // static mate::Handle WebContents::CreateFrom( v8::Isolate* isolate, diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index ec5a4a756ebdb..e55f25b9a170e 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -431,6 +431,9 @@ class WebContents : public mate::TrackableObject, const std::string& channel, const base::ListValue& args); + void OnCreateTemporaryFile(content::RenderFrameHost* frame_host, + IPC::Message* message); + // Called when received a synchronous message from renderer to // set temporary zoom level. void OnSetTemporaryZoomLevel(content::RenderFrameHost* frame_host, diff --git a/atom/common/api/api_messages.h b/atom/common/api/api_messages.h index e7317736e0766..9ec00b25b3ab1 100644 --- a/atom/common/api/api_messages.h +++ b/atom/common/api/api_messages.h @@ -10,6 +10,7 @@ #include "content/public/common/common_param_traits.h" #include "content/public/common/referrer.h" #include "ipc/ipc_message_macros.h" +#include "ipc/ipc_platform_file.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/ipc/gfx_param_traits.h" #include "url/gurl.h" @@ -76,3 +77,7 @@ IPC_SYNC_MESSAGE_ROUTED0_1(AtomFrameHostMsg_GetZoomLevel, double /* result */) IPC_MESSAGE_ROUTED2(AtomFrameHostMsg_PDFSaveURLAs, GURL /* url */, content::Referrer /* referrer */) + +IPC_SYNC_MESSAGE_ROUTED0_2(AtomFrameHostMsg_CreateTemporaryFile, + base::FilePath /* file_path */, + IPC::PlatformFileForTransit /* file_handle */) diff --git a/atom/common/api/atom_bindings.cc b/atom/common/api/atom_bindings.cc index 3dba3d63673ca..3cb7814f8c8fd 100644 --- a/atom/common/api/atom_bindings.cc +++ b/atom/common/api/atom_bindings.cc @@ -8,16 +8,22 @@ #include #include +#include "atom/common/api/api_messages.h" +#include "atom/common/api/file_output_stream.h" #include "atom/common/api/locker.h" #include "atom/common/atom_version.h" #include "atom/common/chrome_version.h" +#include "atom/common/native_mate_converters/file_path_converter.h" #include "atom/common/native_mate_converters/string16_converter.h" #include "atom/common/node_includes.h" #include "base/logging.h" #include "base/process/process_info.h" #include "base/process/process_metrics_iocounters.h" #include "base/sys_info.h" +#include "content/public/renderer/render_frame.h" #include "native_mate/dictionary.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" +#include "v8/include/v8-profiler.h" namespace atom { @@ -54,6 +60,7 @@ void AtomBindings::BindTo(v8::Isolate* isolate, v8::Local process) { dict.SetMethod("crash", &AtomBindings::Crash); dict.SetMethod("hang", &Hang); dict.SetMethod("log", &Log); + dict.SetMethod("getHeapSnapshot", &GetHeapSnapshot); dict.SetMethod("getHeapStatistics", &GetHeapStatistics); dict.SetMethod("getProcessMemoryInfo", &GetProcessMemoryInfo); dict.SetMethod("getCreationTime", &GetCreationTime); @@ -129,6 +136,40 @@ void AtomBindings::Hang() { base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); } +// static +base::FilePath AtomBindings::GetHeapSnapshot(v8::Isolate* isolate) { + auto* frame = blink::WebLocalFrame::FrameForCurrentContext(); + if (!frame) + return base::FilePath(); + + auto* render_frame = content::RenderFrame::FromWebFrame(frame); + if (!render_frame) + return base::FilePath(); + + base::FilePath file_path; + IPC::PlatformFileForTransit file_handle; + auto* message = new AtomFrameHostMsg_CreateTemporaryFile( + render_frame->GetRoutingID(), &file_path, &file_handle); + if (!render_frame->Send(message)) + return base::FilePath(); + + auto file = IPC::PlatformFileForTransitToFile(file_handle); + if (!file.IsValid()) + return base::FilePath(); + + auto* snap = isolate->GetHeapProfiler()->TakeHeapSnapshot(); + + FileOutputStream stream(&file); + snap->Serialize(&stream, v8::HeapSnapshot::kJSON); + + const_cast(snap)->Delete(); + + if (!stream.IsComplete()) + return base::FilePath(); + + return file_path; +} + // static v8::Local AtomBindings::GetHeapStatistics(v8::Isolate* isolate) { v8::HeapStatistics v8_heap_stats; diff --git a/atom/common/api/atom_bindings.h b/atom/common/api/atom_bindings.h index ba8385c979b15..0af90fc270a61 100644 --- a/atom/common/api/atom_bindings.h +++ b/atom/common/api/atom_bindings.h @@ -7,6 +7,7 @@ #include +#include "base/files/file_path.h" #include "base/macros.h" #include "base/process/process_metrics.h" #include "base/strings/string16.h" @@ -35,6 +36,7 @@ class AtomBindings { static void Log(const base::string16& message); static void Crash(); static void Hang(); + static base::FilePath GetHeapSnapshot(v8::Isolate* isolate); static v8::Local GetHeapStatistics(v8::Isolate* isolate); static v8::Local GetProcessMemoryInfo(v8::Isolate* isolate); static v8::Local GetCreationTime(v8::Isolate* isolate); diff --git a/atom/common/api/file_output_stream.cc b/atom/common/api/file_output_stream.cc new file mode 100644 index 0000000000000..e32de2af0c261 --- /dev/null +++ b/atom/common/api/file_output_stream.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2018 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/common/api/file_output_stream.h" + +namespace atom { + +FileOutputStream::FileOutputStream(base::File* file) : file_(file) {} + +bool FileOutputStream::IsComplete() const { + return is_complete_; +} + +int FileOutputStream::GetChunkSize() { + return 65536; +} + +void FileOutputStream::EndOfStream() { + is_complete_ = true; +} + +v8::OutputStream::WriteResult FileOutputStream::WriteAsciiChunk(char* data, + int size) { + auto bytes_written = file_->WriteAtCurrentPos(data, size); + return bytes_written == size ? kContinue : kAbort; +} + +} // namespace atom diff --git a/atom/common/api/file_output_stream.h b/atom/common/api/file_output_stream.h new file mode 100644 index 0000000000000..259c8bcac8d1b --- /dev/null +++ b/atom/common/api/file_output_stream.h @@ -0,0 +1,32 @@ +// Copyright (c) 2018 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_COMMON_API_FILE_OUTPUT_STREAM_H_ +#define ATOM_COMMON_API_FILE_OUTPUT_STREAM_H_ + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "v8/include/v8-profiler.h" + +namespace atom { + +class FileOutputStream : public v8::OutputStream { + public: + explicit FileOutputStream(base::File* file); + + bool IsComplete() const; + + // v8::OutputStream + virtual int GetChunkSize(); + virtual void EndOfStream(); + virtual v8::OutputStream::WriteResult WriteAsciiChunk(char* data, int size); + + private: + base::File* file_ = nullptr; + bool is_complete_ = false; +}; + +} // namespace atom + +#endif // ATOM_COMMON_API_FILE_OUTPUT_STREAM_H_ diff --git a/atom/renderer/atom_sandboxed_renderer_client.cc b/atom/renderer/atom_sandboxed_renderer_client.cc index f820b88cf28fe..2320472e5b0a3 100644 --- a/atom/renderer/atom_sandboxed_renderer_client.cc +++ b/atom/renderer/atom_sandboxed_renderer_client.cc @@ -6,6 +6,7 @@ #include "atom/common/api/api_messages.h" #include "atom/common/api/atom_bindings.h" +#include "atom/common/native_mate_converters/file_path_converter.h" #include "atom/common/native_mate_converters/string16_converter.h" #include "atom/common/native_mate_converters/value_converter.h" #include "atom/common/node_bindings.h" @@ -152,6 +153,7 @@ void AtomSandboxedRendererClient::InitializeBindings( b.SetMethod("getExecPath", GetExecPath); b.SetMethod("getPid", &base::GetCurrentProcId); b.SetMethod("getResourcesPath", &NodeBindings::GetHelperResourcesPath); + b.SetMethod("getHeapSnapshot", &AtomBindings::GetHeapSnapshot); b.SetMethod("getHeapStatistics", &AtomBindings::GetHeapStatistics); b.SetMethod("getProcessMemoryInfo", &AtomBindings::GetProcessMemoryInfo); b.SetMethod("getSystemMemoryInfo", &AtomBindings::GetSystemMemoryInfo); diff --git a/filenames.gypi b/filenames.gypi index a7563a699c459..184b3ebd541c9 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -449,6 +449,8 @@ 'atom/common/api/event_emitter_caller.cc', 'atom/common/api/event_emitter_caller.h', 'atom/common/api/features.cc', + 'atom/common/api/file_output_stream.cc', + 'atom/common/api/file_output_stream.h', 'atom/common/api/locker.cc', 'atom/common/api/locker.h', 'atom/common/api/object_life_monitor.cc', diff --git a/lib/sandboxed_renderer/init.js b/lib/sandboxed_renderer/init.js index afb505490e659..a8ea3e03da933 100644 --- a/lib/sandboxed_renderer/init.js +++ b/lib/sandboxed_renderer/init.js @@ -48,6 +48,7 @@ require('../renderer/web-frame-init')() const preloadProcess = new events.EventEmitter() preloadProcess.crash = () => binding.crash() preloadProcess.hang = () => binding.hang() +preloadProcess.getHeapSnapshot = () => binding.getHeapSnapshot() preloadProcess.getHeapStatistics = () => binding.getHeapStatistics() preloadProcess.getProcessMemoryInfo = () => binding.getProcessMemoryInfo() preloadProcess.getSystemMemoryInfo = () => binding.getSystemMemoryInfo()