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