atom_bindings.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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 "atom/common/api/atom_bindings.h"
  5. #include <algorithm>
  6. #include <iostream>
  7. #include <string>
  8. #include <utility>
  9. #include <vector>
  10. #include "atom/browser/browser.h"
  11. #include "atom/common/api/locker.h"
  12. #include "atom/common/application_info.h"
  13. #include "atom/common/atom_version.h"
  14. #include "atom/common/heap_snapshot.h"
  15. #include "atom/common/native_mate_converters/file_path_converter.h"
  16. #include "atom/common/native_mate_converters/string16_converter.h"
  17. #include "base/logging.h"
  18. #include "base/process/process.h"
  19. #include "base/process/process_handle.h"
  20. #include "base/process/process_metrics_iocounters.h"
  21. #include "base/system/sys_info.h"
  22. #include "base/threading/thread_restrictions.h"
  23. #include "chrome/common/chrome_version.h"
  24. #include "native_mate/dictionary.h"
  25. #include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h"
  26. #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
  27. // Must be the last in the includes list, otherwise the definition of chromium
  28. // macros conflicts with node macros.
  29. #include "atom/common/node_includes.h"
  30. namespace atom {
  31. namespace {
  32. // Dummy class type that used for crashing the program.
  33. struct DummyClass {
  34. bool crash;
  35. };
  36. // Called when there is a fatal error in V8, we just crash the process here so
  37. // we can get the stack trace.
  38. void FatalErrorCallback(const char* location, const char* message) {
  39. LOG(ERROR) << "Fatal error in V8: " << location << " " << message;
  40. AtomBindings::Crash();
  41. }
  42. } // namespace
  43. AtomBindings::AtomBindings(uv_loop_t* loop) {
  44. uv_async_init(loop, &call_next_tick_async_, OnCallNextTick);
  45. call_next_tick_async_.data = this;
  46. metrics_ = base::ProcessMetrics::CreateCurrentProcessMetrics();
  47. }
  48. AtomBindings::~AtomBindings() {
  49. uv_close(reinterpret_cast<uv_handle_t*>(&call_next_tick_async_), nullptr);
  50. }
  51. // static
  52. void AtomBindings::BindProcess(v8::Isolate* isolate,
  53. mate::Dictionary* process,
  54. base::ProcessMetrics* metrics) {
  55. // These bindings are shared between sandboxed & unsandboxed renderers
  56. process->SetMethod("crash", &Crash);
  57. process->SetMethod("hang", &Hang);
  58. process->SetMethod("log", &Log);
  59. process->SetMethod("getCreationTime", &GetCreationTime);
  60. process->SetMethod("getHeapStatistics", &GetHeapStatistics);
  61. process->SetMethod("getProcessMemoryInfo", &GetProcessMemoryInfo);
  62. process->SetMethod("getSystemMemoryInfo", &GetSystemMemoryInfo);
  63. process->SetMethod("getIOCounters", &GetIOCounters);
  64. process->SetMethod("getCPUUsage", base::Bind(&AtomBindings::GetCPUUsage,
  65. base::Unretained(metrics)));
  66. #if defined(MAS_BUILD)
  67. process->SetReadOnly("mas", true);
  68. #endif
  69. #if defined(OS_WIN)
  70. if (IsRunningInDesktopBridge())
  71. process->SetReadOnly("windowsStore", true);
  72. #endif
  73. }
  74. void AtomBindings::BindTo(v8::Isolate* isolate, v8::Local<v8::Object> process) {
  75. isolate->SetFatalErrorHandler(FatalErrorCallback);
  76. mate::Dictionary dict(isolate, process);
  77. BindProcess(isolate, &dict, metrics_.get());
  78. dict.SetMethod("takeHeapSnapshot", &TakeHeapSnapshot);
  79. #if defined(OS_POSIX)
  80. dict.SetMethod("setFdLimit", &base::IncreaseFdLimitTo);
  81. #endif
  82. dict.SetMethod("activateUvLoop", base::Bind(&AtomBindings::ActivateUVLoop,
  83. base::Unretained(this)));
  84. mate::Dictionary versions;
  85. if (dict.Get("versions", &versions)) {
  86. versions.SetReadOnly(ATOM_PROJECT_NAME, ATOM_VERSION_STRING);
  87. versions.SetReadOnly("chrome", CHROME_VERSION_STRING);
  88. }
  89. }
  90. void AtomBindings::EnvironmentDestroyed(node::Environment* env) {
  91. auto it =
  92. std::find(pending_next_ticks_.begin(), pending_next_ticks_.end(), env);
  93. if (it != pending_next_ticks_.end())
  94. pending_next_ticks_.erase(it);
  95. }
  96. void AtomBindings::ActivateUVLoop(v8::Isolate* isolate) {
  97. node::Environment* env = node::Environment::GetCurrent(isolate);
  98. if (std::find(pending_next_ticks_.begin(), pending_next_ticks_.end(), env) !=
  99. pending_next_ticks_.end())
  100. return;
  101. pending_next_ticks_.push_back(env);
  102. uv_async_send(&call_next_tick_async_);
  103. }
  104. // static
  105. void AtomBindings::OnCallNextTick(uv_async_t* handle) {
  106. AtomBindings* self = static_cast<AtomBindings*>(handle->data);
  107. for (std::list<node::Environment*>::const_iterator it =
  108. self->pending_next_ticks_.begin();
  109. it != self->pending_next_ticks_.end(); ++it) {
  110. node::Environment* env = *it;
  111. mate::Locker locker(env->isolate());
  112. v8::Context::Scope context_scope(env->context());
  113. node::InternalCallbackScope scope(
  114. env, v8::Local<v8::Object>(), {0, 0},
  115. node::InternalCallbackScope::kAllowEmptyResource);
  116. }
  117. self->pending_next_ticks_.clear();
  118. }
  119. // static
  120. void AtomBindings::Log(const base::string16& message) {
  121. std::cout << message << std::flush;
  122. }
  123. // static
  124. void AtomBindings::Crash() {
  125. static_cast<DummyClass*>(nullptr)->crash = true;
  126. }
  127. // static
  128. void AtomBindings::Hang() {
  129. for (;;)
  130. base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
  131. }
  132. // static
  133. v8::Local<v8::Value> AtomBindings::GetHeapStatistics(v8::Isolate* isolate) {
  134. v8::HeapStatistics v8_heap_stats;
  135. isolate->GetHeapStatistics(&v8_heap_stats);
  136. mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
  137. dict.SetHidden("simple", true);
  138. dict.Set("totalHeapSize",
  139. static_cast<double>(v8_heap_stats.total_heap_size() >> 10));
  140. dict.Set(
  141. "totalHeapSizeExecutable",
  142. static_cast<double>(v8_heap_stats.total_heap_size_executable() >> 10));
  143. dict.Set("totalPhysicalSize",
  144. static_cast<double>(v8_heap_stats.total_physical_size() >> 10));
  145. dict.Set("totalAvailableSize",
  146. static_cast<double>(v8_heap_stats.total_available_size() >> 10));
  147. dict.Set("usedHeapSize",
  148. static_cast<double>(v8_heap_stats.used_heap_size() >> 10));
  149. dict.Set("heapSizeLimit",
  150. static_cast<double>(v8_heap_stats.heap_size_limit() >> 10));
  151. dict.Set("mallocedMemory",
  152. static_cast<double>(v8_heap_stats.malloced_memory() >> 10));
  153. dict.Set("peakMallocedMemory",
  154. static_cast<double>(v8_heap_stats.peak_malloced_memory() >> 10));
  155. dict.Set("doesZapGarbage",
  156. static_cast<bool>(v8_heap_stats.does_zap_garbage()));
  157. return dict.GetHandle();
  158. }
  159. // static
  160. v8::Local<v8::Value> AtomBindings::GetCreationTime(v8::Isolate* isolate) {
  161. auto timeValue = base::Process::Current().CreationTime();
  162. if (timeValue.is_null()) {
  163. return v8::Null(isolate);
  164. }
  165. double jsTime = timeValue.ToJsTime();
  166. return v8::Number::New(isolate, jsTime);
  167. }
  168. // static
  169. v8::Local<v8::Value> AtomBindings::GetSystemMemoryInfo(v8::Isolate* isolate,
  170. mate::Arguments* args) {
  171. base::SystemMemoryInfoKB mem_info;
  172. if (!base::GetSystemMemoryInfo(&mem_info)) {
  173. args->ThrowError("Unable to retrieve system memory information");
  174. return v8::Undefined(isolate);
  175. }
  176. mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
  177. dict.SetHidden("simple", true);
  178. dict.Set("total", mem_info.total);
  179. // See Chromium's "base/process/process_metrics.h" for an explanation.
  180. int free =
  181. #if defined(OS_WIN)
  182. mem_info.avail_phys;
  183. #else
  184. mem_info.free;
  185. #endif
  186. dict.Set("free", free);
  187. // NB: These return bogus values on macOS
  188. #if !defined(OS_MACOSX)
  189. dict.Set("swapTotal", mem_info.swap_total);
  190. dict.Set("swapFree", mem_info.swap_free);
  191. #endif
  192. return dict.GetHandle();
  193. }
  194. // static
  195. v8::Local<v8::Promise> AtomBindings::GetProcessMemoryInfo(
  196. v8::Isolate* isolate) {
  197. util::Promise promise(isolate);
  198. v8::Local<v8::Promise> handle = promise.GetHandle();
  199. if (mate::Locker::IsBrowserProcess() && !Browser::Get()->is_ready()) {
  200. promise.RejectWithErrorMessage(
  201. "Memory Info is available only after app ready");
  202. return handle;
  203. }
  204. v8::Global<v8::Context> context(isolate, isolate->GetCurrentContext());
  205. memory_instrumentation::MemoryInstrumentation::GetInstance()
  206. ->RequestGlobalDumpForPid(
  207. base::GetCurrentProcId(), std::vector<std::string>(),
  208. base::BindOnce(&AtomBindings::DidReceiveMemoryDump,
  209. std::move(context), std::move(promise)));
  210. return handle;
  211. }
  212. // static
  213. void AtomBindings::DidReceiveMemoryDump(
  214. v8::Global<v8::Context> context,
  215. util::Promise promise,
  216. bool success,
  217. std::unique_ptr<memory_instrumentation::GlobalMemoryDump> global_dump) {
  218. v8::Isolate* isolate = promise.isolate();
  219. mate::Locker locker(isolate);
  220. v8::HandleScope handle_scope(isolate);
  221. v8::MicrotasksScope script_scope(isolate,
  222. v8::MicrotasksScope::kRunMicrotasks);
  223. v8::Context::Scope context_scope(
  224. v8::Local<v8::Context>::New(isolate, context));
  225. if (!success) {
  226. promise.RejectWithErrorMessage("Failed to create memory dump");
  227. return;
  228. }
  229. bool resolved = false;
  230. for (const memory_instrumentation::GlobalMemoryDump::ProcessDump& dump :
  231. global_dump->process_dumps()) {
  232. if (base::GetCurrentProcId() == dump.pid()) {
  233. mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
  234. const auto& osdump = dump.os_dump();
  235. #if defined(OS_LINUX) || defined(OS_WIN)
  236. dict.Set("residentSet", osdump.resident_set_kb);
  237. #endif
  238. dict.Set("private", osdump.private_footprint_kb);
  239. dict.Set("shared", osdump.shared_footprint_kb);
  240. promise.Resolve(dict.GetHandle());
  241. resolved = true;
  242. break;
  243. }
  244. }
  245. if (!resolved) {
  246. promise.RejectWithErrorMessage(
  247. R"(Failed to find current process memory details in memory dump)");
  248. }
  249. }
  250. // static
  251. v8::Local<v8::Value> AtomBindings::GetCPUUsage(base::ProcessMetrics* metrics,
  252. v8::Isolate* isolate) {
  253. mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
  254. dict.SetHidden("simple", true);
  255. int processor_count = base::SysInfo::NumberOfProcessors();
  256. dict.Set("percentCPUUsage",
  257. metrics->GetPlatformIndependentCPUUsage() / processor_count);
  258. // NB: This will throw NOTIMPLEMENTED() on Windows
  259. // For backwards compatibility, we'll return 0
  260. #if !defined(OS_WIN)
  261. dict.Set("idleWakeupsPerSecond", metrics->GetIdleWakeupsPerSecond());
  262. #else
  263. dict.Set("idleWakeupsPerSecond", 0);
  264. #endif
  265. return dict.GetHandle();
  266. }
  267. // static
  268. v8::Local<v8::Value> AtomBindings::GetIOCounters(v8::Isolate* isolate) {
  269. auto metrics = base::ProcessMetrics::CreateCurrentProcessMetrics();
  270. base::IoCounters io_counters;
  271. mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
  272. dict.SetHidden("simple", true);
  273. if (metrics->GetIOCounters(&io_counters)) {
  274. dict.Set("readOperationCount", io_counters.ReadOperationCount);
  275. dict.Set("writeOperationCount", io_counters.WriteOperationCount);
  276. dict.Set("otherOperationCount", io_counters.OtherOperationCount);
  277. dict.Set("readTransferCount", io_counters.ReadTransferCount);
  278. dict.Set("writeTransferCount", io_counters.WriteTransferCount);
  279. dict.Set("otherTransferCount", io_counters.OtherTransferCount);
  280. }
  281. return dict.GetHandle();
  282. }
  283. // static
  284. bool AtomBindings::TakeHeapSnapshot(v8::Isolate* isolate,
  285. const base::FilePath& file_path) {
  286. base::ThreadRestrictions::ScopedAllowIO allow_io;
  287. base::File file(file_path,
  288. base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
  289. return atom::TakeHeapSnapshot(isolate, &file);
  290. }
  291. } // namespace atom