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