Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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 ZoneGrowableArray<const ICData*>* ic_data_array =
342 new (zone()) ZoneGrowableArray<const ICData*>();
343 function.RestoreICDataMap(ic_data_array, false /* clone ic-data */);
344 const PcDescriptors& descriptors =
345 PcDescriptors::Handle(zone(), code.pc_descriptors());
346 const Script& script = Script::Handle(zone(), function.script());
347
348 const int kCoverageNone = 0;
349 const int kCoverageMiss = 1;
350 const int kCoverageHit = 2;
351
352 intptr_t func_length = function.SourceSize() + 1;
353 GrowableArray<char> coverage(func_length);
354 coverage.SetLength(func_length);
355 for (int i = 0; i < func_length; i++) {
356 coverage[i] = kCoverageNone;
357 }
358
359 if (function.WasExecuted()) {
360 coverage[0] = kCoverageHit;
361 } else {
362 coverage[0] = kCoverageMiss;
363 }
364
365 auto update_coverage = [&](TokenPosition token_pos, bool was_executed) {
366 if (!(token_pos.IsReal() && token_pos.IsWithin(begin_pos, end_pos))) {
367 return;
368 }
369
370 const intptr_t token_offset = token_pos.Pos() - begin_pos.Pos();
371 if (was_executed) {
372 coverage[token_offset] = kCoverageHit;
373 } else {
374 if (coverage[token_offset] == kCoverageNone) {
375 coverage[token_offset] = kCoverageMiss;
376 }
377 }
378 };
379
380 if (!report_branch_coverage) {
381 PcDescriptors::Iterator iter(descriptors,
382 UntaggedPcDescriptors::kIcCall |
383 UntaggedPcDescriptors::kUnoptStaticCall);
384 while (iter.MoveNext()) {
385 HANDLESCOPE(thread());
386 ASSERT(iter.DeoptId() < ic_data_array->length());
387 const ICData* ic_data = (*ic_data_array)[iter.DeoptId()];
388 if (ic_data != nullptr) {
389 const TokenPosition& token_pos = iter.TokenPos();
390 update_coverage(token_pos, ic_data->AggregateCount() > 0);
391 }
392 }
393 }
394
395 // Merge the coverage from coverage_array attached to the function.
396 const Array& coverage_array = Array::Handle(function.GetCoverageArray());
397 if (!coverage_array.IsNull()) {
398 for (intptr_t i = 0; i < coverage_array.Length(); i += 2) {
399 bool is_branch_coverage;
400 const TokenPosition token_pos = TokenPosition::DecodeCoveragePosition(
401 Smi::Value(Smi::RawCast(coverage_array.At(i))), &is_branch_coverage);
402 if (is_branch_coverage == report_branch_coverage) {
403 const bool was_executed =
404 Smi::Value(Smi::RawCast(coverage_array.At(i + 1))) != 0;
405 update_coverage(token_pos, was_executed);
406 }
407 }
408 }
409
410 JSONObject cov(jsobj, report_branch_coverage ? "branchCoverage" : "coverage");
411 {
412 JSONArray hits(&cov, "hits");
413 TokenPosition pos = begin_pos;
414 for (int i = 0; i < func_length; i++) {
415 if (coverage[i] == kCoverageHit) {
416 // Add the token position or line number of the hit.
417 hits.AddValue(GetTokenPosOrLine(script, pos));
418 }
419 pos = pos.Next();
420 }
421 }
422 {
423 JSONArray misses(&cov, "misses");
424 TokenPosition pos = begin_pos;
425 for (int i = 0; i < func_length; i++) {
426 if (coverage[i] == kCoverageMiss) {
427 // Add the token position or line number of the miss.
428 misses.AddValue(GetTokenPosOrLine(script, pos));
429 }
430 pos = pos.Next();
431 }
432 }
433}
434
435void SourceReport::PrintPossibleBreakpointsData(JSONObject* jsobj,
436 const Function& func,
437 const Code& code) {
438 const TokenPosition& begin_pos = func.token_pos();
439 const TokenPosition& end_pos = func.end_token_pos();
440 intptr_t func_length = func.SourceSize() + 1;
441
442 BitVector possible(zone(), func_length);
443
444 ASSERT(!code.IsNull());
445
446 const uint8_t kSafepointKind = (UntaggedPcDescriptors::kIcCall |
447 UntaggedPcDescriptors::kUnoptStaticCall |
448 UntaggedPcDescriptors::kRuntimeCall);
449
450 const PcDescriptors& descriptors =
451 PcDescriptors::Handle(zone(), code.pc_descriptors());
452 const Script& script = Script::Handle(zone(), func.script());
453
454 PcDescriptors::Iterator iter(descriptors, kSafepointKind);
455 while (iter.MoveNext()) {
456 const TokenPosition& token_pos = iter.TokenPos();
457 if (!token_pos.IsWithin(begin_pos, end_pos)) {
458 // Does not correspond to a valid source position.
459 continue;
460 }
461 intptr_t token_offset = token_pos.Pos() - begin_pos.Pos();
462 possible.Add(token_offset);
463 }
464
465 JSONArray bpts(jsobj, "possibleBreakpoints");
466 TokenPosition pos = begin_pos;
467 for (int i = 0; i < func_length; i++) {
468 if (possible.Contains(i)) {
469 // Add the token position or line number.
470 bpts.AddValue(GetTokenPosOrLine(script, pos));
471 }
472 pos = pos.Next();
473 }
474}
475
476void SourceReport::PrintProfileData(JSONObject* jsobj,
477 ProfileFunction* profile_function) {
478 ASSERT(profile_function != nullptr);
479 ASSERT(profile_function->NumSourcePositions() > 0);
480
481 {
482 JSONObject profile(jsobj, "profile");
483
484 {
485 JSONObject profileData(&profile, "metadata");
486 profileData.AddProperty("sampleCount", profile_.sample_count());
487 }
488
489 // Positions.
490 {
491 JSONArray positions(&profile, "positions");
492 for (intptr_t i = 0; i < profile_function->NumSourcePositions(); i++) {
493 const ProfileFunctionSourcePosition& position =
494 profile_function->GetSourcePosition(i);
495 if (position.token_pos().IsReal()) {
496 // Add as an integer.
497 positions.AddValue(position.token_pos().Pos());
498 } else {
499 // Add as a string.
500 positions.AddValue(position.token_pos().ToCString());
501 }
502 }
503 }
504
505 // Exclusive ticks.
506 {
507 JSONArray exclusiveTicks(&profile, "exclusiveTicks");
508 for (intptr_t i = 0; i < profile_function->NumSourcePositions(); i++) {
509 const ProfileFunctionSourcePosition& position =
510 profile_function->GetSourcePosition(i);
511 exclusiveTicks.AddValue(position.exclusive_ticks());
512 }
513 }
514 // Inclusive ticks.
515 {
516 JSONArray inclusiveTicks(&profile, "inclusiveTicks");
517 for (intptr_t i = 0; i < profile_function->NumSourcePositions(); i++) {
518 const ProfileFunctionSourcePosition& position =
519 profile_function->GetSourcePosition(i);
520 inclusiveTicks.AddValue(position.inclusive_ticks());
521 }
522 }
523 }
524}
525
526void SourceReport::PrintScriptTable(JSONArray* scripts) {
527 for (intptr_t i = 0; i < script_table_entries_.length(); i++) {
528 const Script* script = script_table_entries_[i]->script;
529 scripts->AddValue(*script);
530 }
531}
532
533void SourceReport::VisitFunction(JSONArray* jsarr,
534 const Function& func,
535 CompileMode compile_mode) {
536 if (ShouldSkipFunction(func)) {
537 return;
538 }
539
540 const Script& script = Script::Handle(zone(), func.script());
541 const TokenPosition begin_pos = func.token_pos();
542 const TokenPosition end_pos = func.end_token_pos();
543
544 const intptr_t script_index = GetScriptIndex(script);
545 if (script_index < 0) {
546 return;
547 }
548
549 Code& code = Code::Handle(zone(), func.unoptimized_code());
550 if (code.IsNull()) {
551 if (func.HasCode() || (compile_mode == kForceCompile)) {
552 const Error& err =
554 if (!err.IsNull()) {
555 // Emit an uncompiled range for this function with error information.
556 JSONObject range(jsarr);
557 range.AddProperty("scriptIndex", script_index);
558 range.AddProperty("startPos", begin_pos);
559 range.AddProperty("endPos", end_pos);
560 range.AddProperty("compiled", false);
561 range.AddProperty("error", err);
562 return;
563 }
564 code = func.unoptimized_code();
565 } else {
566 // This function has not been compiled yet.
567 JSONObject range(jsarr);
568 range.AddProperty("scriptIndex", script_index);
569 range.AddProperty("startPos", begin_pos);
570 range.AddProperty("endPos", end_pos);
571 range.AddProperty("compiled", false);
572 return;
573 }
574 }
575 ASSERT(!code.IsNull());
576
577 JSONObject range(jsarr);
578 range.AddProperty("scriptIndex", script_index);
579 range.AddProperty("startPos", begin_pos);
580 range.AddProperty("endPos", end_pos);
581 range.AddProperty("compiled", true);
582
583 if (IsReportRequested(kCallSites)) {
584 PrintCallSitesData(&range, func, code);
585 }
586 if (IsReportRequested(kCoverage)) {
587 PrintCoverageData(&range, func, code, /* report_branch_coverage */ false);
588 }
589 if (IsReportRequested(kBranchCoverage)) {
590 PrintCoverageData(&range, func, code, /* report_branch_coverage */ true);
591 }
592 if (IsReportRequested(kPossibleBreakpoints)) {
593 PrintPossibleBreakpointsData(&range, func, code);
594 }
595 if (IsReportRequested(kProfile)) {
596 ProfileFunction* profile_function = profile_.FindFunction(func);
597 if ((profile_function != nullptr) &&
598 (profile_function->NumSourcePositions() > 0)) {
599 PrintProfileData(&range, profile_function);
600 }
601 }
602}
603
604void SourceReport::VisitField(JSONArray* jsarr,
605 const Field& field,
606 CompileMode compile_mode) {
607 if (ShouldSkipField(field) || !field.HasInitializerFunction()) return;
608 const Function& func = Function::Handle(field.InitializerFunction());
609 VisitFunction(jsarr, func, compile_mode);
610}
611
612void SourceReport::VisitLibrary(JSONArray* jsarr, const Library& lib) {
613 Class& cls = Class::Handle(zone());
614 Array& functions = Array::Handle(zone());
615 Array& fields = Array::Handle(zone());
616 Function& func = Function::Handle(zone());
617 Field& field = Field::Handle(zone());
618 Script& script = Script::Handle(zone());
619 ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
620 CompileMode compile_mode = compile_mode_;
621 if (compile_mode == kForceCompile && IsLibraryAlreadyCompiled(lib)) {
622 compile_mode = kNoCompile;
623 }
624 while (it.HasNext()) {
625 cls = it.GetNextClass();
626 if (!cls.is_finalized()) {
627 if (compile_mode == kForceCompile) {
628 Error& err = Error::Handle(cls.EnsureIsFinalized(thread()));
629 if (!err.IsNull()) {
630 // Emit an uncompiled range for this class with error information.
631 script = cls.script();
632 const intptr_t script_index = GetScriptIndex(script);
633 if (script_index < 0) {
634 continue;
635 }
636 JSONObject range(jsarr);
637 range.AddProperty("scriptIndex", script_index);
638 range.AddProperty("startPos", cls.token_pos());
639 range.AddProperty("endPos", cls.end_token_pos());
640 range.AddProperty("compiled", false);
641 range.AddProperty("error", err);
642 continue;
643 }
644 ASSERT(cls.is_finalized());
645 } else {
646 cls.EnsureDeclarationLoaded();
647 // Emit one range for the whole uncompiled class.
648 script = cls.script();
649 const intptr_t script_index = GetScriptIndex(script);
650 if (script_index < 0) {
651 continue;
652 }
653 JSONObject range(jsarr);
654 range.AddProperty("scriptIndex", script_index);
655 range.AddProperty("startPos", cls.token_pos());
656 range.AddProperty("endPos", cls.end_token_pos());
657 range.AddProperty("compiled", false);
658 continue;
659 }
660 }
661
662 functions = cls.current_functions();
663 for (int i = 0; i < functions.Length(); i++) {
664 func ^= functions.At(i);
665 // Skip getter functions of static const field.
666 if (func.kind() == UntaggedFunction::kImplicitStaticGetter) {
667 field ^= func.accessor_field();
668 if (field.is_const() && field.is_static()) {
669 continue;
670 }
671 }
672 VisitFunction(jsarr, func, compile_mode);
673 }
674
675 fields = cls.fields();
676 for (intptr_t i = 0; i < fields.Length(); i++) {
677 field ^= fields.At(i);
678 VisitField(jsarr, field, compile_mode);
679 }
680 }
681}
682
683void SourceReport::VisitClosures(JSONArray* jsarr) {
684 ClosureFunctionsCache::ForAllClosureFunctions([&](const Function& func) {
685 VisitFunction(jsarr, func, compile_mode_);
686 return true; // Continue iteration.
687 });
688}
689
691 const Script& script,
692 TokenPosition start_pos,
693 TokenPosition end_pos) {
694 Init(Thread::Current(), &script, start_pos, end_pos);
695
696 JSONObject report(js);
697 report.AddProperty("type", "SourceReport");
698 {
699 JSONArray ranges(&report, "ranges");
700
702 zone(), thread()->isolate_group()->object_store()->libraries());
703
704 // We only visit the libraries which actually load the specified script.
705 Library& lib = Library::Handle(zone());
706 for (intptr_t i = 0; i < libs.Length(); i++) {
707 lib ^= libs.At(i);
708 if (script.IsNull() || ScriptIsLoadedByLibrary(script, lib)) {
709 VisitLibrary(&ranges, lib);
710 }
711 }
712
713 // Visit all closures for this isolate.
714 VisitClosures(&ranges);
715
716 // Output constant coverage if coverage is requested.
717 if (IsReportRequested(kCoverage)) {
718 // Find all scripts. We need to go though all scripts because a script
719 // (even one we don't want) can add coverage to another library (i.e.
720 // potentially one we want).
722 GrowableArray<ScriptTableEntry*> local_script_table_entries;
723 CollectAllScripts(&local_script_table, &local_script_table_entries);
724 CollectConstConstructorCoverageFromScripts(&local_script_table_entries,
725 &ranges);
726 CleanupCollectedScripts(&local_script_table, &local_script_table_entries);
727 }
728 }
729
730 // Print the script table.
731 JSONArray scripts(&report, "scripts");
732 PrintScriptTable(&scripts);
733}
734
735void SourceReport::CollectAllScripts(
736 DirectChainedHashMap<ScriptTableTrait>* local_script_table,
737 GrowableArray<ScriptTableEntry*>* local_script_table_entries) {
738 ScriptTableEntry wrapper;
740 zone(), thread()->isolate_group()->object_store()->libraries());
741 Library& lib = Library::Handle(zone());
742 Script& scriptRef = Script::Handle(zone());
743 for (int i = 0; i < libs.Length(); i++) {
744 lib ^= libs.At(i);
745 const Array& scripts = Array::Handle(zone(), lib.LoadedScripts());
746 for (intptr_t j = 0; j < scripts.Length(); j++) {
747 scriptRef ^= scripts.At(j);
748 const String& url = String::Handle(zone(), scriptRef.url());
749 wrapper.key = &url;
750 wrapper.script = &Script::Handle(zone(), scriptRef.ptr());
751 ScriptTableEntry* pair = local_script_table->LookupValue(&wrapper);
752 if (pair != nullptr) {
753 // Existing one.
754 continue;
755 }
756 // New one. Insert.
757 ScriptTableEntry* tmp = new ScriptTableEntry();
758 tmp->key = &url;
759 tmp->index = -1;
760 tmp->script = wrapper.script;
761 local_script_table_entries->Add(tmp);
762 local_script_table->Insert(tmp);
763 }
764 }
765}
766
767void SourceReport::CleanupCollectedScripts(
768 DirectChainedHashMap<ScriptTableTrait>* local_script_table,
769 GrowableArray<ScriptTableEntry*>* local_script_table_entries) {
770 for (intptr_t i = 0; i < local_script_table_entries->length(); i++) {
771 delete local_script_table_entries->operator[](i);
772 local_script_table_entries->operator[](i) = nullptr;
773 }
774 local_script_table_entries->Clear();
775 local_script_table->Clear();
776}
777
778void SourceReport::CollectConstConstructorCoverageFromScripts(
779 GrowableArray<ScriptTableEntry*>* local_script_table_entries,
780 JSONArray* ranges) {
781 // Now output the wanted constant coverage.
782 for (intptr_t i = 0; i < local_script_table_entries->length(); i++) {
783 const Script* script = local_script_table_entries->At(i)->script;
784
785 // Whether we want *this* script or not we need to look at the constant
786 // constructor coverage. Any of those could be in a script we *do* want.
787 {
788 Script& scriptRef = Script::Handle(zone());
789 const Array& constructors =
790 Array::Handle(script->CollectConstConstructorCoverageFrom());
791 intptr_t constructors_count = constructors.Length();
792 Function& constructor = Function::Handle(zone());
793 Code& code = Code::Handle(zone());
794 for (intptr_t i = 0; i < constructors_count; i++) {
795 constructor ^= constructors.At(i);
796 // Check if we want coverage for this constructor.
797 if (ShouldSkipFunction(constructor)) {
798 continue;
799 }
800 scriptRef ^= constructor.script();
801 const intptr_t script_index = GetScriptIndex(scriptRef);
802 if (script_index < 0) {
803 continue;
804 }
805 code ^= constructor.unoptimized_code();
806 const TokenPosition begin_pos = constructor.token_pos();
807 const TokenPosition end_pos = constructor.end_token_pos();
808 JSONObject range(ranges);
809 range.AddProperty("scriptIndex", script_index);
810 range.AddProperty("compiled",
811 !code.IsNull()); // Does this make a difference?
812 range.AddProperty("startPos", begin_pos);
813 range.AddProperty("endPos", end_pos);
814
815 JSONObject cov(&range, "coverage");
816 {
817 JSONArray hits(&cov, "hits");
818 hits.AddValue(GetTokenPosOrLine(scriptRef, begin_pos));
819 }
820 {
821 JSONArray misses(&cov, "misses");
822 // No misses
823 }
824 }
825 }
826 }
827}
828
829} // namespace dart
830#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:855
intptr_t Length() const
Definition object.h:11046
ObjectPtr At(intptr_t index) const
Definition object.h:11059
SafepointRwLock * program_lock()
Definition isolate.h:532
Dart_Port main_port() const
Definition isolate.h:1001
void AddProperty(const char *name, bool b) const
ArrayPtr LoadedScripts() const
Definition object.cc:13987
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:4903
intptr_t Value() const
Definition object.h:9969
void PrintJSON(JSONStream *js, const Script &script, TokenPosition start_pos=TokenPosition::kMinSource, TokenPosition end_pos=TokenPosition::kMaxSource)
static const char * kPossibleBreakpointsStr
static const char * kCallSitesStr
static const char * kCoverageStr
static const char * kBranchCoverageStr
SourceReport(intptr_t report_set, CompileMode compile=kNoCompile, bool report_lines=false)
static const char * kProfileStr
static Thread * Current()
Definition thread.h:361
Isolate * isolate() const
Definition thread.h:533
IsolateGroup * isolate_group() const
Definition thread.h:540
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
void Init()
#define HANDLESCOPE(thread)
Definition handles.h:321
const uint8_t kSafepointKind
Definition debugger.cc:1313