1 // Copyright 2015 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-profiler-agent-impl.h" 6 7 #include <vector> 8 9 #include "src/base/atomicops.h" 10 #include "src/inspector/protocol/Protocol.h" 11 #include "src/inspector/string-util.h" 12 #include "src/inspector/v8-debugger.h" 13 #include "src/inspector/v8-inspector-impl.h" 14 #include "src/inspector/v8-inspector-session-impl.h" 15 #include "src/inspector/v8-stack-trace-impl.h" 16 17 #include "include/v8-profiler.h" 18 19 namespace v8_inspector { 20 21 namespace ProfilerAgentState { 22 static const char samplingInterval[] = "samplingInterval"; 23 static const char userInitiatedProfiling[] = "userInitiatedProfiling"; 24 static const char profilerEnabled[] = "profilerEnabled"; 25 } 26 27 namespace { 28 29 std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>> 30 buildInspectorObjectForPositionTicks(const v8::CpuProfileNode* node) { 31 unsigned lineCount = node->GetHitLineCount(); 32 if (!lineCount) return nullptr; 33 auto array = protocol::Array<protocol::Profiler::PositionTickInfo>::create(); 34 std::vector<v8::CpuProfileNode::LineTick> entries(lineCount); 35 if (node->GetLineTicks(&entries[0], lineCount)) { 36 for (unsigned i = 0; i < lineCount; i++) { 37 std::unique_ptr<protocol::Profiler::PositionTickInfo> line = 38 protocol::Profiler::PositionTickInfo::create() 39 .setLine(entries[i].line) 40 .setTicks(entries[i].hit_count) 41 .build(); 42 array->addItem(std::move(line)); 43 } 44 } 45 return array; 46 } 47 48 std::unique_ptr<protocol::Profiler::ProfileNode> buildInspectorObjectFor( 49 v8::Isolate* isolate, const v8::CpuProfileNode* node) { 50 v8::HandleScope handleScope(isolate); 51 auto callFrame = 52 protocol::Runtime::CallFrame::create() 53 .setFunctionName(toProtocolString(node->GetFunctionName())) 54 .setScriptId(String16::fromInteger(node->GetScriptId())) 55 .setUrl(toProtocolString(node->GetScriptResourceName())) 56 .setLineNumber(node->GetLineNumber() - 1) 57 .setColumnNumber(node->GetColumnNumber() - 1) 58 .build(); 59 auto result = protocol::Profiler::ProfileNode::create() 60 .setCallFrame(std::move(callFrame)) 61 .setHitCount(node->GetHitCount()) 62 .setId(node->GetNodeId()) 63 .build(); 64 65 const int childrenCount = node->GetChildrenCount(); 66 if (childrenCount) { 67 auto children = protocol::Array<int>::create(); 68 for (int i = 0; i < childrenCount; i++) 69 children->addItem(node->GetChild(i)->GetNodeId()); 70 result->setChildren(std::move(children)); 71 } 72 73 const char* deoptReason = node->GetBailoutReason(); 74 if (deoptReason && deoptReason[0] && strcmp(deoptReason, "no reason")) 75 result->setDeoptReason(deoptReason); 76 77 auto positionTicks = buildInspectorObjectForPositionTicks(node); 78 if (positionTicks) result->setPositionTicks(std::move(positionTicks)); 79 80 return result; 81 } 82 83 std::unique_ptr<protocol::Array<int>> buildInspectorObjectForSamples( 84 v8::CpuProfile* v8profile) { 85 auto array = protocol::Array<int>::create(); 86 int count = v8profile->GetSamplesCount(); 87 for (int i = 0; i < count; i++) 88 array->addItem(v8profile->GetSample(i)->GetNodeId()); 89 return array; 90 } 91 92 std::unique_ptr<protocol::Array<int>> buildInspectorObjectForTimestamps( 93 v8::CpuProfile* v8profile) { 94 auto array = protocol::Array<int>::create(); 95 int count = v8profile->GetSamplesCount(); 96 uint64_t lastTime = v8profile->GetStartTime(); 97 for (int i = 0; i < count; i++) { 98 uint64_t ts = v8profile->GetSampleTimestamp(i); 99 array->addItem(static_cast<int>(ts - lastTime)); 100 lastTime = ts; 101 } 102 return array; 103 } 104 105 void flattenNodesTree(v8::Isolate* isolate, const v8::CpuProfileNode* node, 106 protocol::Array<protocol::Profiler::ProfileNode>* list) { 107 list->addItem(buildInspectorObjectFor(isolate, node)); 108 const int childrenCount = node->GetChildrenCount(); 109 for (int i = 0; i < childrenCount; i++) 110 flattenNodesTree(isolate, node->GetChild(i), list); 111 } 112 113 std::unique_ptr<protocol::Profiler::Profile> createCPUProfile( 114 v8::Isolate* isolate, v8::CpuProfile* v8profile) { 115 auto nodes = protocol::Array<protocol::Profiler::ProfileNode>::create(); 116 flattenNodesTree(isolate, v8profile->GetTopDownRoot(), nodes.get()); 117 return protocol::Profiler::Profile::create() 118 .setNodes(std::move(nodes)) 119 .setStartTime(static_cast<double>(v8profile->GetStartTime())) 120 .setEndTime(static_cast<double>(v8profile->GetEndTime())) 121 .setSamples(buildInspectorObjectForSamples(v8profile)) 122 .setTimeDeltas(buildInspectorObjectForTimestamps(v8profile)) 123 .build(); 124 } 125 126 std::unique_ptr<protocol::Debugger::Location> currentDebugLocation( 127 V8InspectorImpl* inspector) { 128 std::unique_ptr<V8StackTraceImpl> callStack = 129 inspector->debugger()->captureStackTrace(false /* fullStack */); 130 auto location = protocol::Debugger::Location::create() 131 .setScriptId(toString16(callStack->topScriptId())) 132 .setLineNumber(callStack->topLineNumber()) 133 .build(); 134 location->setColumnNumber(callStack->topColumnNumber()); 135 return location; 136 } 137 138 volatile int s_lastProfileId = 0; 139 140 } // namespace 141 142 class V8ProfilerAgentImpl::ProfileDescriptor { 143 public: 144 ProfileDescriptor(const String16& id, const String16& title) 145 : m_id(id), m_title(title) {} 146 String16 m_id; 147 String16 m_title; 148 }; 149 150 V8ProfilerAgentImpl::V8ProfilerAgentImpl( 151 V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel, 152 protocol::DictionaryValue* state) 153 : m_session(session), 154 m_isolate(m_session->inspector()->isolate()), 155 m_profiler(nullptr), 156 m_state(state), 157 m_frontend(frontendChannel), 158 m_enabled(false), 159 m_recordingCPUProfile(false) {} 160 161 V8ProfilerAgentImpl::~V8ProfilerAgentImpl() { 162 if (m_profiler) m_profiler->Dispose(); 163 } 164 165 void V8ProfilerAgentImpl::consoleProfile(const String16& title) { 166 if (!m_enabled) return; 167 String16 id = nextProfileId(); 168 m_startedProfiles.push_back(ProfileDescriptor(id, title)); 169 startProfiling(id); 170 m_frontend.consoleProfileStarted( 171 id, currentDebugLocation(m_session->inspector()), title); 172 } 173 174 void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) { 175 if (!m_enabled) return; 176 String16 id; 177 String16 resolvedTitle; 178 // Take last started profile if no title was passed. 179 if (title.isEmpty()) { 180 if (m_startedProfiles.empty()) return; 181 id = m_startedProfiles.back().m_id; 182 resolvedTitle = m_startedProfiles.back().m_title; 183 m_startedProfiles.pop_back(); 184 } else { 185 for (size_t i = 0; i < m_startedProfiles.size(); i++) { 186 if (m_startedProfiles[i].m_title == title) { 187 resolvedTitle = title; 188 id = m_startedProfiles[i].m_id; 189 m_startedProfiles.erase(m_startedProfiles.begin() + i); 190 break; 191 } 192 } 193 if (id.isEmpty()) return; 194 } 195 std::unique_ptr<protocol::Profiler::Profile> profile = 196 stopProfiling(id, true); 197 if (!profile) return; 198 std::unique_ptr<protocol::Debugger::Location> location = 199 currentDebugLocation(m_session->inspector()); 200 m_frontend.consoleProfileFinished(id, std::move(location), std::move(profile), 201 resolvedTitle); 202 } 203 204 Response V8ProfilerAgentImpl::enable() { 205 if (m_enabled) return Response::OK(); 206 m_enabled = true; 207 DCHECK(!m_profiler); 208 m_profiler = v8::CpuProfiler::New(m_isolate); 209 m_state->setBoolean(ProfilerAgentState::profilerEnabled, true); 210 return Response::OK(); 211 } 212 213 Response V8ProfilerAgentImpl::disable() { 214 if (!m_enabled) return Response::OK(); 215 for (size_t i = m_startedProfiles.size(); i > 0; --i) 216 stopProfiling(m_startedProfiles[i - 1].m_id, false); 217 m_startedProfiles.clear(); 218 stop(nullptr); 219 m_profiler->Dispose(); 220 m_profiler = nullptr; 221 m_enabled = false; 222 m_state->setBoolean(ProfilerAgentState::profilerEnabled, false); 223 return Response::OK(); 224 } 225 226 Response V8ProfilerAgentImpl::setSamplingInterval(int interval) { 227 if (m_recordingCPUProfile) 228 return Response::Error("Cannot change sampling interval when profiling."); 229 m_state->setInteger(ProfilerAgentState::samplingInterval, interval); 230 m_profiler->SetSamplingInterval(interval); 231 return Response::OK(); 232 } 233 234 void V8ProfilerAgentImpl::restore() { 235 DCHECK(!m_enabled); 236 if (!m_state->booleanProperty(ProfilerAgentState::profilerEnabled, false)) 237 return; 238 m_enabled = true; 239 DCHECK(!m_profiler); 240 m_profiler = v8::CpuProfiler::New(m_isolate); 241 int interval = 0; 242 m_state->getInteger(ProfilerAgentState::samplingInterval, &interval); 243 if (interval) m_profiler->SetSamplingInterval(interval); 244 if (m_state->booleanProperty(ProfilerAgentState::userInitiatedProfiling, 245 false)) { 246 start(); 247 } 248 } 249 250 Response V8ProfilerAgentImpl::start() { 251 if (m_recordingCPUProfile) return Response::OK(); 252 if (!m_enabled) return Response::Error("Profiler is not enabled"); 253 m_recordingCPUProfile = true; 254 m_frontendInitiatedProfileId = nextProfileId(); 255 startProfiling(m_frontendInitiatedProfileId); 256 m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, true); 257 return Response::OK(); 258 } 259 260 Response V8ProfilerAgentImpl::stop( 261 std::unique_ptr<protocol::Profiler::Profile>* profile) { 262 if (!m_recordingCPUProfile) 263 return Response::Error("No recording profiles found"); 264 m_recordingCPUProfile = false; 265 std::unique_ptr<protocol::Profiler::Profile> cpuProfile = 266 stopProfiling(m_frontendInitiatedProfileId, !!profile); 267 if (profile) { 268 *profile = std::move(cpuProfile); 269 if (!profile->get()) return Response::Error("Profile is not found"); 270 } 271 m_frontendInitiatedProfileId = String16(); 272 m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, false); 273 return Response::OK(); 274 } 275 276 String16 V8ProfilerAgentImpl::nextProfileId() { 277 return String16::fromInteger( 278 v8::base::NoBarrier_AtomicIncrement(&s_lastProfileId, 1)); 279 } 280 281 void V8ProfilerAgentImpl::startProfiling(const String16& title) { 282 v8::HandleScope handleScope(m_isolate); 283 m_profiler->StartProfiling(toV8String(m_isolate, title), true); 284 } 285 286 std::unique_ptr<protocol::Profiler::Profile> V8ProfilerAgentImpl::stopProfiling( 287 const String16& title, bool serialize) { 288 v8::HandleScope handleScope(m_isolate); 289 v8::CpuProfile* profile = 290 m_profiler->StopProfiling(toV8String(m_isolate, title)); 291 if (!profile) return nullptr; 292 std::unique_ptr<protocol::Profiler::Profile> result; 293 if (serialize) result = createCPUProfile(m_isolate, profile); 294 profile->Delete(); 295 return result; 296 } 297 298 bool V8ProfilerAgentImpl::isRecording() const { 299 return m_recordingCPUProfile || !m_startedProfiles.empty(); 300 } 301 302 bool V8ProfilerAgentImpl::idleStarted() { 303 if (m_profiler) m_profiler->SetIdle(true); 304 return m_profiler; 305 } 306 307 bool V8ProfilerAgentImpl::idleFinished() { 308 if (m_profiler) m_profiler->SetIdle(false); 309 return m_profiler; 310 } 311 312 void V8ProfilerAgentImpl::collectSample() { 313 if (m_profiler) m_profiler->CollectSample(); 314 } 315 316 } // namespace v8_inspector 317