| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 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 "platform/v8_inspector/V8HeapProfilerAgentImpl.h" | |
| 6 | |
| 7 #include "platform/v8_inspector/InjectedScript.h" | |
| 8 #include "platform/v8_inspector/StringUtil.h" | |
| 9 #include "platform/v8_inspector/V8Debugger.h" | |
| 10 #include "platform/v8_inspector/V8InspectorImpl.h" | |
| 11 #include "platform/v8_inspector/V8InspectorSessionImpl.h" | |
| 12 #include "platform/v8_inspector/protocol/Protocol.h" | |
| 13 #include "platform/v8_inspector/public/V8InspectorClient.h" | |
| 14 | |
| 15 #include <v8-profiler.h> | |
| 16 #include <v8-version.h> | |
| 17 | |
| 18 namespace v8_inspector { | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 namespace HeapProfilerAgentState { | |
| 23 static const char heapProfilerEnabled[] = "heapProfilerEnabled"; | |
| 24 static const char heapObjectsTrackingEnabled[] = "heapObjectsTrackingEnabled"; | |
| 25 static const char allocationTrackingEnabled[] = "allocationTrackingEnabled"; | |
| 26 #if V8_MAJOR_VERSION >= 5 | |
| 27 static const char samplingHeapProfilerEnabled[] = "samplingHeapProfilerEnabled"; | |
| 28 static const char samplingHeapProfilerInterval[] = "samplingHeapProfilerInterval
"; | |
| 29 #endif | |
| 30 } | |
| 31 | |
| 32 class HeapSnapshotProgress final : public v8::ActivityControl { | |
| 33 public: | |
| 34 HeapSnapshotProgress(protocol::HeapProfiler::Frontend* frontend) | |
| 35 : m_frontend(frontend) { } | |
| 36 ControlOption ReportProgressValue(int done, int total) override | |
| 37 { | |
| 38 m_frontend->reportHeapSnapshotProgress(done, total, protocol::Maybe<bool
>()); | |
| 39 if (done >= total) { | |
| 40 m_frontend->reportHeapSnapshotProgress(total, total, true); | |
| 41 } | |
| 42 m_frontend->flush(); | |
| 43 return kContinue; | |
| 44 } | |
| 45 private: | |
| 46 protocol::HeapProfiler::Frontend* m_frontend; | |
| 47 }; | |
| 48 | |
| 49 class GlobalObjectNameResolver final : public v8::HeapProfiler::ObjectNameResolv
er { | |
| 50 public: | |
| 51 explicit GlobalObjectNameResolver(V8InspectorSessionImpl* session) | |
| 52 : m_offset(0), m_strings(10000), m_session(session) {} | |
| 53 | |
| 54 const char* GetName(v8::Local<v8::Object> object) override | |
| 55 { | |
| 56 InspectedContext* context = m_session->inspector()->getContext(m_session
->contextGroupId(), V8Debugger::contextId(object->CreationContext())); | |
| 57 if (!context) | |
| 58 return ""; | |
| 59 String16 name = context->origin(); | |
| 60 size_t length = name.length(); | |
| 61 if (m_offset + length + 1 >= m_strings.size()) | |
| 62 return ""; | |
| 63 for (size_t i = 0; i < length; ++i) { | |
| 64 UChar ch = name[i]; | |
| 65 m_strings[m_offset + i] = ch > 0xff ? '?' : static_cast<char>(ch); | |
| 66 } | |
| 67 m_strings[m_offset + length] = '\0'; | |
| 68 char* result = &*m_strings.begin() + m_offset; | |
| 69 m_offset += length + 1; | |
| 70 return result; | |
| 71 } | |
| 72 | |
| 73 private: | |
| 74 size_t m_offset; | |
| 75 std::vector<char> m_strings; | |
| 76 V8InspectorSessionImpl* m_session; | |
| 77 }; | |
| 78 | |
| 79 class HeapSnapshotOutputStream final : public v8::OutputStream { | |
| 80 public: | |
| 81 HeapSnapshotOutputStream(protocol::HeapProfiler::Frontend* frontend) | |
| 82 : m_frontend(frontend) { } | |
| 83 void EndOfStream() override { } | |
| 84 int GetChunkSize() override { return 102400; } | |
| 85 WriteResult WriteAsciiChunk(char* data, int size) override | |
| 86 { | |
| 87 m_frontend->addHeapSnapshotChunk(String16(data, size)); | |
| 88 m_frontend->flush(); | |
| 89 return kContinue; | |
| 90 } | |
| 91 private: | |
| 92 protocol::HeapProfiler::Frontend* m_frontend; | |
| 93 }; | |
| 94 | |
| 95 v8::Local<v8::Object> objectByHeapObjectId(v8::Isolate* isolate, int id) | |
| 96 { | |
| 97 v8::HeapProfiler* profiler = isolate->GetHeapProfiler(); | |
| 98 v8::Local<v8::Value> value = profiler->FindObjectById(id); | |
| 99 if (value.IsEmpty() || !value->IsObject()) | |
| 100 return v8::Local<v8::Object>(); | |
| 101 return value.As<v8::Object>(); | |
| 102 } | |
| 103 | |
| 104 class InspectableHeapObject final : public V8InspectorSession::Inspectable { | |
| 105 public: | |
| 106 explicit InspectableHeapObject(int heapObjectId) : m_heapObjectId(heapObject
Id) { } | |
| 107 v8::Local<v8::Value> get(v8::Local<v8::Context> context) override | |
| 108 { | |
| 109 return objectByHeapObjectId(context->GetIsolate(), m_heapObjectId); | |
| 110 } | |
| 111 private: | |
| 112 int m_heapObjectId; | |
| 113 }; | |
| 114 | |
| 115 class HeapStatsStream final : public v8::OutputStream { | |
| 116 public: | |
| 117 HeapStatsStream(protocol::HeapProfiler::Frontend* frontend) | |
| 118 : m_frontend(frontend) | |
| 119 { | |
| 120 } | |
| 121 | |
| 122 void EndOfStream() override { } | |
| 123 | |
| 124 WriteResult WriteAsciiChunk(char* data, int size) override | |
| 125 { | |
| 126 DCHECK(false); | |
| 127 return kAbort; | |
| 128 } | |
| 129 | |
| 130 WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* updateData, int count)
override | |
| 131 { | |
| 132 DCHECK_GT(count, 0); | |
| 133 std::unique_ptr<protocol::Array<int>> statsDiff = protocol::Array<int>::
create(); | |
| 134 for (int i = 0; i < count; ++i) { | |
| 135 statsDiff->addItem(updateData[i].index); | |
| 136 statsDiff->addItem(updateData[i].count); | |
| 137 statsDiff->addItem(updateData[i].size); | |
| 138 } | |
| 139 m_frontend->heapStatsUpdate(std::move(statsDiff)); | |
| 140 return kContinue; | |
| 141 } | |
| 142 | |
| 143 private: | |
| 144 protocol::HeapProfiler::Frontend* m_frontend; | |
| 145 }; | |
| 146 | |
| 147 } // namespace | |
| 148 | |
| 149 V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl(V8InspectorSessionImpl* session
, protocol::FrontendChannel* frontendChannel, protocol::DictionaryValue* state) | |
| 150 : m_session(session) | |
| 151 , m_isolate(session->inspector()->isolate()) | |
| 152 , m_frontend(frontendChannel) | |
| 153 , m_state(state) | |
| 154 , m_hasTimer(false) | |
| 155 { | |
| 156 } | |
| 157 | |
| 158 V8HeapProfilerAgentImpl::~V8HeapProfilerAgentImpl() | |
| 159 { | |
| 160 } | |
| 161 | |
| 162 void V8HeapProfilerAgentImpl::restore() | |
| 163 { | |
| 164 if (m_state->booleanProperty(HeapProfilerAgentState::heapProfilerEnabled, fa
lse)) | |
| 165 m_frontend.resetProfiles(); | |
| 166 if (m_state->booleanProperty(HeapProfilerAgentState::heapObjectsTrackingEnab
led, false)) | |
| 167 startTrackingHeapObjectsInternal(m_state->booleanProperty(HeapProfilerAg
entState::allocationTrackingEnabled, false)); | |
| 168 #if V8_MAJOR_VERSION >= 5 | |
| 169 if (m_state->booleanProperty(HeapProfilerAgentState::samplingHeapProfilerEna
bled, false)) { | |
| 170 ErrorString error; | |
| 171 double samplingInterval = m_state->doubleProperty(HeapProfilerAgentState
::samplingHeapProfilerInterval, -1); | |
| 172 DCHECK_GE(samplingInterval, 0); | |
| 173 startSampling(&error, Maybe<double>(samplingInterval)); | |
| 174 } | |
| 175 #endif | |
| 176 } | |
| 177 | |
| 178 void V8HeapProfilerAgentImpl::collectGarbage(ErrorString*) | |
| 179 { | |
| 180 m_isolate->LowMemoryNotification(); | |
| 181 } | |
| 182 | |
| 183 void V8HeapProfilerAgentImpl::startTrackingHeapObjects(ErrorString*, const proto
col::Maybe<bool>& trackAllocations) | |
| 184 { | |
| 185 m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, true
); | |
| 186 bool allocationTrackingEnabled = trackAllocations.fromMaybe(false); | |
| 187 m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, alloc
ationTrackingEnabled); | |
| 188 startTrackingHeapObjectsInternal(allocationTrackingEnabled); | |
| 189 } | |
| 190 | |
| 191 void V8HeapProfilerAgentImpl::stopTrackingHeapObjects(ErrorString* error, const
protocol::Maybe<bool>& reportProgress) | |
| 192 { | |
| 193 requestHeapStatsUpdate(); | |
| 194 takeHeapSnapshot(error, reportProgress); | |
| 195 stopTrackingHeapObjectsInternal(); | |
| 196 } | |
| 197 | |
| 198 void V8HeapProfilerAgentImpl::enable(ErrorString*) | |
| 199 { | |
| 200 m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true); | |
| 201 } | |
| 202 | |
| 203 void V8HeapProfilerAgentImpl::disable(ErrorString* error) | |
| 204 { | |
| 205 stopTrackingHeapObjectsInternal(); | |
| 206 #if V8_MAJOR_VERSION >= 5 | |
| 207 if (m_state->booleanProperty(HeapProfilerAgentState::samplingHeapProfilerEna
bled, false)) { | |
| 208 v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); | |
| 209 if (profiler) | |
| 210 profiler->StopSamplingHeapProfiler(); | |
| 211 } | |
| 212 #endif | |
| 213 m_isolate->GetHeapProfiler()->ClearObjectIds(); | |
| 214 m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, false); | |
| 215 } | |
| 216 | |
| 217 void V8HeapProfilerAgentImpl::takeHeapSnapshot(ErrorString* errorString, const p
rotocol::Maybe<bool>& reportProgress) | |
| 218 { | |
| 219 v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); | |
| 220 if (!profiler) { | |
| 221 *errorString = "Cannot access v8 heap profiler"; | |
| 222 return; | |
| 223 } | |
| 224 std::unique_ptr<HeapSnapshotProgress> progress; | |
| 225 if (reportProgress.fromMaybe(false)) | |
| 226 progress = wrapUnique(new HeapSnapshotProgress(&m_frontend)); | |
| 227 | |
| 228 GlobalObjectNameResolver resolver(m_session); | |
| 229 const v8::HeapSnapshot* snapshot = profiler->TakeHeapSnapshot(progress.get()
, &resolver); | |
| 230 if (!snapshot) { | |
| 231 *errorString = "Failed to take heap snapshot"; | |
| 232 return; | |
| 233 } | |
| 234 HeapSnapshotOutputStream stream(&m_frontend); | |
| 235 snapshot->Serialize(&stream); | |
| 236 const_cast<v8::HeapSnapshot*>(snapshot)->Delete(); | |
| 237 } | |
| 238 | |
| 239 void V8HeapProfilerAgentImpl::getObjectByHeapObjectId(ErrorString* error, const
String16& heapSnapshotObjectId, const protocol::Maybe<String16>& objectGroup, st
d::unique_ptr<protocol::Runtime::RemoteObject>* result) | |
| 240 { | |
| 241 bool ok; | |
| 242 int id = heapSnapshotObjectId.toInteger(&ok); | |
| 243 if (!ok) { | |
| 244 *error = "Invalid heap snapshot object id"; | |
| 245 return; | |
| 246 } | |
| 247 | |
| 248 v8::HandleScope handles(m_isolate); | |
| 249 v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id); | |
| 250 if (heapObject.IsEmpty()) { | |
| 251 *error = "Object is not available"; | |
| 252 return; | |
| 253 } | |
| 254 | |
| 255 if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject))
{ | |
| 256 *error = "Object is not available"; | |
| 257 return; | |
| 258 } | |
| 259 | |
| 260 *result = m_session->wrapObject(heapObject->CreationContext(), heapObject, o
bjectGroup.fromMaybe(""), false); | |
| 261 if (!result) | |
| 262 *error = "Object is not available"; | |
| 263 } | |
| 264 | |
| 265 void V8HeapProfilerAgentImpl::addInspectedHeapObject(ErrorString* errorString, c
onst String16& inspectedHeapObjectId) | |
| 266 { | |
| 267 bool ok; | |
| 268 int id = inspectedHeapObjectId.toInteger(&ok); | |
| 269 if (!ok) { | |
| 270 *errorString = "Invalid heap snapshot object id"; | |
| 271 return; | |
| 272 } | |
| 273 | |
| 274 v8::HandleScope handles(m_isolate); | |
| 275 v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id); | |
| 276 if (heapObject.IsEmpty()) { | |
| 277 *errorString = "Object is not available"; | |
| 278 return; | |
| 279 } | |
| 280 | |
| 281 if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject))
{ | |
| 282 *errorString = "Object is not available"; | |
| 283 return; | |
| 284 } | |
| 285 | |
| 286 m_session->addInspectedObject(wrapUnique(new InspectableHeapObject(id))); | |
| 287 } | |
| 288 | |
| 289 void V8HeapProfilerAgentImpl::getHeapObjectId(ErrorString* errorString, const St
ring16& objectId, String16* heapSnapshotObjectId) | |
| 290 { | |
| 291 v8::HandleScope handles(m_isolate); | |
| 292 v8::Local<v8::Value> value; | |
| 293 v8::Local<v8::Context> context; | |
| 294 if (!m_session->unwrapObject(errorString, objectId, &value, &context, nullpt
r) || value->IsUndefined()) | |
| 295 return; | |
| 296 | |
| 297 v8::SnapshotObjectId id = m_isolate->GetHeapProfiler()->GetObjectId(value); | |
| 298 *heapSnapshotObjectId = String16::fromInteger(id); | |
| 299 } | |
| 300 | |
| 301 void V8HeapProfilerAgentImpl::requestHeapStatsUpdate() | |
| 302 { | |
| 303 HeapStatsStream stream(&m_frontend); | |
| 304 v8::SnapshotObjectId lastSeenObjectId = m_isolate->GetHeapProfiler()->GetHea
pStats(&stream); | |
| 305 m_frontend.lastSeenObjectId(lastSeenObjectId, m_session->inspector()->client
()->currentTimeMS()); | |
| 306 } | |
| 307 | |
| 308 // static | |
| 309 void V8HeapProfilerAgentImpl::onTimer(void* data) | |
| 310 { | |
| 311 reinterpret_cast<V8HeapProfilerAgentImpl*>(data)->requestHeapStatsUpdate(); | |
| 312 } | |
| 313 | |
| 314 void V8HeapProfilerAgentImpl::startTrackingHeapObjectsInternal(bool trackAllocat
ions) | |
| 315 { | |
| 316 m_isolate->GetHeapProfiler()->StartTrackingHeapObjects(trackAllocations); | |
| 317 if (!m_hasTimer) { | |
| 318 m_hasTimer = true; | |
| 319 m_session->inspector()->client()->startRepeatingTimer(0.05, &V8HeapProfi
lerAgentImpl::onTimer, reinterpret_cast<void*>(this)); | |
| 320 } | |
| 321 } | |
| 322 | |
| 323 void V8HeapProfilerAgentImpl::stopTrackingHeapObjectsInternal() | |
| 324 { | |
| 325 if (m_hasTimer) { | |
| 326 m_session->inspector()->client()->cancelTimer(reinterpret_cast<void*>(th
is)); | |
| 327 m_hasTimer = false; | |
| 328 } | |
| 329 m_isolate->GetHeapProfiler()->StopTrackingHeapObjects(); | |
| 330 m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, fals
e); | |
| 331 m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, false
); | |
| 332 } | |
| 333 | |
| 334 void V8HeapProfilerAgentImpl::startSampling(ErrorString* errorString, const Mayb
e<double>& samplingInterval) | |
| 335 { | |
| 336 #if V8_MAJOR_VERSION >= 5 | |
| 337 v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); | |
| 338 if (!profiler) { | |
| 339 *errorString = "Cannot access v8 heap profiler"; | |
| 340 return; | |
| 341 } | |
| 342 const unsigned defaultSamplingInterval = 1 << 15; | |
| 343 double samplingIntervalValue = samplingInterval.fromMaybe(defaultSamplingInt
erval); | |
| 344 m_state->setDouble(HeapProfilerAgentState::samplingHeapProfilerInterval, sam
plingIntervalValue); | |
| 345 m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled, tru
e); | |
| 346 #if V8_MAJOR_VERSION * 1000 + V8_MINOR_VERSION >= 5002 | |
| 347 profiler->StartSamplingHeapProfiler(static_cast<uint64_t>(samplingIntervalVa
lue), 128, v8::HeapProfiler::kSamplingForceGC); | |
| 348 #else | |
| 349 profiler->StartSamplingHeapProfiler(static_cast<uint64_t>(samplingIntervalVa
lue), 128); | |
| 350 #endif | |
| 351 #endif | |
| 352 } | |
| 353 | |
| 354 #if V8_MAJOR_VERSION >= 5 | |
| 355 namespace { | |
| 356 std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> buildSampingHea
pProfileNode(const v8::AllocationProfile::Node* node) | |
| 357 { | |
| 358 auto children = protocol::Array<protocol::HeapProfiler::SamplingHeapProfileN
ode>::create(); | |
| 359 for (const auto* child : node->children) | |
| 360 children->addItem(buildSampingHeapProfileNode(child)); | |
| 361 size_t selfSize = 0; | |
| 362 for (const auto& allocation : node->allocations) | |
| 363 selfSize += allocation.size * allocation.count; | |
| 364 std::unique_ptr<protocol::Runtime::CallFrame> callFrame = protocol::Runtime:
:CallFrame::create() | |
| 365 .setFunctionName(toProtocolString(node->name)) | |
| 366 .setScriptId(String16::fromInteger(node->script_id)) | |
| 367 .setUrl(toProtocolString(node->script_name)) | |
| 368 .setLineNumber(node->line_number - 1) | |
| 369 .setColumnNumber(node->column_number - 1) | |
| 370 .build(); | |
| 371 std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> result = pr
otocol::HeapProfiler::SamplingHeapProfileNode::create() | |
| 372 .setCallFrame(std::move(callFrame)) | |
| 373 .setSelfSize(selfSize) | |
| 374 .setChildren(std::move(children)).build(); | |
| 375 return result; | |
| 376 } | |
| 377 } // namespace | |
| 378 #endif | |
| 379 | |
| 380 void V8HeapProfilerAgentImpl::stopSampling(ErrorString* errorString, std::unique
_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) | |
| 381 { | |
| 382 #if V8_MAJOR_VERSION >= 5 | |
| 383 v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); | |
| 384 if (!profiler) { | |
| 385 *errorString = "Cannot access v8 heap profiler"; | |
| 386 return; | |
| 387 } | |
| 388 v8::HandleScope scope(m_isolate); // Allocation profile contains Local handl
es. | |
| 389 std::unique_ptr<v8::AllocationProfile> v8Profile(profiler->GetAllocationProf
ile()); | |
| 390 profiler->StopSamplingHeapProfiler(); | |
| 391 m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled, fal
se); | |
| 392 if (!v8Profile) { | |
| 393 *errorString = "Cannot access v8 sampled heap profile."; | |
| 394 return; | |
| 395 } | |
| 396 v8::AllocationProfile::Node* root = v8Profile->GetRootNode(); | |
| 397 *profile = protocol::HeapProfiler::SamplingHeapProfile::create() | |
| 398 .setHead(buildSampingHeapProfileNode(root)).build(); | |
| 399 #endif | |
| 400 } | |
| 401 | |
| 402 } // namespace v8_inspector | |
| OLD | NEW |