v8_isolate_memory_dump_provider.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. // Copyright 2015 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "gin/v8_isolate_memory_dump_provider.h"
  5. #include <inttypes.h>
  6. #include <stddef.h>
  7. #include "base/logging.h"
  8. #include "base/strings/stringprintf.h"
  9. #include "base/trace_event/memory_dump_manager.h"
  10. #include "base/trace_event/process_memory_dump.h"
  11. #include "gin/public/isolate_holder.h"
  12. #include "v8/include/v8-isolate.h"
  13. #include "v8/include/v8-locker.h"
  14. #include "v8/include/v8-statistics.h"
  15. namespace gin {
  16. V8IsolateMemoryDumpProvider::V8IsolateMemoryDumpProvider(
  17. IsolateHolder* isolate_holder,
  18. scoped_refptr<base::SingleThreadTaskRunner> task_runner)
  19. : isolate_holder_(isolate_holder) {
  20. DCHECK(task_runner);
  21. base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
  22. this, "V8Isolate", task_runner);
  23. }
  24. V8IsolateMemoryDumpProvider::~V8IsolateMemoryDumpProvider() {
  25. base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
  26. this);
  27. }
  28. // Called at trace dump point time. Creates a snapshot with the memory counters
  29. // for the current isolate.
  30. bool V8IsolateMemoryDumpProvider::OnMemoryDump(
  31. const base::trace_event::MemoryDumpArgs& args,
  32. base::trace_event::ProcessMemoryDump* process_memory_dump) {
  33. // TODO(ssid): Use MemoryDumpArgs to create light dumps when requested
  34. // (crbug.com/499731).
  35. if (isolate_holder_->access_mode() == IsolateHolder::kUseLocker) {
  36. v8::Locker locked(isolate_holder_->isolate());
  37. DumpHeapStatistics(args, process_memory_dump);
  38. } else {
  39. DumpHeapStatistics(args, process_memory_dump);
  40. }
  41. return true;
  42. }
  43. namespace {
  44. // Dump statistics related to code/bytecode when memory-infra.v8.code_stats is
  45. // enabled.
  46. void DumpCodeStatistics(base::trace_event::MemoryAllocatorDump* dump,
  47. IsolateHolder* isolate_holder) {
  48. // Collecting code statistics is an expensive operation (~10 ms) when
  49. // compared to other v8 metrics (< 1 ms). So, dump them only when
  50. // memory-infra.v8.code_stats is enabled.
  51. // TODO(primiano): This information should be plumbed through TraceConfig.
  52. // See crbug.com/616441.
  53. bool dump_code_stats = false;
  54. TRACE_EVENT_CATEGORY_GROUP_ENABLED(
  55. TRACE_DISABLED_BY_DEFAULT("memory-infra.v8.code_stats"),
  56. &dump_code_stats);
  57. if (!dump_code_stats)
  58. return;
  59. v8::HeapCodeStatistics code_statistics;
  60. if (!isolate_holder->isolate()->GetHeapCodeAndMetadataStatistics(
  61. &code_statistics)) {
  62. return;
  63. }
  64. dump->AddScalar("code_and_metadata_size",
  65. base::trace_event::MemoryAllocatorDump::kUnitsBytes,
  66. code_statistics.code_and_metadata_size());
  67. dump->AddScalar("bytecode_and_metadata_size",
  68. base::trace_event::MemoryAllocatorDump::kUnitsBytes,
  69. code_statistics.bytecode_and_metadata_size());
  70. dump->AddScalar("external_script_source_size",
  71. base::trace_event::MemoryAllocatorDump::kUnitsBytes,
  72. code_statistics.external_script_source_size());
  73. dump->AddScalar("cpu_profiler_metadata_size",
  74. base::trace_event::MemoryAllocatorDump::kUnitsBytes,
  75. code_statistics.cpu_profiler_metadata_size());
  76. }
  77. // Dump the number of native and detached contexts.
  78. // The result looks as follows in the Chrome trace viewer:
  79. // ========================================
  80. // Component object_count
  81. // - v8
  82. // - main
  83. // - contexts
  84. // - detached_context 10
  85. // - native_context 20
  86. // - workers
  87. // - contexts
  88. // - detached_context
  89. // - isolate_0x1234 10
  90. // - native_context
  91. // - isolate_0x1234 20
  92. // ========================================
  93. void DumpContextStatistics(
  94. base::trace_event::ProcessMemoryDump* process_memory_dump,
  95. std::string dump_base_name,
  96. std::string dump_name_suffix,
  97. size_t number_of_detached_contexts,
  98. size_t number_of_native_contexts) {
  99. std::string dump_name_prefix = dump_base_name + "/contexts";
  100. std::string native_context_name =
  101. dump_name_prefix + "/native_context" + dump_name_suffix;
  102. auto* native_context_dump =
  103. process_memory_dump->CreateAllocatorDump(native_context_name);
  104. native_context_dump->AddScalar(
  105. "object_count", base::trace_event::MemoryAllocatorDump::kUnitsObjects,
  106. number_of_native_contexts);
  107. std::string detached_context_name =
  108. dump_name_prefix + "/detached_context" + dump_name_suffix;
  109. auto* detached_context_dump =
  110. process_memory_dump->CreateAllocatorDump(detached_context_name);
  111. detached_context_dump->AddScalar(
  112. "object_count", base::trace_event::MemoryAllocatorDump::kUnitsObjects,
  113. number_of_detached_contexts);
  114. }
  115. std::string IsolateTypeString(IsolateHolder::IsolateType isolate_type) {
  116. switch (isolate_type) {
  117. case IsolateHolder::IsolateType::kBlinkMainThread:
  118. return "main";
  119. case IsolateHolder::IsolateType::kBlinkWorkerThread:
  120. return "workers";
  121. case IsolateHolder::IsolateType::kTest:
  122. LOG(FATAL) << "Unreachable code";
  123. return "test";
  124. case IsolateHolder::IsolateType::kUtility:
  125. return "utility";
  126. }
  127. LOG(FATAL) << "Unreachable code";
  128. }
  129. bool CanHaveMultipleIsolates(IsolateHolder::IsolateType isolate_type) {
  130. switch (isolate_type) {
  131. case IsolateHolder::IsolateType::kBlinkMainThread:
  132. return false;
  133. case IsolateHolder::IsolateType::kBlinkWorkerThread:
  134. return true;
  135. case IsolateHolder::IsolateType::kTest:
  136. LOG(FATAL) << "Unreachable code";
  137. return false;
  138. case IsolateHolder::IsolateType::kUtility:
  139. // PDFium and ProxyResolver create one isolate per process.
  140. return false;
  141. }
  142. LOG(FATAL) << "Unreachable code";
  143. }
  144. } // namespace
  145. void V8IsolateMemoryDumpProvider::DumpHeapStatistics(
  146. const base::trace_event::MemoryDumpArgs& args,
  147. base::trace_event::ProcessMemoryDump* process_memory_dump) {
  148. if (args.determinism == base::trace_event::MemoryDumpDeterminism::FORCE_GC) {
  149. // Force GC in V8 using the same API as DevTools uses in "collectGarbage".
  150. isolate_holder_->isolate()->LowMemoryNotification();
  151. }
  152. std::string isolate_name = base::StringPrintf(
  153. "isolate_0x%" PRIXPTR,
  154. reinterpret_cast<uintptr_t>(isolate_holder_->isolate()));
  155. // Dump statistics of the heap's spaces.
  156. v8::HeapStatistics heap_statistics;
  157. // The total heap sizes should be sampled before the individual space sizes
  158. // because of concurrent allocation. DCHECKs below rely on this order.
  159. isolate_holder_->isolate()->GetHeapStatistics(&heap_statistics);
  160. IsolateHolder::IsolateType isolate_type = isolate_holder_->isolate_type();
  161. std::string dump_base_name = "v8/" + IsolateTypeString(isolate_type);
  162. std::string dump_name_suffix =
  163. CanHaveMultipleIsolates(isolate_type) ? "/" + isolate_name : "";
  164. std::string space_name_prefix = dump_base_name + "/heap";
  165. size_t known_spaces_used_size = 0;
  166. size_t known_spaces_size = 0;
  167. size_t known_spaces_physical_size = 0;
  168. size_t number_of_spaces = isolate_holder_->isolate()->NumberOfHeapSpaces();
  169. for (size_t space = 0; space < number_of_spaces; space++) {
  170. v8::HeapSpaceStatistics space_statistics;
  171. isolate_holder_->isolate()->GetHeapSpaceStatistics(&space_statistics,
  172. space);
  173. const size_t space_size = space_statistics.space_size();
  174. const size_t space_used_size = space_statistics.space_used_size();
  175. const size_t space_physical_size = space_statistics.physical_space_size();
  176. known_spaces_size += space_size;
  177. known_spaces_used_size += space_used_size;
  178. known_spaces_physical_size += space_physical_size;
  179. std::string space_dump_name = dump_base_name + "/heap/" +
  180. space_statistics.space_name() +
  181. dump_name_suffix;
  182. auto* space_dump =
  183. process_memory_dump->CreateAllocatorDump(space_dump_name);
  184. space_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
  185. base::trace_event::MemoryAllocatorDump::kUnitsBytes,
  186. space_physical_size);
  187. space_dump->AddScalar("virtual_size",
  188. base::trace_event::MemoryAllocatorDump::kUnitsBytes,
  189. space_size);
  190. space_dump->AddScalar("allocated_objects_size",
  191. base::trace_event::MemoryAllocatorDump::kUnitsBytes,
  192. space_used_size);
  193. }
  194. // Sanity checks that all spaces are accounted for in GetHeapSpaceStatistics.
  195. // Background threads may be running and allocating concurrently, so the sum
  196. // of space sizes may be exceed the total heap size that was sampled earlier.
  197. DCHECK_LE(heap_statistics.total_physical_size(), known_spaces_physical_size);
  198. DCHECK_LE(heap_statistics.used_heap_size(), known_spaces_used_size);
  199. DCHECK_LE(heap_statistics.total_heap_size(), known_spaces_size);
  200. // If V8 zaps garbage, all the memory mapped regions become resident,
  201. // so we add an extra dump to avoid mismatches w.r.t. the total
  202. // resident values.
  203. if (heap_statistics.does_zap_garbage()) {
  204. auto* zap_dump = process_memory_dump->CreateAllocatorDump(
  205. dump_base_name + "/zapped_for_debug" + dump_name_suffix);
  206. zap_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
  207. base::trace_event::MemoryAllocatorDump::kUnitsBytes,
  208. known_spaces_size - known_spaces_physical_size);
  209. }
  210. // Dump statistics about malloced memory.
  211. std::string malloc_name = dump_base_name + "/malloc" + dump_name_suffix;
  212. auto* malloc_dump = process_memory_dump->CreateAllocatorDump(malloc_name);
  213. malloc_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
  214. base::trace_event::MemoryAllocatorDump::kUnitsBytes,
  215. heap_statistics.malloced_memory());
  216. malloc_dump->AddScalar("peak_size",
  217. base::trace_event::MemoryAllocatorDump::kUnitsBytes,
  218. heap_statistics.peak_malloced_memory());
  219. const char* system_allocator_name =
  220. base::trace_event::MemoryDumpManager::GetInstance()
  221. ->system_allocator_pool_name();
  222. if (system_allocator_name) {
  223. process_memory_dump->AddSuballocation(malloc_dump->guid(),
  224. system_allocator_name);
  225. }
  226. DumpContextStatistics(process_memory_dump, dump_base_name, dump_name_suffix,
  227. heap_statistics.number_of_detached_contexts(),
  228. heap_statistics.number_of_native_contexts());
  229. auto* code_stats_dump = process_memory_dump->CreateAllocatorDump(
  230. dump_base_name + "/code_stats" + dump_name_suffix);
  231. // Dump statistics related to code and bytecode if requested.
  232. DumpCodeStatistics(code_stats_dump, isolate_holder_);
  233. // Dump statistics for global handles.
  234. auto* global_handles_dump = process_memory_dump->CreateAllocatorDump(
  235. dump_base_name + "/global_handles" + dump_name_suffix);
  236. global_handles_dump->AddScalar(
  237. base::trace_event::MemoryAllocatorDump::kNameSize,
  238. base::trace_event::MemoryAllocatorDump::kUnitsBytes,
  239. heap_statistics.total_global_handles_size());
  240. global_handles_dump->AddScalar(
  241. "allocated_objects_size",
  242. base::trace_event::MemoryAllocatorDump::kUnitsBytes,
  243. heap_statistics.used_global_handles_size());
  244. if (system_allocator_name) {
  245. process_memory_dump->AddSuballocation(global_handles_dump->guid(),
  246. system_allocator_name);
  247. }
  248. // Dump object statistics only for detailed dumps.
  249. if (args.level_of_detail !=
  250. base::trace_event::MemoryDumpLevelOfDetail::DETAILED) {
  251. return;
  252. }
  253. // Dump statistics of the heap's live objects from last GC.
  254. // TODO(primiano): these should not be tracked in the same trace event as they
  255. // report stats for the last GC (not the current state). See crbug.com/498779.
  256. std::string object_name_prefix =
  257. dump_base_name + "/heap_objects_at_last_gc" + dump_name_suffix;
  258. bool did_dump_object_stats = false;
  259. const size_t object_types =
  260. isolate_holder_->isolate()->NumberOfTrackedHeapObjectTypes();
  261. for (size_t type_index = 0; type_index < object_types; type_index++) {
  262. v8::HeapObjectStatistics object_statistics;
  263. if (!isolate_holder_->isolate()->GetHeapObjectStatisticsAtLastGC(
  264. &object_statistics, type_index))
  265. continue;
  266. std::string dump_name =
  267. object_name_prefix + "/" + object_statistics.object_type();
  268. if (object_statistics.object_sub_type()[0] != '\0')
  269. dump_name += std::string("/") + object_statistics.object_sub_type();
  270. auto* object_dump = process_memory_dump->CreateAllocatorDump(dump_name);
  271. object_dump->AddScalar(
  272. base::trace_event::MemoryAllocatorDump::kNameObjectCount,
  273. base::trace_event::MemoryAllocatorDump::kUnitsObjects,
  274. object_statistics.object_count());
  275. object_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
  276. base::trace_event::MemoryAllocatorDump::kUnitsBytes,
  277. object_statistics.object_size());
  278. did_dump_object_stats = true;
  279. }
  280. if (process_memory_dump->GetAllocatorDump(object_name_prefix +
  281. "/CODE_TYPE")) {
  282. auto* code_kind_dump = process_memory_dump->CreateAllocatorDump(
  283. object_name_prefix + "/CODE_TYPE/CODE_KIND");
  284. auto* code_age_dump = process_memory_dump->CreateAllocatorDump(
  285. object_name_prefix + "/CODE_TYPE/CODE_AGE");
  286. process_memory_dump->AddOwnershipEdge(code_kind_dump->guid(),
  287. code_age_dump->guid());
  288. }
  289. if (did_dump_object_stats) {
  290. process_memory_dump->AddOwnershipEdge(
  291. process_memory_dump->CreateAllocatorDump(object_name_prefix)->guid(),
  292. process_memory_dump->GetOrCreateAllocatorDump(space_name_prefix)
  293. ->guid());
  294. }
  295. }
  296. } // namespace gin