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/protocol/Protocol.h" 9 #include "src/inspector/string-util.h" 10 #include "src/inspector/v8-debugger.h" 11 #include "src/inspector/v8-inspector-impl.h" 12 #include "src/inspector/v8-inspector-session-impl.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 explicit 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 explicit 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 explicit 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 double samplingInterval = m_state->doubleProperty( 168 HeapProfilerAgentState::samplingHeapProfilerInterval, -1); 169 DCHECK_GE(samplingInterval, 0); 170 startSampling(Maybe<double>(samplingInterval)); 171 } 172 } 173 174 Response V8HeapProfilerAgentImpl::collectGarbage() { 175 m_isolate->LowMemoryNotification(); 176 return Response::OK(); 177 } 178 179 Response V8HeapProfilerAgentImpl::startTrackingHeapObjects( 180 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 return Response::OK(); 187 } 188 189 Response V8HeapProfilerAgentImpl::stopTrackingHeapObjects( 190 Maybe<bool> reportProgress) { 191 requestHeapStatsUpdate(); 192 takeHeapSnapshot(std::move(reportProgress)); 193 stopTrackingHeapObjectsInternal(); 194 return Response::OK(); 195 } 196 197 Response V8HeapProfilerAgentImpl::enable() { 198 m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true); 199 return Response::OK(); 200 } 201 202 Response V8HeapProfilerAgentImpl::disable() { 203 stopTrackingHeapObjectsInternal(); 204 if (m_state->booleanProperty( 205 HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) { 206 v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); 207 if (profiler) profiler->StopSamplingHeapProfiler(); 208 } 209 m_isolate->GetHeapProfiler()->ClearObjectIds(); 210 m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, false); 211 return Response::OK(); 212 } 213 214 Response V8HeapProfilerAgentImpl::takeHeapSnapshot(Maybe<bool> reportProgress) { 215 v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); 216 if (!profiler) return Response::Error("Cannot access v8 heap profiler"); 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) return Response::Error("Failed to take heap snapshot"); 225 HeapSnapshotOutputStream stream(&m_frontend); 226 snapshot->Serialize(&stream); 227 const_cast<v8::HeapSnapshot*>(snapshot)->Delete(); 228 return Response::OK(); 229 } 230 231 Response V8HeapProfilerAgentImpl::getObjectByHeapObjectId( 232 const String16& heapSnapshotObjectId, Maybe<String16> objectGroup, 233 std::unique_ptr<protocol::Runtime::RemoteObject>* result) { 234 bool ok; 235 int id = heapSnapshotObjectId.toInteger(&ok); 236 if (!ok) return Response::Error("Invalid heap snapshot object id"); 237 238 v8::HandleScope handles(m_isolate); 239 v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id); 240 if (heapObject.IsEmpty()) return Response::Error("Object is not available"); 241 242 if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject)) 243 return Response::Error("Object is not available"); 244 245 *result = m_session->wrapObject(heapObject->CreationContext(), heapObject, 246 objectGroup.fromMaybe(""), false); 247 if (!result) return Response::Error("Object is not available"); 248 return Response::OK(); 249 } 250 251 Response V8HeapProfilerAgentImpl::addInspectedHeapObject( 252 const String16& inspectedHeapObjectId) { 253 bool ok; 254 int id = inspectedHeapObjectId.toInteger(&ok); 255 if (!ok) return Response::Error("Invalid heap snapshot object id"); 256 257 v8::HandleScope handles(m_isolate); 258 v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id); 259 if (heapObject.IsEmpty()) return Response::Error("Object is not available"); 260 261 if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject)) 262 return Response::Error("Object is not available"); 263 m_session->addInspectedObject(wrapUnique(new InspectableHeapObject(id))); 264 return Response::OK(); 265 } 266 267 Response V8HeapProfilerAgentImpl::getHeapObjectId( 268 const String16& objectId, String16* heapSnapshotObjectId) { 269 v8::HandleScope handles(m_isolate); 270 v8::Local<v8::Value> value; 271 v8::Local<v8::Context> context; 272 Response response = 273 m_session->unwrapObject(objectId, &value, &context, nullptr); 274 if (!response.isSuccess()) return response; 275 if (value->IsUndefined()) return Response::InternalError(); 276 277 v8::SnapshotObjectId id = m_isolate->GetHeapProfiler()->GetObjectId(value); 278 *heapSnapshotObjectId = String16::fromInteger(static_cast<size_t>(id)); 279 return Response::OK(); 280 } 281 282 void V8HeapProfilerAgentImpl::requestHeapStatsUpdate() { 283 HeapStatsStream stream(&m_frontend); 284 v8::SnapshotObjectId lastSeenObjectId = 285 m_isolate->GetHeapProfiler()->GetHeapStats(&stream); 286 m_frontend.lastSeenObjectId( 287 lastSeenObjectId, m_session->inspector()->client()->currentTimeMS()); 288 } 289 290 // static 291 void V8HeapProfilerAgentImpl::onTimer(void* data) { 292 reinterpret_cast<V8HeapProfilerAgentImpl*>(data)->requestHeapStatsUpdate(); 293 } 294 295 void V8HeapProfilerAgentImpl::startTrackingHeapObjectsInternal( 296 bool trackAllocations) { 297 m_isolate->GetHeapProfiler()->StartTrackingHeapObjects(trackAllocations); 298 if (!m_hasTimer) { 299 m_hasTimer = true; 300 m_session->inspector()->client()->startRepeatingTimer( 301 0.05, &V8HeapProfilerAgentImpl::onTimer, reinterpret_cast<void*>(this)); 302 } 303 } 304 305 void V8HeapProfilerAgentImpl::stopTrackingHeapObjectsInternal() { 306 if (m_hasTimer) { 307 m_session->inspector()->client()->cancelTimer( 308 reinterpret_cast<void*>(this)); 309 m_hasTimer = false; 310 } 311 m_isolate->GetHeapProfiler()->StopTrackingHeapObjects(); 312 m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, 313 false); 314 m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, false); 315 } 316 317 Response V8HeapProfilerAgentImpl::startSampling( 318 Maybe<double> samplingInterval) { 319 v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); 320 if (!profiler) return Response::Error("Cannot access v8 heap profiler"); 321 const unsigned defaultSamplingInterval = 1 << 15; 322 double samplingIntervalValue = 323 samplingInterval.fromMaybe(defaultSamplingInterval); 324 m_state->setDouble(HeapProfilerAgentState::samplingHeapProfilerInterval, 325 samplingIntervalValue); 326 m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled, 327 true); 328 profiler->StartSamplingHeapProfiler( 329 static_cast<uint64_t>(samplingIntervalValue), 128, 330 v8::HeapProfiler::kSamplingForceGC); 331 return Response::OK(); 332 } 333 334 namespace { 335 std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> 336 buildSampingHeapProfileNode(const v8::AllocationProfile::Node* node) { 337 auto children = protocol::Array< 338 protocol::HeapProfiler::SamplingHeapProfileNode>::create(); 339 for (const auto* child : node->children) 340 children->addItem(buildSampingHeapProfileNode(child)); 341 size_t selfSize = 0; 342 for (const auto& allocation : node->allocations) 343 selfSize += allocation.size * allocation.count; 344 std::unique_ptr<protocol::Runtime::CallFrame> callFrame = 345 protocol::Runtime::CallFrame::create() 346 .setFunctionName(toProtocolString(node->name)) 347 .setScriptId(String16::fromInteger(node->script_id)) 348 .setUrl(toProtocolString(node->script_name)) 349 .setLineNumber(node->line_number - 1) 350 .setColumnNumber(node->column_number - 1) 351 .build(); 352 std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> result = 353 protocol::HeapProfiler::SamplingHeapProfileNode::create() 354 .setCallFrame(std::move(callFrame)) 355 .setSelfSize(selfSize) 356 .setChildren(std::move(children)) 357 .build(); 358 return result; 359 } 360 } // namespace 361 362 Response V8HeapProfilerAgentImpl::stopSampling( 363 std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) { 364 v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); 365 if (!profiler) return Response::Error("Cannot access v8 heap profiler"); 366 v8::HandleScope scope( 367 m_isolate); // Allocation profile contains Local handles. 368 std::unique_ptr<v8::AllocationProfile> v8Profile( 369 profiler->GetAllocationProfile()); 370 profiler->StopSamplingHeapProfiler(); 371 m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled, 372 false); 373 if (!v8Profile) 374 return Response::Error("Cannot access v8 sampled heap profile."); 375 v8::AllocationProfile::Node* root = v8Profile->GetRootNode(); 376 *profile = protocol::HeapProfiler::SamplingHeapProfile::create() 377 .setHead(buildSampingHeapProfileNode(root)) 378 .build(); 379 return Response::OK(); 380 } 381 382 } // namespace v8_inspector 383