atom_bindings.cc 11 KB

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