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