• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static const char preciseCoverageStarted[] = "preciseCoverageStarted";
26 }
27 
28 namespace {
29 
30 std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>>
buildInspectorObjectForPositionTicks(const v8::CpuProfileNode * node)31 buildInspectorObjectForPositionTicks(const v8::CpuProfileNode* node) {
32   unsigned lineCount = node->GetHitLineCount();
33   if (!lineCount) return nullptr;
34   auto array = protocol::Array<protocol::Profiler::PositionTickInfo>::create();
35   std::vector<v8::CpuProfileNode::LineTick> entries(lineCount);
36   if (node->GetLineTicks(&entries[0], lineCount)) {
37     for (unsigned i = 0; i < lineCount; i++) {
38       std::unique_ptr<protocol::Profiler::PositionTickInfo> line =
39           protocol::Profiler::PositionTickInfo::create()
40               .setLine(entries[i].line)
41               .setTicks(entries[i].hit_count)
42               .build();
43       array->addItem(std::move(line));
44     }
45   }
46   return array;
47 }
48 
buildInspectorObjectFor(v8::Isolate * isolate,const v8::CpuProfileNode * node)49 std::unique_ptr<protocol::Profiler::ProfileNode> buildInspectorObjectFor(
50     v8::Isolate* isolate, const v8::CpuProfileNode* node) {
51   v8::HandleScope handleScope(isolate);
52   auto callFrame =
53       protocol::Runtime::CallFrame::create()
54           .setFunctionName(toProtocolString(node->GetFunctionName()))
55           .setScriptId(String16::fromInteger(node->GetScriptId()))
56           .setUrl(toProtocolString(node->GetScriptResourceName()))
57           .setLineNumber(node->GetLineNumber() - 1)
58           .setColumnNumber(node->GetColumnNumber() - 1)
59           .build();
60   auto result = protocol::Profiler::ProfileNode::create()
61                     .setCallFrame(std::move(callFrame))
62                     .setHitCount(node->GetHitCount())
63                     .setId(node->GetNodeId())
64                     .build();
65 
66   const int childrenCount = node->GetChildrenCount();
67   if (childrenCount) {
68     auto children = protocol::Array<int>::create();
69     for (int i = 0; i < childrenCount; i++)
70       children->addItem(node->GetChild(i)->GetNodeId());
71     result->setChildren(std::move(children));
72   }
73 
74   const char* deoptReason = node->GetBailoutReason();
75   if (deoptReason && deoptReason[0] && strcmp(deoptReason, "no reason"))
76     result->setDeoptReason(deoptReason);
77 
78   auto positionTicks = buildInspectorObjectForPositionTicks(node);
79   if (positionTicks) result->setPositionTicks(std::move(positionTicks));
80 
81   return result;
82 }
83 
buildInspectorObjectForSamples(v8::CpuProfile * v8profile)84 std::unique_ptr<protocol::Array<int>> buildInspectorObjectForSamples(
85     v8::CpuProfile* v8profile) {
86   auto array = protocol::Array<int>::create();
87   int count = v8profile->GetSamplesCount();
88   for (int i = 0; i < count; i++)
89     array->addItem(v8profile->GetSample(i)->GetNodeId());
90   return array;
91 }
92 
buildInspectorObjectForTimestamps(v8::CpuProfile * v8profile)93 std::unique_ptr<protocol::Array<int>> buildInspectorObjectForTimestamps(
94     v8::CpuProfile* v8profile) {
95   auto array = protocol::Array<int>::create();
96   int count = v8profile->GetSamplesCount();
97   uint64_t lastTime = v8profile->GetStartTime();
98   for (int i = 0; i < count; i++) {
99     uint64_t ts = v8profile->GetSampleTimestamp(i);
100     array->addItem(static_cast<int>(ts - lastTime));
101     lastTime = ts;
102   }
103   return array;
104 }
105 
flattenNodesTree(v8::Isolate * isolate,const v8::CpuProfileNode * node,protocol::Array<protocol::Profiler::ProfileNode> * list)106 void flattenNodesTree(v8::Isolate* isolate, const v8::CpuProfileNode* node,
107                       protocol::Array<protocol::Profiler::ProfileNode>* list) {
108   list->addItem(buildInspectorObjectFor(isolate, node));
109   const int childrenCount = node->GetChildrenCount();
110   for (int i = 0; i < childrenCount; i++)
111     flattenNodesTree(isolate, node->GetChild(i), list);
112 }
113 
createCPUProfile(v8::Isolate * isolate,v8::CpuProfile * v8profile)114 std::unique_ptr<protocol::Profiler::Profile> createCPUProfile(
115     v8::Isolate* isolate, v8::CpuProfile* v8profile) {
116   auto nodes = protocol::Array<protocol::Profiler::ProfileNode>::create();
117   flattenNodesTree(isolate, v8profile->GetTopDownRoot(), nodes.get());
118   return protocol::Profiler::Profile::create()
119       .setNodes(std::move(nodes))
120       .setStartTime(static_cast<double>(v8profile->GetStartTime()))
121       .setEndTime(static_cast<double>(v8profile->GetEndTime()))
122       .setSamples(buildInspectorObjectForSamples(v8profile))
123       .setTimeDeltas(buildInspectorObjectForTimestamps(v8profile))
124       .build();
125 }
126 
currentDebugLocation(V8InspectorImpl * inspector)127 std::unique_ptr<protocol::Debugger::Location> currentDebugLocation(
128     V8InspectorImpl* inspector) {
129   std::unique_ptr<V8StackTraceImpl> callStack =
130       inspector->debugger()->captureStackTrace(false /* fullStack */);
131   auto location = protocol::Debugger::Location::create()
132                       .setScriptId(toString16(callStack->topScriptId()))
133                       .setLineNumber(callStack->topLineNumber())
134                       .build();
135   location->setColumnNumber(callStack->topColumnNumber());
136   return location;
137 }
138 
139 volatile int s_lastProfileId = 0;
140 
141 }  // namespace
142 
143 class V8ProfilerAgentImpl::ProfileDescriptor {
144  public:
ProfileDescriptor(const String16 & id,const String16 & title)145   ProfileDescriptor(const String16& id, const String16& title)
146       : m_id(id), m_title(title) {}
147   String16 m_id;
148   String16 m_title;
149 };
150 
V8ProfilerAgentImpl(V8InspectorSessionImpl * session,protocol::FrontendChannel * frontendChannel,protocol::DictionaryValue * state)151 V8ProfilerAgentImpl::V8ProfilerAgentImpl(
152     V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
153     protocol::DictionaryValue* state)
154     : m_session(session),
155       m_isolate(m_session->inspector()->isolate()),
156       m_state(state),
157       m_frontend(frontendChannel) {}
158 
~V8ProfilerAgentImpl()159 V8ProfilerAgentImpl::~V8ProfilerAgentImpl() {
160   if (m_profiler) m_profiler->Dispose();
161 }
162 
consoleProfile(const String16 & title)163 void V8ProfilerAgentImpl::consoleProfile(const String16& title) {
164   if (!m_enabled) return;
165   String16 id = nextProfileId();
166   m_startedProfiles.push_back(ProfileDescriptor(id, title));
167   startProfiling(id);
168   m_frontend.consoleProfileStarted(
169       id, currentDebugLocation(m_session->inspector()), title);
170 }
171 
consoleProfileEnd(const String16 & title)172 void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) {
173   if (!m_enabled) return;
174   String16 id;
175   String16 resolvedTitle;
176   // Take last started profile if no title was passed.
177   if (title.isEmpty()) {
178     if (m_startedProfiles.empty()) return;
179     id = m_startedProfiles.back().m_id;
180     resolvedTitle = m_startedProfiles.back().m_title;
181     m_startedProfiles.pop_back();
182   } else {
183     for (size_t i = 0; i < m_startedProfiles.size(); i++) {
184       if (m_startedProfiles[i].m_title == title) {
185         resolvedTitle = title;
186         id = m_startedProfiles[i].m_id;
187         m_startedProfiles.erase(m_startedProfiles.begin() + i);
188         break;
189       }
190     }
191     if (id.isEmpty()) return;
192   }
193   std::unique_ptr<protocol::Profiler::Profile> profile =
194       stopProfiling(id, true);
195   if (!profile) return;
196   std::unique_ptr<protocol::Debugger::Location> location =
197       currentDebugLocation(m_session->inspector());
198   m_frontend.consoleProfileFinished(id, std::move(location), std::move(profile),
199                                     resolvedTitle);
200 }
201 
enable()202 Response V8ProfilerAgentImpl::enable() {
203   if (m_enabled) return Response::OK();
204   m_enabled = true;
205   m_state->setBoolean(ProfilerAgentState::profilerEnabled, true);
206   return Response::OK();
207 }
208 
disable()209 Response V8ProfilerAgentImpl::disable() {
210   if (!m_enabled) return Response::OK();
211   for (size_t i = m_startedProfiles.size(); i > 0; --i)
212     stopProfiling(m_startedProfiles[i - 1].m_id, false);
213   m_startedProfiles.clear();
214   stop(nullptr);
215   stopPreciseCoverage();
216   DCHECK(!m_profiler);
217   m_enabled = false;
218   m_state->setBoolean(ProfilerAgentState::profilerEnabled, false);
219   return Response::OK();
220 }
221 
setSamplingInterval(int interval)222 Response V8ProfilerAgentImpl::setSamplingInterval(int interval) {
223   if (m_profiler) {
224     return Response::Error("Cannot change sampling interval when profiling.");
225   }
226   m_state->setInteger(ProfilerAgentState::samplingInterval, interval);
227   return Response::OK();
228 }
229 
restore()230 void V8ProfilerAgentImpl::restore() {
231   DCHECK(!m_enabled);
232   if (!m_state->booleanProperty(ProfilerAgentState::profilerEnabled, false))
233     return;
234   m_enabled = true;
235   DCHECK(!m_profiler);
236   if (m_state->booleanProperty(ProfilerAgentState::userInitiatedProfiling,
237                                false)) {
238     start();
239   }
240   if (m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
241                                false)) {
242     startPreciseCoverage();
243   }
244 }
245 
start()246 Response V8ProfilerAgentImpl::start() {
247   if (m_recordingCPUProfile) return Response::OK();
248   if (!m_enabled) return Response::Error("Profiler is not enabled");
249   m_recordingCPUProfile = true;
250   m_frontendInitiatedProfileId = nextProfileId();
251   startProfiling(m_frontendInitiatedProfileId);
252   m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, true);
253   return Response::OK();
254 }
255 
stop(std::unique_ptr<protocol::Profiler::Profile> * profile)256 Response V8ProfilerAgentImpl::stop(
257     std::unique_ptr<protocol::Profiler::Profile>* profile) {
258   if (!m_recordingCPUProfile) {
259     return Response::Error("No recording profiles found");
260   }
261   m_recordingCPUProfile = false;
262   std::unique_ptr<protocol::Profiler::Profile> cpuProfile =
263       stopProfiling(m_frontendInitiatedProfileId, !!profile);
264   if (profile) {
265     *profile = std::move(cpuProfile);
266     if (!profile->get()) return Response::Error("Profile is not found");
267   }
268   m_frontendInitiatedProfileId = String16();
269   m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, false);
270   return Response::OK();
271 }
272 
startPreciseCoverage()273 Response V8ProfilerAgentImpl::startPreciseCoverage() {
274   if (!m_enabled) return Response::Error("Profiler is not enabled");
275   m_state->setBoolean(ProfilerAgentState::preciseCoverageStarted, true);
276   v8::debug::Coverage::TogglePrecise(m_isolate, true);
277   return Response::OK();
278 }
279 
stopPreciseCoverage()280 Response V8ProfilerAgentImpl::stopPreciseCoverage() {
281   if (!m_enabled) return Response::Error("Profiler is not enabled");
282   m_state->setBoolean(ProfilerAgentState::preciseCoverageStarted, false);
283   v8::debug::Coverage::TogglePrecise(m_isolate, false);
284   return Response::OK();
285 }
286 
287 namespace {
takeCoverage(v8::Isolate * isolate,bool reset_count,std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>> * out_result)288 Response takeCoverage(
289     v8::Isolate* isolate, bool reset_count,
290     std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
291         out_result) {
292   std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>> result =
293       protocol::Array<protocol::Profiler::ScriptCoverage>::create();
294   v8::HandleScope handle_scope(isolate);
295   v8::debug::Coverage coverage =
296       v8::debug::Coverage::Collect(isolate, reset_count);
297   for (size_t i = 0; i < coverage.ScriptCount(); i++) {
298     v8::debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
299     v8::Local<v8::debug::Script> script = script_data.GetScript();
300     std::unique_ptr<protocol::Array<protocol::Profiler::FunctionCoverage>>
301         functions =
302             protocol::Array<protocol::Profiler::FunctionCoverage>::create();
303     for (size_t j = 0; j < script_data.FunctionCount(); j++) {
304       v8::debug::Coverage::FunctionData function_data =
305           script_data.GetFunctionData(j);
306       std::unique_ptr<protocol::Array<protocol::Profiler::CoverageRange>>
307           ranges = protocol::Array<protocol::Profiler::CoverageRange>::create();
308       // At this point we only have per-function coverage data, so there is
309       // only one range per function.
310       ranges->addItem(
311           protocol::Profiler::CoverageRange::create()
312               .setStartLineNumber(function_data.Start().GetLineNumber())
313               .setStartColumnNumber(function_data.Start().GetColumnNumber())
314               .setEndLineNumber(function_data.End().GetLineNumber())
315               .setEndColumnNumber(function_data.End().GetColumnNumber())
316               .setCount(function_data.Count())
317               .build());
318       functions->addItem(
319           protocol::Profiler::FunctionCoverage::create()
320               .setFunctionName(toProtocolString(
321                   function_data.Name().FromMaybe(v8::Local<v8::String>())))
322               .setRanges(std::move(ranges))
323               .build());
324     }
325     String16 url;
326     v8::Local<v8::String> name;
327     if (script->Name().ToLocal(&name) || script->SourceURL().ToLocal(&name)) {
328       url = toProtocolString(name);
329     }
330     result->addItem(protocol::Profiler::ScriptCoverage::create()
331                         .setScriptId(String16::fromInteger(script->Id()))
332                         .setUrl(url)
333                         .setFunctions(std::move(functions))
334                         .build());
335   }
336   *out_result = std::move(result);
337   return Response::OK();
338 }
339 }  // anonymous namespace
340 
takePreciseCoverage(std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>> * out_result)341 Response V8ProfilerAgentImpl::takePreciseCoverage(
342     std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
343         out_result) {
344   if (!m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
345                                 false)) {
346     return Response::Error("Precise coverage has not been started.");
347   }
348   return takeCoverage(m_isolate, true, out_result);
349 }
350 
getBestEffortCoverage(std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>> * out_result)351 Response V8ProfilerAgentImpl::getBestEffortCoverage(
352     std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
353         out_result) {
354   return takeCoverage(m_isolate, false, out_result);
355 }
356 
nextProfileId()357 String16 V8ProfilerAgentImpl::nextProfileId() {
358   return String16::fromInteger(
359       v8::base::NoBarrier_AtomicIncrement(&s_lastProfileId, 1));
360 }
361 
startProfiling(const String16 & title)362 void V8ProfilerAgentImpl::startProfiling(const String16& title) {
363   v8::HandleScope handleScope(m_isolate);
364   if (!m_startedProfilesCount) {
365     DCHECK(!m_profiler);
366     m_profiler = v8::CpuProfiler::New(m_isolate);
367     m_profiler->SetIdle(m_idle);
368     int interval =
369         m_state->integerProperty(ProfilerAgentState::samplingInterval, 0);
370     if (interval) m_profiler->SetSamplingInterval(interval);
371   }
372   ++m_startedProfilesCount;
373   m_profiler->StartProfiling(toV8String(m_isolate, title), true);
374 }
375 
stopProfiling(const String16 & title,bool serialize)376 std::unique_ptr<protocol::Profiler::Profile> V8ProfilerAgentImpl::stopProfiling(
377     const String16& title, bool serialize) {
378   v8::HandleScope handleScope(m_isolate);
379   v8::CpuProfile* profile =
380       m_profiler->StopProfiling(toV8String(m_isolate, title));
381   std::unique_ptr<protocol::Profiler::Profile> result;
382   if (profile) {
383     if (serialize) result = createCPUProfile(m_isolate, profile);
384     profile->Delete();
385   }
386   --m_startedProfilesCount;
387   if (!m_startedProfilesCount) {
388     m_profiler->Dispose();
389     m_profiler = nullptr;
390   }
391   return result;
392 }
393 
idleStarted()394 bool V8ProfilerAgentImpl::idleStarted() {
395   m_idle = true;
396   if (m_profiler) m_profiler->SetIdle(m_idle);
397   return m_profiler;
398 }
399 
idleFinished()400 bool V8ProfilerAgentImpl::idleFinished() {
401   m_idle = false;
402   if (m_profiler) m_profiler->SetIdle(m_idle);
403   return m_profiler;
404 }
405 
406 }  // namespace v8_inspector
407