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 
5 #include "third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h"
6 
7 #include "base/strings/string_number_conversions.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/trace_event/memory_allocator_dump.h"
10 #include "base/trace_event/memory_dump_manager.h"
11 #include "base/trace_event/process_memory_dump.h"
12 #include "third_party/blink/public/platform/platform.h"
13 #include "third_party/blink/renderer/platform/heap/handle.h"
14 #include "third_party/blink/renderer/platform/heap/impl/thread_state_statistics.h"
15 #include "third_party/blink/renderer/platform/heap/thread_state.h"
16 #include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
17 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
18 #include "third_party/blink/renderer/platform/wtf/threading.h"
19 
20 namespace blink {
21 namespace {
22 
HeapTypeString(BlinkGCMemoryDumpProvider::HeapType heap_type)23 constexpr const char* HeapTypeString(
24     BlinkGCMemoryDumpProvider::HeapType heap_type) {
25   switch (heap_type) {
26     case BlinkGCMemoryDumpProvider::HeapType::kBlinkMainThread:
27       return "main";
28     case BlinkGCMemoryDumpProvider::HeapType::kBlinkWorkerThread:
29       return "workers";
30   }
31 }
32 
33 }  // namespace
34 
BlinkGCMemoryDumpProvider(ThreadState * thread_state,scoped_refptr<base::SingleThreadTaskRunner> task_runner,BlinkGCMemoryDumpProvider::HeapType heap_type)35 BlinkGCMemoryDumpProvider::BlinkGCMemoryDumpProvider(
36     ThreadState* thread_state,
37     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
38     BlinkGCMemoryDumpProvider::HeapType heap_type)
39     : thread_state_(thread_state),
40       heap_type_(heap_type),
41       dump_base_name_(
42           "blink_gc/" + std::string(HeapTypeString(heap_type_)) + "/heap" +
43           (heap_type_ == HeapType::kBlinkWorkerThread
44                ? "/" + base::StringPrintf(
45                            "worker_0x%" PRIXPTR,
46                            reinterpret_cast<uintptr_t>(thread_state_))
47                : "")) {
48   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
49       this, "BlinkGC", task_runner);
50 }
51 
~BlinkGCMemoryDumpProvider()52 BlinkGCMemoryDumpProvider::~BlinkGCMemoryDumpProvider() {
53   base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
54       this);
55 }
56 
OnMemoryDump(const base::trace_event::MemoryDumpArgs & args,base::trace_event::ProcessMemoryDump * process_memory_dump)57 bool BlinkGCMemoryDumpProvider::OnMemoryDump(
58     const base::trace_event::MemoryDumpArgs& args,
59     base::trace_event::ProcessMemoryDump* process_memory_dump) {
60   ThreadState::Statistics::DetailLevel detail_level =
61       args.level_of_detail ==
62               base::trace_event::MemoryDumpLevelOfDetail::DETAILED
63           ? ThreadState::Statistics::kDetailed
64           : ThreadState::Statistics::kBrief;
65 
66   ThreadState::Statistics stats =
67       ThreadState::StatisticsCollector(thread_state_)
68           .CollectStatistics(detail_level);
69 
70   auto* heap_dump = process_memory_dump->CreateAllocatorDump(dump_base_name_);
71   heap_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
72                        base::trace_event::MemoryAllocatorDump::kUnitsBytes,
73                        stats.committed_size_bytes);
74   heap_dump->AddScalar("allocated_objects_size",
75                        base::trace_event::MemoryAllocatorDump::kUnitsBytes,
76                        stats.used_size_bytes);
77 
78   if (detail_level == ThreadState::Statistics::kBrief) {
79     return true;
80   }
81 
82   // Detailed statistics.
83   for (const ThreadState::Statistics::ArenaStatistics& arena_stats :
84        stats.arena_stats) {
85     std::string arena_dump_name = dump_base_name_ + "/" + arena_stats.name;
86     auto* arena_dump =
87         process_memory_dump->CreateAllocatorDump(arena_dump_name);
88     arena_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
89                           base::trace_event::MemoryAllocatorDump::kUnitsBytes,
90                           arena_stats.committed_size_bytes);
91     arena_dump->AddScalar("allocated_objects_size",
92                           base::trace_event::MemoryAllocatorDump::kUnitsBytes,
93                           arena_stats.used_size_bytes);
94 
95     size_t page_count = 0;
96     for (const ThreadState::Statistics::PageStatistics& page_stats :
97          arena_stats.page_stats) {
98       auto* page_dump = process_memory_dump->CreateAllocatorDump(
99           arena_dump_name + "/pages/page_" +
100           base::NumberToString(page_count++));
101       page_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
102                            base::trace_event::MemoryAllocatorDump::kUnitsBytes,
103                            page_stats.committed_size_bytes);
104       page_dump->AddScalar("allocated_objects_size",
105                            base::trace_event::MemoryAllocatorDump::kUnitsBytes,
106                            page_stats.used_size_bytes);
107     }
108 
109     const ThreadState::Statistics::FreeListStatistics& free_list_stats =
110         arena_stats.free_list_stats;
111     for (wtf_size_t i = 0; i < free_list_stats.bucket_size.size(); ++i) {
112       constexpr size_t kDigits = 8;
113       std::string original_bucket_size =
114           base::NumberToString(free_list_stats.bucket_size[i]);
115       std::string padded_bucket_size =
116           std::string(kDigits - original_bucket_size.length(), '0') +
117           original_bucket_size;
118       auto* free_list_bucket_dump = process_memory_dump->CreateAllocatorDump(
119           arena_dump_name + "/freelist/bucket_" + padded_bucket_size);
120       free_list_bucket_dump->AddScalar(
121           "free_size", base::trace_event::MemoryAllocatorDump::kUnitsBytes,
122           free_list_stats.free_size[i]);
123     }
124 
125     const ThreadState::Statistics::ObjectStatistics& object_stats =
126         arena_stats.object_stats;
127     for (wtf_size_t i = 1; i < object_stats.num_types; i++) {
128       if (object_stats.type_name[i].empty())
129         continue;
130 
131       auto* class_dump = process_memory_dump->CreateAllocatorDump(
132           arena_dump_name + "/classes/" + object_stats.type_name[i]);
133       class_dump->AddScalar(
134           "object_count", base::trace_event::MemoryAllocatorDump::kUnitsObjects,
135           object_stats.type_count[i]);
136       class_dump->AddScalar("object_size",
137                             base::trace_event::MemoryAllocatorDump::kUnitsBytes,
138                             object_stats.type_bytes[i]);
139     }
140   }
141   return true;
142 }
143 
144 }  // namespace blink
145