OLD | NEW |
(Empty) | |
| 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 |
| 5 #include "vm/source_report.h" |
| 6 |
| 7 #include "vm/compiler.h" |
| 8 #include "vm/object.h" |
| 9 #include "vm/object_store.h" |
| 10 |
| 11 namespace dart { |
| 12 |
| 13 SourceReport::SourceReport(ReportKind report_kind, CompileMode compile_mode) |
| 14 : report_kind_(report_kind), |
| 15 compile_mode_(compile_mode), |
| 16 thread_(NULL), |
| 17 script_(NULL), |
| 18 start_pos_(-1), |
| 19 end_pos_(-1), |
| 20 next_script_index_(0) { |
| 21 } |
| 22 |
| 23 |
| 24 void SourceReport::Init(Thread* thread, |
| 25 const Script* script, |
| 26 intptr_t start_pos, |
| 27 intptr_t end_pos) { |
| 28 thread_ = thread; |
| 29 script_ = script; |
| 30 start_pos_ = start_pos; |
| 31 end_pos_ = end_pos; |
| 32 script_table_entries_.Clear(); |
| 33 script_table_.Clear(); |
| 34 next_script_index_ = 0; |
| 35 } |
| 36 |
| 37 |
| 38 bool SourceReport::ShouldSkipFunction(const Function& func) { |
| 39 if (script_ != NULL && !script_->IsNull()) { |
| 40 if (func.script() != script_->raw()) { |
| 41 // The function is from the wrong script. |
| 42 return true; |
| 43 } |
| 44 if (((start_pos_ > 0) && (func.end_token_pos() < start_pos_)) || |
| 45 ((end_pos_ > 0) && (func.token_pos() > end_pos_))) { |
| 46 // The function does not intersect with the requested token range. |
| 47 return true; |
| 48 } |
| 49 } |
| 50 |
| 51 switch (func.kind()) { |
| 52 case RawFunction::kRegularFunction: |
| 53 case RawFunction::kClosureFunction: |
| 54 case RawFunction::kGetterFunction: |
| 55 case RawFunction::kSetterFunction: |
| 56 case RawFunction::kConstructor: |
| 57 break; |
| 58 default: |
| 59 return true; |
| 60 } |
| 61 if (func.is_abstract() || |
| 62 func.IsImplicitConstructor() || |
| 63 func.IsRedirectingFactory()) { |
| 64 return true; |
| 65 } |
| 66 if (func.IsNonImplicitClosureFunction() && |
| 67 (func.context_scope() == ContextScope::null())) { |
| 68 // TODO(iposva): This can arise if we attempt to compile an inner function |
| 69 // before we have compiled its enclosing function or if the enclosing |
| 70 // function failed to compile. |
| 71 return true; |
| 72 } |
| 73 return false; |
| 74 } |
| 75 |
| 76 |
| 77 intptr_t SourceReport::GetScriptIndex(const Script& script) { |
| 78 const String& url = String::Handle(zone(), script.url()); |
| 79 ScriptTableEntry* pair = script_table_.Lookup(&url); |
| 80 if (pair != NULL) { |
| 81 return pair->index; |
| 82 } |
| 83 |
| 84 ScriptTableEntry tmp; |
| 85 tmp.key = &url; |
| 86 tmp.index = next_script_index_++; |
| 87 tmp.script = &script; |
| 88 script_table_entries_.Add(tmp); |
| 89 script_table_.Insert(&(script_table_entries_.Last())); |
| 90 return tmp.index; |
| 91 } |
| 92 |
| 93 |
| 94 bool SourceReport::ScriptIsLoadedByLibrary(const Script& script, |
| 95 const Library& lib) { |
| 96 const Array& scripts = Array::Handle(zone(), lib.LoadedScripts()); |
| 97 for (intptr_t j = 0; j < scripts.Length(); j++) { |
| 98 if (scripts.At(j) == script.raw()) { |
| 99 return true; |
| 100 } |
| 101 } |
| 102 return false; |
| 103 } |
| 104 |
| 105 |
| 106 void SourceReport::PrintCallSitesData(JSONObject* jsobj, |
| 107 const Function& func, |
| 108 const Code& code) { |
| 109 const intptr_t begin_pos = func.token_pos(); |
| 110 const intptr_t end_pos = func.end_token_pos(); |
| 111 |
| 112 ZoneGrowableArray<const ICData*>* ic_data_array = |
| 113 new(zone()) ZoneGrowableArray<const ICData*>(); |
| 114 func.RestoreICDataMap(ic_data_array, false /* clone descriptors */); |
| 115 const PcDescriptors& descriptors = PcDescriptors::Handle( |
| 116 zone(), code.pc_descriptors()); |
| 117 |
| 118 JSONArray sites(jsobj, "callSites"); |
| 119 |
| 120 PcDescriptors::Iterator iter( |
| 121 descriptors, |
| 122 RawPcDescriptors::kIcCall | RawPcDescriptors::kUnoptStaticCall); |
| 123 while (iter.MoveNext()) { |
| 124 HANDLESCOPE(thread()); |
| 125 const ICData* ic_data = (*ic_data_array)[iter.DeoptId()]; |
| 126 if (!ic_data->IsNull()) { |
| 127 const intptr_t token_pos = iter.TokenPos(); |
| 128 if ((token_pos < begin_pos) || (token_pos > end_pos)) { |
| 129 // Does not correspond to a valid source position. |
| 130 continue; |
| 131 } |
| 132 bool is_static_call = iter.Kind() == RawPcDescriptors::kUnoptStaticCall; |
| 133 ic_data->PrintToJSONArrayNew(sites, token_pos, is_static_call); |
| 134 } |
| 135 } |
| 136 } |
| 137 |
| 138 void SourceReport::PrintCoverageData(JSONObject* jsobj, |
| 139 const Function& func, |
| 140 const Code& code) { |
| 141 const intptr_t begin_pos = func.token_pos(); |
| 142 const intptr_t end_pos = func.end_token_pos(); |
| 143 |
| 144 ZoneGrowableArray<const ICData*>* ic_data_array = |
| 145 new(zone()) ZoneGrowableArray<const ICData*>(); |
| 146 func.RestoreICDataMap(ic_data_array, false /* clone descriptors */); |
| 147 const PcDescriptors& descriptors = PcDescriptors::Handle( |
| 148 zone(), code.pc_descriptors()); |
| 149 |
| 150 const int kCoverageNone = 0; |
| 151 const int kCoverageMiss = 1; |
| 152 const int kCoverageHit = 2; |
| 153 |
| 154 intptr_t func_length = (end_pos - begin_pos) + 1; |
| 155 GrowableArray<char> coverage(func_length); |
| 156 coverage.SetLength(func_length); |
| 157 for (int i = 0; i < func_length; i++) { |
| 158 coverage[i] = kCoverageNone; |
| 159 } |
| 160 |
| 161 PcDescriptors::Iterator iter( |
| 162 descriptors, |
| 163 RawPcDescriptors::kIcCall | RawPcDescriptors::kUnoptStaticCall); |
| 164 while (iter.MoveNext()) { |
| 165 HANDLESCOPE(thread()); |
| 166 const ICData* ic_data = (*ic_data_array)[iter.DeoptId()]; |
| 167 if (!ic_data->IsNull()) { |
| 168 const intptr_t token_pos = iter.TokenPos(); |
| 169 if ((token_pos < begin_pos) || (token_pos > end_pos)) { |
| 170 // Does not correspond to a valid source position. |
| 171 continue; |
| 172 } |
| 173 intptr_t count = ic_data->AggregateCount(); |
| 174 intptr_t token_offset = token_pos - begin_pos; |
| 175 if (count > 0) { |
| 176 coverage[token_offset] = kCoverageHit; |
| 177 } else { |
| 178 if (coverage[token_offset] == kCoverageNone) { |
| 179 coverage[token_offset] = kCoverageMiss; |
| 180 } |
| 181 } |
| 182 } |
| 183 } |
| 184 |
| 185 JSONObject cov(jsobj, "coverage"); |
| 186 { |
| 187 JSONArray hits(&cov, "hits"); |
| 188 for (int i = 0; i < func_length; i++) { |
| 189 if (coverage[i] == kCoverageHit) { |
| 190 hits.AddValue(begin_pos + i); // Add the token position of the hit. |
| 191 } |
| 192 } |
| 193 } |
| 194 { |
| 195 JSONArray misses(&cov, "misses"); |
| 196 for (int i = 0; i < func_length; i++) { |
| 197 if (coverage[i] == kCoverageMiss) { |
| 198 misses.AddValue(begin_pos + i); // Add the token position of the miss. |
| 199 } |
| 200 } |
| 201 } |
| 202 } |
| 203 |
| 204 |
| 205 void SourceReport::PrintScriptTable(JSONArray* scripts) { |
| 206 for (int i = 0; i < script_table_entries_.length(); i++) { |
| 207 const Script* script = script_table_entries_[i].script; |
| 208 scripts->AddValue(*script); |
| 209 } |
| 210 } |
| 211 |
| 212 |
| 213 void SourceReport::VisitFunction(JSONArray* jsarr, const Function& func) { |
| 214 if (ShouldSkipFunction(func)) { |
| 215 return; |
| 216 } |
| 217 |
| 218 const Script& script = Script::Handle(zone(), func.script()); |
| 219 const intptr_t begin_pos = func.token_pos(); |
| 220 const intptr_t end_pos = func.end_token_pos(); |
| 221 |
| 222 Code& code = Code::Handle(zone(), func.unoptimized_code()); |
| 223 if (code.IsNull()) { |
| 224 if (func.HasCode() || (compile_mode_ == kForceCompile)) { |
| 225 if (Compiler::EnsureUnoptimizedCode(thread(), func) != Error::null()) { |
| 226 // Ignore the error and this function entirely. |
| 227 return; |
| 228 } |
| 229 code = func.unoptimized_code(); |
| 230 } else { |
| 231 // This function has not been compiled yet. |
| 232 JSONObject range(jsarr); |
| 233 range.AddProperty("scriptIndex", GetScriptIndex(script)); |
| 234 range.AddProperty("startPos", begin_pos); |
| 235 range.AddProperty("endPos", end_pos); |
| 236 range.AddProperty("compiled", false); |
| 237 return; |
| 238 } |
| 239 } |
| 240 ASSERT(!code.IsNull()); |
| 241 |
| 242 JSONObject range(jsarr); |
| 243 range.AddProperty("scriptIndex", GetScriptIndex(script)); |
| 244 range.AddProperty("startPos", begin_pos); |
| 245 range.AddProperty("endPos", end_pos); |
| 246 range.AddProperty("compiled", true); |
| 247 |
| 248 if (report_kind_ == kCallSites) { |
| 249 PrintCallSitesData(&range, func, code); |
| 250 } else if (report_kind_ == kCoverage) { |
| 251 PrintCoverageData(&range, func, code); |
| 252 } |
| 253 } |
| 254 |
| 255 |
| 256 void SourceReport::VisitLibrary(JSONArray* jsarr, const Library& lib) { |
| 257 Class& cls = Class::Handle(zone()); |
| 258 Array& functions = Array::Handle(zone()); |
| 259 Function& func = Function::Handle(zone()); |
| 260 ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate); |
| 261 while (it.HasNext()) { |
| 262 cls = it.GetNextClass(); |
| 263 functions = cls.functions(); |
| 264 for (int i = 0; i < functions.Length(); i++) { |
| 265 func ^= functions.At(i); |
| 266 VisitFunction(jsarr, func); |
| 267 } |
| 268 } |
| 269 } |
| 270 |
| 271 |
| 272 void SourceReport::VisitClosures(JSONArray* jsarr) { |
| 273 const GrowableObjectArray& closures = GrowableObjectArray::Handle( |
| 274 thread()->isolate()->object_store()->closure_functions()); |
| 275 |
| 276 // We need to keep rechecking the length of the closures array, as handling |
| 277 // a closure potentially adds new entries to the end. |
| 278 Function& func = Function::Handle(zone()); |
| 279 for (int i = 0; i < closures.Length(); i++) { |
| 280 func ^= closures.At(i); |
| 281 VisitFunction(jsarr, func); |
| 282 } |
| 283 } |
| 284 |
| 285 |
| 286 void SourceReport::PrintJSON(JSONStream* js, |
| 287 const Script& script, |
| 288 intptr_t start_pos, intptr_t end_pos) { |
| 289 Init(Thread::Current(), &script, start_pos, end_pos); |
| 290 |
| 291 JSONObject report(js); |
| 292 report.AddProperty("type", "SourceReport"); |
| 293 { |
| 294 JSONArray ranges(&report, "ranges"); |
| 295 |
| 296 const GrowableObjectArray& libs = GrowableObjectArray::Handle( |
| 297 zone(), thread()->isolate()->object_store()->libraries()); |
| 298 |
| 299 // We only visit the libraries which actually load the specified script. |
| 300 Library& lib = Library::Handle(zone()); |
| 301 for (int i = 0; i < libs.Length(); i++) { |
| 302 lib ^= libs.At(i); |
| 303 if (script.IsNull() || ScriptIsLoadedByLibrary(script, lib)) { |
| 304 VisitLibrary(&ranges, lib); |
| 305 } |
| 306 } |
| 307 |
| 308 // Visit all closures for this isolate. |
| 309 VisitClosures(&ranges); |
| 310 } |
| 311 |
| 312 // Print the script table. |
| 313 JSONArray scripts(&report, "scripts"); |
| 314 PrintScriptTable(&scripts); |
| 315 } |
| 316 |
| 317 |
| 318 } // namespace dart |
OLD | NEW |