electron_bindings.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. // Copyright (c) 2013 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "shell/common/api/electron_bindings.h"
  5. #include <algorithm>
  6. #include <string>
  7. #include <utility>
  8. #include <vector>
  9. #include "base/containers/contains.h"
  10. #include "base/logging.h"
  11. #include "base/process/process.h"
  12. #include "base/process/process_handle.h"
  13. #include "base/process/process_metrics_iocounters.h"
  14. #include "base/system/sys_info.h"
  15. #include "chrome/common/chrome_version.h"
  16. #include "electron/electron_version.h"
  17. #include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h"
  18. #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
  19. #include "shell/browser/browser.h"
  20. #include "shell/common/application_info.h"
  21. #include "shell/common/gin_converters/file_path_converter.h"
  22. #include "shell/common/gin_helper/dictionary.h"
  23. #include "shell/common/gin_helper/microtasks_scope.h"
  24. #include "shell/common/gin_helper/promise.h"
  25. #include "shell/common/heap_snapshot.h"
  26. #include "shell/common/node_includes.h"
  27. #include "shell/common/process_util.h"
  28. #include "shell/common/thread_restrictions.h"
  29. #include "third_party/blink/renderer/platform/heap/process_heap.h" // nogncheck
  30. namespace electron {
  31. ElectronBindings::ElectronBindings(uv_loop_t* loop) {
  32. uv_async_init(loop, call_next_tick_async_.get(), OnCallNextTick);
  33. call_next_tick_async_.get()->data = this;
  34. metrics_ = base::ProcessMetrics::CreateCurrentProcessMetrics();
  35. }
  36. ElectronBindings::~ElectronBindings() = default;
  37. // static
  38. void ElectronBindings::BindProcess(v8::Isolate* isolate,
  39. gin_helper::Dictionary* process,
  40. base::ProcessMetrics* metrics) {
  41. // These bindings are shared between sandboxed & unsandboxed renderers
  42. process->SetMethod("crash", &Crash);
  43. process->SetMethod("hang", &Hang);
  44. process->SetMethod("getCreationTime", &GetCreationTime);
  45. process->SetMethod("getHeapStatistics", &GetHeapStatistics);
  46. process->SetMethod("getBlinkMemoryInfo", &GetBlinkMemoryInfo);
  47. if (electron::IsBrowserProcess()) {
  48. process->SetMethod("getProcessMemoryInfo", &GetProcessMemoryInfo);
  49. }
  50. process->SetMethod("getSystemMemoryInfo", &GetSystemMemoryInfo);
  51. process->SetMethod("getSystemVersion",
  52. &base::SysInfo::OperatingSystemVersion);
  53. process->SetMethod("getIOCounters", &GetIOCounters);
  54. process->SetMethod("getCPUUsage",
  55. base::BindRepeating(&ElectronBindings::GetCPUUsage,
  56. base::Unretained(metrics)));
  57. #if IS_MAS_BUILD()
  58. process->SetReadOnly("mas", true);
  59. #endif
  60. #if BUILDFLAG(IS_WIN)
  61. if (IsRunningInDesktopBridge())
  62. process->SetReadOnly("windowsStore", true);
  63. #endif
  64. }
  65. void ElectronBindings::BindTo(v8::Isolate* isolate,
  66. v8::Local<v8::Object> process) {
  67. gin_helper::Dictionary dict(isolate, process);
  68. BindProcess(isolate, &dict, metrics_.get());
  69. dict.SetMethod("takeHeapSnapshot", &TakeHeapSnapshot);
  70. #if BUILDFLAG(IS_POSIX)
  71. dict.SetMethod("setFdLimit", &base::IncreaseFdLimitTo);
  72. #endif
  73. dict.SetMethod("activateUvLoop",
  74. base::BindRepeating(&ElectronBindings::ActivateUVLoop,
  75. base::Unretained(this)));
  76. gin_helper::Dictionary versions;
  77. if (dict.Get("versions", &versions)) {
  78. versions.SetReadOnly(ELECTRON_PROJECT_NAME, ELECTRON_VERSION_STRING);
  79. versions.SetReadOnly("chrome", CHROME_VERSION_STRING);
  80. }
  81. }
  82. void ElectronBindings::EnvironmentDestroyed(node::Environment* env) {
  83. auto it =
  84. std::find(pending_next_ticks_.begin(), pending_next_ticks_.end(), env);
  85. if (it != pending_next_ticks_.end())
  86. pending_next_ticks_.erase(it);
  87. }
  88. void ElectronBindings::ActivateUVLoop(v8::Isolate* isolate) {
  89. node::Environment* env = node::Environment::GetCurrent(isolate);
  90. if (base::Contains(pending_next_ticks_, env))
  91. return;
  92. pending_next_ticks_.push_back(env);
  93. uv_async_send(call_next_tick_async_.get());
  94. }
  95. // static
  96. void ElectronBindings::OnCallNextTick(uv_async_t* handle) {
  97. auto* self = static_cast<ElectronBindings*>(handle->data);
  98. for (auto* env : self->pending_next_ticks_) {
  99. gin_helper::Locker locker(env->isolate());
  100. v8::Context::Scope context_scope(env->context());
  101. v8::HandleScope handle_scope(env->isolate());
  102. node::CallbackScope scope(env->isolate(), v8::Object::New(env->isolate()),
  103. {0, 0});
  104. }
  105. self->pending_next_ticks_.clear();
  106. }
  107. // static
  108. void ElectronBindings::Crash() {
  109. volatile int* zero = nullptr;
  110. *zero = 0;
  111. }
  112. // static
  113. void ElectronBindings::Hang() {
  114. for (;;)
  115. base::PlatformThread::Sleep(base::Seconds(1));
  116. }
  117. // static
  118. v8::Local<v8::Value> ElectronBindings::GetHeapStatistics(v8::Isolate* isolate) {
  119. v8::HeapStatistics v8_heap_stats;
  120. isolate->GetHeapStatistics(&v8_heap_stats);
  121. auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
  122. dict.Set("totalHeapSize",
  123. static_cast<double>(v8_heap_stats.total_heap_size() >> 10));
  124. dict.Set(
  125. "totalHeapSizeExecutable",
  126. static_cast<double>(v8_heap_stats.total_heap_size_executable() >> 10));
  127. dict.Set("totalPhysicalSize",
  128. static_cast<double>(v8_heap_stats.total_physical_size() >> 10));
  129. dict.Set("totalAvailableSize",
  130. static_cast<double>(v8_heap_stats.total_available_size() >> 10));
  131. dict.Set("usedHeapSize",
  132. static_cast<double>(v8_heap_stats.used_heap_size() >> 10));
  133. dict.Set("heapSizeLimit",
  134. static_cast<double>(v8_heap_stats.heap_size_limit() >> 10));
  135. dict.Set("mallocedMemory",
  136. static_cast<double>(v8_heap_stats.malloced_memory() >> 10));
  137. dict.Set("peakMallocedMemory",
  138. static_cast<double>(v8_heap_stats.peak_malloced_memory() >> 10));
  139. dict.Set("doesZapGarbage",
  140. static_cast<bool>(v8_heap_stats.does_zap_garbage()));
  141. return dict.GetHandle();
  142. }
  143. // static
  144. v8::Local<v8::Value> ElectronBindings::GetCreationTime(v8::Isolate* isolate) {
  145. auto timeValue = base::Process::Current().CreationTime();
  146. if (timeValue.is_null()) {
  147. return v8::Null(isolate);
  148. }
  149. double jsTime = timeValue.ToJsTime();
  150. return v8::Number::New(isolate, jsTime);
  151. }
  152. // static
  153. v8::Local<v8::Value> ElectronBindings::GetSystemMemoryInfo(
  154. v8::Isolate* isolate,
  155. gin_helper::Arguments* args) {
  156. base::SystemMemoryInfoKB mem_info;
  157. if (!base::GetSystemMemoryInfo(&mem_info)) {
  158. args->ThrowError("Unable to retrieve system memory information");
  159. return v8::Undefined(isolate);
  160. }
  161. auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
  162. dict.Set("total", mem_info.total);
  163. // See Chromium's "base/process/process_metrics.h" for an explanation.
  164. int free =
  165. #if BUILDFLAG(IS_WIN)
  166. mem_info.avail_phys;
  167. #else
  168. mem_info.free;
  169. #endif
  170. dict.Set("free", free);
  171. // NB: These return bogus values on macOS
  172. #if !BUILDFLAG(IS_MAC)
  173. dict.Set("swapTotal", mem_info.swap_total);
  174. dict.Set("swapFree", mem_info.swap_free);
  175. #endif
  176. return dict.GetHandle();
  177. }
  178. // static
  179. v8::Local<v8::Promise> ElectronBindings::GetProcessMemoryInfo(
  180. v8::Isolate* isolate) {
  181. CHECK(electron::IsBrowserProcess());
  182. gin_helper::Promise<gin_helper::Dictionary> promise(isolate);
  183. v8::Local<v8::Promise> handle = promise.GetHandle();
  184. if (!Browser::Get()->is_ready()) {
  185. promise.RejectWithErrorMessage(
  186. "Memory Info is available only after app ready");
  187. return handle;
  188. }
  189. v8::Global<v8::Context> context(isolate, isolate->GetCurrentContext());
  190. memory_instrumentation::MemoryInstrumentation::GetInstance()
  191. ->RequestGlobalDumpForPid(
  192. base::GetCurrentProcId(), std::vector<std::string>(),
  193. base::BindOnce(&ElectronBindings::DidReceiveMemoryDump,
  194. std::move(context), std::move(promise),
  195. base::GetCurrentProcId()));
  196. return handle;
  197. }
  198. // static
  199. v8::Local<v8::Value> ElectronBindings::GetBlinkMemoryInfo(
  200. v8::Isolate* isolate) {
  201. auto allocated = blink::ProcessHeap::TotalAllocatedObjectSize();
  202. auto total = blink::ProcessHeap::TotalAllocatedSpace();
  203. auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
  204. dict.Set("allocated", static_cast<double>(allocated >> 10));
  205. dict.Set("total", static_cast<double>(total >> 10));
  206. return dict.GetHandle();
  207. }
  208. // static
  209. void ElectronBindings::DidReceiveMemoryDump(
  210. v8::Global<v8::Context> context,
  211. gin_helper::Promise<gin_helper::Dictionary> promise,
  212. base::ProcessId target_pid,
  213. bool success,
  214. std::unique_ptr<memory_instrumentation::GlobalMemoryDump> global_dump) {
  215. v8::Isolate* isolate = promise.isolate();
  216. v8::HandleScope handle_scope(isolate);
  217. v8::Local<v8::Context> local_context =
  218. v8::Local<v8::Context>::New(isolate, context);
  219. gin_helper::MicrotasksScope microtasks_scope(
  220. isolate, local_context->GetMicrotaskQueue(), true);
  221. v8::Context::Scope context_scope(local_context);
  222. if (!success) {
  223. promise.RejectWithErrorMessage("Failed to create memory dump");
  224. return;
  225. }
  226. bool resolved = false;
  227. for (const memory_instrumentation::GlobalMemoryDump::ProcessDump& dump :
  228. global_dump->process_dumps()) {
  229. if (target_pid == dump.pid()) {
  230. auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
  231. const auto& osdump = dump.os_dump();
  232. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
  233. dict.Set("residentSet", osdump.resident_set_kb);
  234. #endif
  235. dict.Set("private", osdump.private_footprint_kb);
  236. dict.Set("shared", osdump.shared_footprint_kb);
  237. promise.Resolve(dict);
  238. resolved = true;
  239. break;
  240. }
  241. }
  242. if (!resolved) {
  243. promise.RejectWithErrorMessage(
  244. R"(Failed to find current process memory details in memory dump)");
  245. }
  246. }
  247. // static
  248. v8::Local<v8::Value> ElectronBindings::GetCPUUsage(
  249. base::ProcessMetrics* metrics,
  250. v8::Isolate* isolate) {
  251. auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
  252. int processor_count = base::SysInfo::NumberOfProcessors();
  253. dict.Set("percentCPUUsage",
  254. metrics->GetPlatformIndependentCPUUsage() / processor_count);
  255. // NB: This will throw NOTIMPLEMENTED() on Windows
  256. // For backwards compatibility, we'll return 0
  257. #if !BUILDFLAG(IS_WIN)
  258. dict.Set("idleWakeupsPerSecond", metrics->GetIdleWakeupsPerSecond());
  259. #else
  260. dict.Set("idleWakeupsPerSecond", 0);
  261. #endif
  262. return dict.GetHandle();
  263. }
  264. // static
  265. v8::Local<v8::Value> ElectronBindings::GetIOCounters(v8::Isolate* isolate) {
  266. auto metrics = base::ProcessMetrics::CreateCurrentProcessMetrics();
  267. base::IoCounters io_counters;
  268. auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
  269. if (metrics->GetIOCounters(&io_counters)) {
  270. dict.Set("readOperationCount", io_counters.ReadOperationCount);
  271. dict.Set("writeOperationCount", io_counters.WriteOperationCount);
  272. dict.Set("otherOperationCount", io_counters.OtherOperationCount);
  273. dict.Set("readTransferCount", io_counters.ReadTransferCount);
  274. dict.Set("writeTransferCount", io_counters.WriteTransferCount);
  275. dict.Set("otherTransferCount", io_counters.OtherTransferCount);
  276. }
  277. return dict.GetHandle();
  278. }
  279. // static
  280. bool ElectronBindings::TakeHeapSnapshot(v8::Isolate* isolate,
  281. const base::FilePath& file_path) {
  282. ScopedAllowBlockingForElectron allow_blocking;
  283. base::File file(file_path,
  284. base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
  285. return electron::TakeHeapSnapshot(isolate, &file);
  286. }
  287. } // namespace electron