Flutter Engine
The Flutter Engine
source_report.cc
Go to the documentation of this file.
1// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4#include "vm/globals.h"
5#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
6#include "vm/source_report.h"
7
8#include "vm/bit_vector.h"
11#include "vm/isolate.h"
12#include "vm/kernel_loader.h"
13#include "vm/object.h"
14#include "vm/object_store.h"
15#include "vm/profiler.h"
16#include "vm/profiler_service.h"
17
18namespace dart {
19
20const char* SourceReport::kCallSitesStr = "_CallSites";
21const char* SourceReport::kCoverageStr = "Coverage";
22const char* SourceReport::kPossibleBreakpointsStr = "PossibleBreakpoints";
23const char* SourceReport::kProfileStr = "_Profile";
24const char* SourceReport::kBranchCoverageStr = "BranchCoverage";
25
26SourceReport::SourceReport(intptr_t report_set,
27 CompileMode compile_mode,
28 bool report_lines)
29 : report_set_(report_set),
30 compile_mode_(compile_mode),
31 report_lines_(report_lines),
32 library_filters_(GrowableObjectArray::Handle()),
33 libraries_already_compiled_(nullptr),
34 thread_(nullptr),
35 script_(nullptr),
36 start_pos_(TokenPosition::kMinSource),
37 end_pos_(TokenPosition::kMaxSource),
38 next_script_index_(0) {}
39
40SourceReport::SourceReport(intptr_t report_set,
41 const GrowableObjectArray& library_filters,
42 ZoneCStringSet* libraries_already_compiled,
43 CompileMode compile_mode,
44 bool report_lines)
45 : report_set_(report_set),
46 compile_mode_(compile_mode),
47 report_lines_(report_lines),
48 library_filters_(library_filters),
49 libraries_already_compiled_(libraries_already_compiled),
50 thread_(nullptr),
51 script_(nullptr),
52 start_pos_(TokenPosition::kMinSource),
53 end_pos_(TokenPosition::kMaxSource),
54 next_script_index_(0) {}
55
57 ClearScriptTable();
58}
59
60void SourceReport::ClearScriptTable() {
61 // Delete entries from script_table_ as it has the unfiltered list.
63 script_table_.GetIterator();
64 ScriptTableTrait::Pair* pair;
65 while ((pair = iter.Next()) != nullptr) {
66 delete ScriptTableTrait::ValueOf(*pair);
67 }
68 script_table_.Clear();
69
70 for (intptr_t i = 0; i < script_table_entries_.length(); i++) {
71 script_table_entries_[i] = nullptr;
72 }
73 script_table_entries_.Clear();
74
75 next_script_index_ = 0;
76}
77
78void SourceReport::Init(Thread* thread,
79 const Script* script,
80 TokenPosition start_pos,
81 TokenPosition end_pos) {
82 thread_ = thread;
83 script_ = script;
84 start_pos_ = TokenPosition::Max(start_pos, TokenPosition::kMinSource);
86 ClearScriptTable();
87 if (IsReportRequested(kProfile)) {
88 // Build the profile.
89 SampleFilter samplesForIsolate(thread_->isolate()->main_port(),
90 Thread::kMutatorTask, -1, -1);
91 profile_.Build(thread, thread->isolate(), &samplesForIsolate,
93 }
94}
95
96bool SourceReport::IsReportRequested(ReportKind report_kind) {
97 return (report_set_ & report_kind) != 0;
98}
99
100bool SourceReport::ShouldSkipFunction(const Function& func) {
101 // TODO(32315): Verify that the check is still needed after the issue is
102 // resolved.
103 if (!func.token_pos().IsReal() || !func.end_token_pos().IsReal()) {
104 // At least one of the token positions is not known.
105 return true;
106 }
107
108 if (script_ != nullptr && !script_->IsNull()) {
109 if (func.script() != script_->ptr()) {
110 // The function is from the wrong script.
111 return true;
112 }
113 if ((func.end_token_pos() < start_pos_) || (func.token_pos() > end_pos_)) {
114 // The function does not intersect with the requested token range.
115 return true;
116 }
117 }
118
119 // These don't have unoptimized code and are only used for synthetic stubs.
120 if (func.ForceOptimize()) return true;
121
122 switch (func.kind()) {
123 case UntaggedFunction::kRegularFunction:
124 case UntaggedFunction::kClosureFunction:
125 case UntaggedFunction::kImplicitClosureFunction:
126 case UntaggedFunction::kImplicitStaticGetter:
127 case UntaggedFunction::kFieldInitializer:
128 case UntaggedFunction::kGetterFunction:
129 case UntaggedFunction::kSetterFunction:
130 case UntaggedFunction::kConstructor:
131 break;
132 default:
133 return true;
134 }
135 if (func.is_abstract() || func.IsImplicitConstructor() ||
136 func.is_synthetic() || func.is_redirecting_factory()) {
137 return true;
138 }
139 if (func.IsNonImplicitClosureFunction() &&
140 (func.context_scope() == ContextScope::null())) {
141 // TODO(iposva): This can arise if we attempt to compile an inner function
142 // before we have compiled its enclosing function or if the enclosing
143 // function failed to compile.
144 return true;
145 }
146
147 // There is an idiom where static utility classes are given a private
148 // constructor to prevent the class from being instantiated. Ignore these
149 // constructors so that they don't lower the coverage rate. See #47021.
150 SafepointReadRwLocker ml(thread_, thread_->isolate_group()->program_lock());
151 if (func.kind() == UntaggedFunction::kConstructor &&
152 func.NumParameters() == func.NumImplicitParameters() &&
153 func.IsPrivate()) {
154 // Check that the class has no non-static members and no subclasses.
155 Class& cls = Class::Handle(func.Owner());
156 GrowableObjectArray& subclasses =
157 GrowableObjectArray::Handle(cls.direct_subclasses());
158 if (cls.is_abstract() && !cls.HasInstanceFields() &&
159 (subclasses.IsNull() || subclasses.Length() == 0)) {
160 // Check that the constructor is the only non-static function.
161 Array& clsFuncs = Array::Handle(cls.functions());
162 Function& otherFunc = Function::Handle();
163 intptr_t numNonStaticFunctions = 0;
164 for (intptr_t i = 0; i < clsFuncs.Length(); ++i) {
165 otherFunc ^= clsFuncs.At(i);
166 if (!otherFunc.IsStaticFunction()) {
167 ++numNonStaticFunctions;
168 }
169 }
170 if (numNonStaticFunctions == 1) {
171 return true;
172 }
173 }
174 }
175
176 // Enum constructors cannot be invoked by the user, so ignore them.
177 if (func.IsGenerativeConstructor()) {
178 Class& cls = Class::Handle(func.Owner());
179 if (cls.is_enum_class()) {
180 return true;
181 }
182 }
183
184 return false;
185}
186
187bool SourceReport::ShouldSkipField(const Field& field) {
188 if (!field.token_pos().IsReal() || !field.end_token_pos().IsReal()) {
189 // At least one of the token positions is not known.
190 return true;
191 }
192
193 if (script_ != nullptr && !script_->IsNull()) {
194 if (field.Script() != script_->ptr()) {
195 // The field is from the wrong script.
196 return true;
197 }
198 if ((field.end_token_pos() < start_pos_) ||
199 (field.token_pos() > end_pos_)) {
200 // The field does not intersect with the requested token range.
201 return true;
202 }
203 }
204 return false;
205}
206
207bool SourceReport::IsLibraryAlreadyCompiled(const Library& lib) {
208 if (libraries_already_compiled_ == nullptr) return false;
209 const char* url = String::ToCString(thread(), lib.url());
210 if (url == nullptr) return false;
211 return libraries_already_compiled_->Lookup(url) != nullptr;
212}
213
214bool SourceReport::ShouldFiltersIncludeUrl(const String& url) {
215 String& filter = String::Handle(zone());
216 const intptr_t num_filters = library_filters_.Length();
217 for (intptr_t i = 0; i < num_filters; ++i) {
218 filter ^= library_filters_.At(i);
219 if (url.StartsWith(filter)) {
220 return true;
221 }
222 }
223 return false;
224}
225
226bool SourceReport::ShouldFiltersIncludeScript(const Script& script) {
227 if (library_filters_.IsNull()) return true;
228 String& url = String::Handle(zone(), script.url());
229 if (ShouldFiltersIncludeUrl(url)) return true;
230 const Library& lib = Library::Handle(zone(), script.FindLibrary());
231 url ^= lib.url();
232 return ShouldFiltersIncludeUrl(url);
233}
234
235intptr_t SourceReport::GetScriptIndex(const Script& script) {
236 ScriptTableEntry wrapper;
237 const String& url = String::Handle(zone(), script.url());
238 wrapper.key = &url;
239 wrapper.script = &Script::Handle(zone(), script.ptr());
240 ScriptTableEntry* pair = script_table_.LookupValue(&wrapper);
241 if (pair != nullptr) {
242 return pair->index;
243 }
244 ScriptTableEntry* tmp = new ScriptTableEntry();
245 tmp->key = &url;
246 tmp->script = wrapper.script;
247 if (ShouldFiltersIncludeScript(script)) {
248 tmp->index = next_script_index_++;
249 script_table_entries_.Add(tmp);
250 } else {
251 tmp->index = -1;
252 }
253 script_table_.Insert(tmp);
254 ASSERT(script_table_entries_.length() == next_script_index_);
255#if defined(DEBUG)
256 VerifyScriptTable();
257#endif
258 return tmp->index;
259}
260
261#if defined(DEBUG)
262void SourceReport::VerifyScriptTable() {
263 for (intptr_t i = 0; i < script_table_entries_.length(); i++) {
264 const String* url = script_table_entries_[i]->key;
265 const Script* script = script_table_entries_[i]->script;
266 intptr_t index = script_table_entries_[i]->index;
267 ASSERT(i == index);
268 const String& url2 = String::Handle(zone(), script->url());
269 ASSERT(url2.Equals(*url));
270 ScriptTableEntry wrapper;
271 wrapper.key = &url2;
272 wrapper.script = &Script::Handle(zone(), script->ptr());
273 ScriptTableEntry* pair = script_table_.LookupValue(&wrapper);
274 ASSERT(i == pair->index);
275 }
276}
277#endif
278
279bool SourceReport::ScriptIsLoadedByLibrary(const Script& script,
280 const Library& lib) {
281 const Array& scripts = Array::Handle(zone(), lib.LoadedScripts());
282 for (intptr_t j = 0; j < scripts.Length(); j++) {
283 if (scripts.At(j) == script.ptr()) {
284 return true;
285 }
286 }
287 return false;
288}
289
290void SourceReport::PrintCallSitesData(JSONObject* jsobj,
291 const Function& function,
292 const Code& code) {
293 ASSERT(!code.IsNull());
294 const TokenPosition& begin_pos = function.token_pos();
295 const TokenPosition& end_pos = function.end_token_pos();
296 ZoneGrowableArray<const ICData*>* ic_data_array =
297 new (zone()) ZoneGrowableArray<const ICData*>();
298 function.RestoreICDataMap(ic_data_array, false /* clone ic-data */);
299 const PcDescriptors& descriptors =
300 PcDescriptors::Handle(zone(), code.pc_descriptors());
301
302 JSONArray sites(jsobj, "callSites");
303
304 PcDescriptors::Iterator iter(
305 descriptors,
306 UntaggedPcDescriptors::kIcCall | UntaggedPcDescriptors::kUnoptStaticCall);
307 while (iter.MoveNext()) {
308 HANDLESCOPE(thread());
309 ASSERT(iter.DeoptId() < ic_data_array->length());
310 const ICData* ic_data = (*ic_data_array)[iter.DeoptId()];
311 if (ic_data != nullptr) {
312 const TokenPosition& token_pos = iter.TokenPos();
313 if (!token_pos.IsWithin(begin_pos, end_pos)) {
314 // Does not correspond to a valid source position.
315 continue;
316 }
317 ic_data->PrintToJSONArray(sites, token_pos);
318 }
319 }
320}
321
322intptr_t SourceReport::GetTokenPosOrLine(const Script& script,
323 const TokenPosition& token_pos) {
324 if (!report_lines_) {
325 return token_pos.Pos();
326 }
327 intptr_t line = -1;
328 const bool found = script.GetTokenLocation(token_pos, &line);
329 ASSERT(found);
330 return line;
331}
332
333void SourceReport::PrintCoverageData(JSONObject* jsobj,
334 const Function& function,
335 const Code& code,
336 bool report_branch_coverage) {
337 ASSERT(!code.IsNull());
338 const TokenPosition& begin_pos = function.token_pos();
339 const TokenPosition& end_pos = function.end_token_pos();
340
341 const Script& script = Script::Handle(zone(), function.script());
342
343 const int kCoverageNone = 0;
344 const int kCoverageMiss = 1;
345 const int kCoverageHit = 2;
346
347 intptr_t func_length = function.SourceSize() + 1;
348 GrowableArray<char> coverage(func_length);
349 coverage.SetLength(func_length);
350 for (int i = 0; i < func_length; i++) {
351 coverage[i] = kCoverageNone;
352 }
353
354 if (function.WasExecuted()) {
355 coverage[0] = kCoverageHit;
356 } else {
357 coverage[0] = kCoverageMiss;
358 }
359
360 auto update_coverage = [&](TokenPosition token_pos, bool was_executed) {
361 if (!(token_pos.IsReal() && token_pos.IsWithin(begin_pos, end_pos))) {
362 return;
363 }
364
365 const intptr_t token_offset = token_pos.Pos() - begin_pos.Pos();
366 if (was_executed) {
367 coverage[token_offset] = kCoverageHit;
368 } else {
369 if (coverage[token_offset] == kCoverageNone) {
370 coverage[token_offset] = kCoverageMiss;
371 }
372 }
373 };
374
375 // Merge the coverage from coverage_array attached to the function.
376 const Array& coverage_array = Array::Handle(function.GetCoverageArray());
377 if (!coverage_array.IsNull()) {
378 for (intptr_t i = 0; i < coverage_array.Length(); i += 2) {
379 bool is_branch_coverage;
380 const TokenPosition token_pos = TokenPosition::DecodeCoveragePosition(
381 Smi::Value(Smi::RawCast(coverage_array.At(i))), &is_branch_coverage);
382 if (is_branch_coverage == report_branch_coverage) {
383 const bool was_executed =
384 Smi::Value(Smi::RawCast(coverage_array.At(i + 1))) != 0;
385 update_coverage(token_pos, was_executed);
386 }
387 }
388 }
389
390 JSONObject cov(jsobj, report_branch_coverage ? "branchCoverage" : "coverage");
391 {
392 JSONArray hits(&cov, "hits");
393 TokenPosition pos = begin_pos;
394 for (int i = 0; i < func_length; i++) {
395 if (coverage[i] == kCoverageHit) {
396 // Add the token position or line number of the hit.
397 hits.AddValue(GetTokenPosOrLine(script, pos));
398 }
399 pos = pos.Next();
400 }
401 }
402 {
403 JSONArray misses(&cov, "misses");
404 TokenPosition pos = begin_pos;
405 for (int i = 0; i < func_length; i++) {
406 if (coverage[i] == kCoverageMiss) {
407 // Add the token position or line number of the miss.
408 misses.AddValue(GetTokenPosOrLine(script, pos));
409 }
410 pos = pos.Next();
411 }
412 }
413}
414
415void SourceReport::PrintPossibleBreakpointsData(JSONObject* jsobj,
416 const Function& func,
417 const Code& code) {
418 const TokenPosition& begin_pos = func.token_pos();
419 const TokenPosition& end_pos = func.end_token_pos();
420 intptr_t func_length = func.SourceSize() + 1;
421
422 BitVector possible(zone(), func_length);
423
424 ASSERT(!code.IsNull());
425
426 const uint8_t kSafepointKind = (UntaggedPcDescriptors::kIcCall |
427 UntaggedPcDescriptors::kUnoptStaticCall |
428 UntaggedPcDescriptors::kRuntimeCall);
429
430 const PcDescriptors& descriptors =
431 PcDescriptors::Handle(zone(), code.pc_descriptors());
432 const Script& script = Script::Handle(zone(), func.script());
433
434 PcDescriptors::Iterator iter(descriptors, kSafepointKind);
435 while (iter.MoveNext()) {
436 const TokenPosition& token_pos = iter.TokenPos();
437 if (!token_pos.IsWithin(begin_pos, end_pos)) {
438 // Does not correspond to a valid source position.
439 continue;
440 }
441 intptr_t token_offset = token_pos.Pos() - begin_pos.Pos();
442 possible.Add(token_offset);
443 }
444
445 JSONArray bpts(jsobj, "possibleBreakpoints");
446 TokenPosition pos = begin_pos;
447 for (int i = 0; i < func_length; i++) {
448 if (possible.Contains(i)) {
449 // Add the token position or line number.
450 bpts.AddValue(GetTokenPosOrLine(script, pos));
451 }
452 pos = pos.Next();
453 }
454}
455
456void SourceReport::PrintProfileData(JSONObject* jsobj,
457 ProfileFunction* profile_function) {
458 ASSERT(profile_function != nullptr);
459 ASSERT(profile_function->NumSourcePositions() > 0);
460
461 {
462 JSONObject profile(jsobj, "profile");
463
464 {
465 JSONObject profileData(&profile, "metadata");
466 profileData.AddProperty("sampleCount", profile_.sample_count());
467 }
468
469 // Positions.
470 {
471 JSONArray positions(&profile, "positions");
472 for (intptr_t i = 0; i < profile_function->NumSourcePositions(); i++) {
473 const ProfileFunctionSourcePosition& position =
474 profile_function->GetSourcePosition(i);
475 if (position.token_pos().IsReal()) {
476 // Add as an integer.
477 positions.AddValue(position.token_pos().Pos());
478 } else {
479 // Add as a string.
480 positions.AddValue(position.token_pos().ToCString());
481 }
482 }
483 }
484
485 // Exclusive ticks.
486 {
487 JSONArray exclusiveTicks(&profile, "exclusiveTicks");
488 for (intptr_t i = 0; i < profile_function->NumSourcePositions(); i++) {
489 const ProfileFunctionSourcePosition& position =
490 profile_function->GetSourcePosition(i);
491 exclusiveTicks.AddValue(position.exclusive_ticks());
492 }
493 }
494 // Inclusive ticks.
495 {
496 JSONArray inclusiveTicks(&profile, "inclusiveTicks");
497 for (intptr_t i = 0; i < profile_function->NumSourcePositions(); i++) {
498 const ProfileFunctionSourcePosition& position =
499 profile_function->GetSourcePosition(i);
500 inclusiveTicks.AddValue(position.inclusive_ticks());
501 }
502 }
503 }
504}
505
506void SourceReport::PrintScriptTable(JSONArray* scripts) {
507 for (intptr_t i = 0; i < script_table_entries_.length(); i++) {
508 const Script* script = script_table_entries_[i]->script;
509 scripts->AddValue(*script);
510 }
511}
512
513void SourceReport::VisitFunction(JSONArray* jsarr,
514 const Function& func,
515 CompileMode compile_mode) {
516 if (ShouldSkipFunction(func)) {
517 return;
518 }
519
520 const Script& script = Script::Handle(zone(), func.script());
521 const TokenPosition begin_pos = func.token_pos();
522 const TokenPosition end_pos = func.end_token_pos();
523
524 const intptr_t script_index = GetScriptIndex(script);
525 if (script_index < 0) {
526 return;
527 }
528
529 Code& code = Code::Handle(zone(), func.unoptimized_code());
530 if (code.IsNull()) {
531 if (func.HasCode() || (compile_mode == kForceCompile)) {
532 const Error& err =
534 if (!err.IsNull()) {
535 // Emit an uncompiled range for this function with error information.
536 JSONObject range(jsarr);
537 range.AddProperty("scriptIndex", script_index);
538 range.AddProperty("startPos", begin_pos);
539 range.AddProperty("endPos", end_pos);
540 range.AddProperty("compiled", false);
541 range.AddProperty("error", err);
542 return;
543 }
544 code = func.unoptimized_code();
545 } else {
546 // This function has not been compiled yet.
547 JSONObject range(jsarr);
548 range.AddProperty("scriptIndex", script_index);
549 range.AddProperty("startPos", begin_pos);
550 range.AddProperty("endPos", end_pos);
551 range.AddProperty("compiled", false);
552 return;
553 }
554 }
555 ASSERT(!code.IsNull());
556
557 JSONObject range(jsarr);
558 range.AddProperty("scriptIndex", script_index);
559 range.AddProperty("startPos", begin_pos);
560 range.AddProperty("endPos", end_pos);
561 range.AddProperty("compiled", true);
562
563 if (IsReportRequested(kCallSites)) {
564 PrintCallSitesData(&range, func, code);
565 }
566 if (IsReportRequested(kCoverage)) {
567 PrintCoverageData(&range, func, code, /* report_branch_coverage */ false);
568 }
569 if (IsReportRequested(kBranchCoverage)) {
570 PrintCoverageData(&range, func, code, /* report_branch_coverage */ true);
571 }
572 if (IsReportRequested(kPossibleBreakpoints)) {
573 PrintPossibleBreakpointsData(&range, func, code);
574 }
575 if (IsReportRequested(kProfile)) {
576 ProfileFunction* profile_function = profile_.FindFunction(func);
577 if ((profile_function != nullptr) &&
578 (profile_function->NumSourcePositions() > 0)) {
579 PrintProfileData(&range, profile_function);
580 }
581 }
582}
583
584void SourceReport::VisitField(JSONArray* jsarr,
585 const Field& field,
586 CompileMode compile_mode) {
587 if (ShouldSkipField(field) || !field.HasInitializerFunction()) return;
588 const Function& func = Function::Handle(field.InitializerFunction());
589 VisitFunction(jsarr, func, compile_mode);
590}
591
592void SourceReport::VisitLibrary(JSONArray* jsarr, const Library& lib) {
593 Class& cls = Class::Handle(zone());
594 Array& functions = Array::Handle(zone());
595 Array& fields = Array::Handle(zone());
596 Function& func = Function::Handle(zone());
597 Field& field = Field::Handle(zone());
598 Script& script = Script::Handle(zone());
599 ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
600 CompileMode compile_mode = compile_mode_;
601 if (compile_mode == kForceCompile && IsLibraryAlreadyCompiled(lib)) {
602 compile_mode = kNoCompile;
603 }
604 while (it.HasNext()) {
605 cls = it.GetNextClass();
606 if (!cls.is_finalized()) {
607 if (compile_mode == kForceCompile) {
608 Error& err = Error::Handle(cls.EnsureIsFinalized(thread()));
609 if (!err.IsNull()) {
610 // Emit an uncompiled range for this class with error information.
611 script = cls.script();
612 const intptr_t script_index = GetScriptIndex(script);
613 if (script_index < 0) {
614 continue;
615 }
616 JSONObject range(jsarr);
617 range.AddProperty("scriptIndex", script_index);
618 range.AddProperty("startPos", cls.token_pos());
619 range.AddProperty("endPos", cls.end_token_pos());
620 range.AddProperty("compiled", false);
621 range.AddProperty("error", err);
622 continue;
623 }
624 ASSERT(cls.is_finalized());
625 } else {
626 cls.EnsureDeclarationLoaded();
627 // Emit one range for the whole uncompiled class.
628 script = cls.script();
629 const intptr_t script_index = GetScriptIndex(script);
630 if (script_index < 0) {
631 continue;
632 }
633 JSONObject range(jsarr);
634 range.AddProperty("scriptIndex", script_index);
635 range.AddProperty("startPos", cls.token_pos());
636 range.AddProperty("endPos", cls.end_token_pos());
637 range.AddProperty("compiled", false);
638 continue;
639 }
640 }
641
642 functions = cls.current_functions();
643 for (int i = 0; i < functions.Length(); i++) {
644 func ^= functions.At(i);
645 // Skip getter functions of static const field.
646 if (func.kind() == UntaggedFunction::kImplicitStaticGetter) {
647 field ^= func.accessor_field();
648 if (field.is_const() && field.is_static()) {
649 continue;
650 }
651 }
652 VisitFunction(jsarr, func, compile_mode);
653 }
654
655 fields = cls.fields();
656 for (intptr_t i = 0; i < fields.Length(); i++) {
657 field ^= fields.At(i);
658 VisitField(jsarr, field, compile_mode);
659 }
660 }
661}
662
663void SourceReport::VisitClosures(JSONArray* jsarr) {
664 ClosureFunctionsCache::ForAllClosureFunctions([&](const Function& func) {
665 VisitFunction(jsarr, func, compile_mode_);
666 return true; // Continue iteration.
667 });
668}
669
671 const Script& script,
672 TokenPosition start_pos,
673 TokenPosition end_pos) {
674 Init(Thread::Current(), &script, start_pos, end_pos);
675
676 JSONObject report(js);
677 report.AddProperty("type", "SourceReport");
678 {
679 JSONArray ranges(&report, "ranges");
680
682 zone(), thread()->isolate_group()->object_store()->libraries());
683
684 // We only visit the libraries which actually load the specified script.
685 Library& lib = Library::Handle(zone());
686 for (intptr_t i = 0; i < libs.Length(); i++) {
687 lib ^= libs.At(i);
688 if (script.IsNull() || ScriptIsLoadedByLibrary(script, lib)) {
689 VisitLibrary(&ranges, lib);
690 }
691 }
692
693 // Visit all closures for this isolate.
694 VisitClosures(&ranges);
695
696 // Output constant coverage if coverage is requested.
697 if (IsReportRequested(kCoverage)) {
698 // Find all scripts. We need to go though all scripts because a script
699 // (even one we don't want) can add coverage to another library (i.e.
700 // potentially one we want).
702 GrowableArray<ScriptTableEntry*> local_script_table_entries;
703 CollectAllScripts(&local_script_table, &local_script_table_entries);
704 CollectConstConstructorCoverageFromScripts(&local_script_table_entries,
705 &ranges);
706 CleanupCollectedScripts(&local_script_table, &local_script_table_entries);
707 }
708 }
709
710 // Print the script table.
711 JSONArray scripts(&report, "scripts");
712 PrintScriptTable(&scripts);
713}
714
715void SourceReport::CollectAllScripts(
716 DirectChainedHashMap<ScriptTableTrait>* local_script_table,
717 GrowableArray<ScriptTableEntry*>* local_script_table_entries) {
718 ScriptTableEntry wrapper;
720 zone(), thread()->isolate_group()->object_store()->libraries());
721 Library& lib = Library::Handle(zone());
722 Script& scriptRef = Script::Handle(zone());
723 for (int i = 0; i < libs.Length(); i++) {
724 lib ^= libs.At(i);
725 const Array& scripts = Array::Handle(zone(), lib.LoadedScripts());
726 for (intptr_t j = 0; j < scripts.Length(); j++) {
727 scriptRef ^= scripts.At(j);
728 const String& url = String::Handle(zone(), scriptRef.url());
729 wrapper.key = &url;
730 wrapper.script = &Script::Handle(zone(), scriptRef.ptr());
731 ScriptTableEntry* pair = local_script_table->LookupValue(&wrapper);
732 if (pair != nullptr) {
733 // Existing one.
734 continue;
735 }
736 // New one. Insert.
737 ScriptTableEntry* tmp = new ScriptTableEntry();
738 tmp->key = &url;
739 tmp->index = -1;
740 tmp->script = wrapper.script;
741 local_script_table_entries->Add(tmp);
742 local_script_table->Insert(tmp);
743 }
744 }
745}
746
747void SourceReport::CleanupCollectedScripts(
748 DirectChainedHashMap<ScriptTableTrait>* local_script_table,
749 GrowableArray<ScriptTableEntry*>* local_script_table_entries) {
750 for (intptr_t i = 0; i < local_script_table_entries->length(); i++) {
751 delete local_script_table_entries->operator[](i);
752 local_script_table_entries->operator[](i) = nullptr;
753 }
754 local_script_table_entries->Clear();
755 local_script_table->Clear();
756}
757
758void SourceReport::CollectConstConstructorCoverageFromScripts(
759 GrowableArray<ScriptTableEntry*>* local_script_table_entries,
760 JSONArray* ranges) {
761 // Now output the wanted constant coverage.
762 for (intptr_t i = 0; i < local_script_table_entries->length(); i++) {
763 const Script* script = local_script_table_entries->At(i)->script;
764
765 // Whether we want *this* script or not we need to look at the constant
766 // constructor coverage. Any of those could be in a script we *do* want.
767 {
768 Script& scriptRef = Script::Handle(zone());
769 const Array& constructors =
770 Array::Handle(script->CollectConstConstructorCoverageFrom());
771 intptr_t constructors_count = constructors.Length();
772 Function& constructor = Function::Handle(zone());
773 Code& code = Code::Handle(zone());
774 for (intptr_t i = 0; i < constructors_count; i++) {
775 constructor ^= constructors.At(i);
776 // Check if we want coverage for this constructor.
777 if (ShouldSkipFunction(constructor)) {
778 continue;
779 }
780 scriptRef ^= constructor.script();
781 const intptr_t script_index = GetScriptIndex(scriptRef);
782 if (script_index < 0) {
783 continue;
784 }
785 code ^= constructor.unoptimized_code();
786 const TokenPosition begin_pos = constructor.token_pos();
787 const TokenPosition end_pos = constructor.end_token_pos();
788 JSONObject range(ranges);
789 range.AddProperty("scriptIndex", script_index);
790 range.AddProperty("compiled",
791 !code.IsNull()); // Does this make a difference?
792 range.AddProperty("startPos", begin_pos);
793 range.AddProperty("endPos", end_pos);
794
795 JSONObject cov(&range, "coverage");
796 {
797 JSONArray hits(&cov, "hits");
798 hits.AddValue(GetTokenPosOrLine(scriptRef, begin_pos));
799 }
800 {
801 JSONArray misses(&cov, "misses");
802 // No misses
803 }
804 }
805 }
806 }
807}
808
809} // namespace dart
810#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
SkPoint pos
KeyValueTrait::Value LookupValue(typename KeyValueTrait::Key key) const
Definition: hash_map.h:159
void Insert(typename KeyValueTrait::Pair kv)
Definition: hash_map.h:230
KeyValueTrait::Pair * Lookup(typename KeyValueTrait::Key key) const
Definition: hash_map.h:130
Iterator GetIterator() const
Definition: hash_map.h:87
void Add(const T &value)
intptr_t length() const
static void ForAllClosureFunctions(std::function< bool(const Function &)> callback)
static ErrorPtr EnsureUnoptimizedCode(Thread *thread, const Function &function)
Definition: compiler.cc:854
intptr_t Length() const
Definition: object.h:11072
ObjectPtr At(intptr_t index) const
Definition: object.h:11085
SafepointRwLock * program_lock()
Definition: isolate.h:537
Dart_Port main_port() const
Definition: isolate.h:1048
void AddProperty(const char *name, bool b) const
Definition: json_stream.h:395
ArrayPtr LoadedScripts() const
Definition: object.cc:13940
static ObjectPtr null()
Definition: object.h:433
ObjectPtr ptr() const
Definition: object.h:332
virtual const char * ToCString() const
Definition: object.h:366
bool IsNull() const
Definition: object.h:363
static Object & Handle()
Definition: object.h:407
static ObjectPtr RawCast(ObjectPtr obj)
Definition: object.h:325
intptr_t sample_count() const
void Build(Thread *thread, Isolate *isolate, SampleFilter *filter, SampleBlockBuffer *sample_block_buffer)
ProfileFunction * FindFunction(const Function &function)
static SampleBlockBuffer * sample_block_buffer()
Definition: profiler.h:67
StringPtr url() const
Definition: object.h:4932
intptr_t Value() const
Definition: object.h:9990
void PrintJSON(JSONStream *js, const Script &script, TokenPosition start_pos=TokenPosition::kMinSource, TokenPosition end_pos=TokenPosition::kMaxSource)
static const char * kPossibleBreakpointsStr
Definition: source_report.h:35
static const char * kCallSitesStr
Definition: source_report.h:33
static const char * kCoverageStr
Definition: source_report.h:34
static const char * kBranchCoverageStr
Definition: source_report.h:37
SourceReport(intptr_t report_set, CompileMode compile=kNoCompile, bool report_lines=false)
static const char * kProfileStr
Definition: source_report.h:36
@ kMutatorTask
Definition: thread.h:347
static Thread * Current()
Definition: thread.h:362
Isolate * isolate() const
Definition: thread.h:534
IsolateGroup * isolate_group() const
Definition: thread.h:541
static const TokenPosition & Min(const TokenPosition &a, const TokenPosition &b)
static TokenPosition DecodeCoveragePosition(intptr_t encoded_position, bool *is_branch_coverage)
static const TokenPosition & Max(const TokenPosition &a, const TokenPosition &b)
static const TokenPosition kMaxSource
static const TokenPosition kMinSource
#define ASSERT(E)
Dart_NativeFunction function
Definition: fuchsia.cc:51
#define HANDLESCOPE(thread)
Definition: handles.h:321
Definition: dart_vm.cc:33
const uint8_t kSafepointKind
Definition: debugger.cc:1313
scripts
Printing Glyph Map Stats.