875922a88f
When library_filters were given, we used to prefill the script_table_, then just assume that any scripts not in the script_table_ must have been filtered out. We wrote it this way to avoid checking the filters in every GetScriptIndex call. But in some cases (eg mixins), lib.LoadedScripts() can miss some scripts, so they'd be incorrectly omitted from the table. The new implementation lazy loads the scripts, the same way it works when there are no library_filters. Skipped scripts are still placed in the script_table_, but given an index of -1, and omitted from script_table_entries_. Bug: https://github.com/dart-lang/sdk/issues/49887 Change-Id: Ide938ddfa9a3750c72c615e296b1a23875e46ab8 TEST=CI (also manually tested that the bug is fixed, but it's a really fiddly setup and I'm not sure how to put it in a unit test. See bug for details) Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/260076 Reviewed-by: Ben Konyi <bkonyi@google.com> Commit-Queue: Liam Appelbe <liama@google.com>
163 lines
5.5 KiB
C++
163 lines
5.5 KiB
C++
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_SOURCE_REPORT_H_
|
|
#define RUNTIME_VM_SOURCE_REPORT_H_
|
|
|
|
#include "vm/globals.h"
|
|
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
#include "vm/allocation.h"
|
|
#include "vm/flags.h"
|
|
#include "vm/hash_map.h"
|
|
#include "vm/object.h"
|
|
#include "vm/profiler_service.h"
|
|
#include "vm/token_position.h"
|
|
|
|
namespace dart {
|
|
|
|
// A SourceReport object is used to generate reports about the program
|
|
// source code, with information associated with source token
|
|
// positions. There are multiple possible kinds of reports.
|
|
class SourceReport {
|
|
public:
|
|
enum ReportKind {
|
|
kCallSites = 0x1,
|
|
kCoverage = 0x2,
|
|
kPossibleBreakpoints = 0x4,
|
|
kProfile = 0x8,
|
|
kBranchCoverage = 0x10,
|
|
};
|
|
|
|
static const char* kCallSitesStr;
|
|
static const char* kCoverageStr;
|
|
static const char* kPossibleBreakpointsStr;
|
|
static const char* kProfileStr;
|
|
static const char* kBranchCoverageStr;
|
|
|
|
enum CompileMode { kNoCompile, kForceCompile };
|
|
|
|
// report_set is a bitvector indicating which reports to generate
|
|
// (e.g. kCallSites | kCoverage).
|
|
//
|
|
// If library_filters is not null, then the report will only include libraries
|
|
// whose URIs start with one of the filter strings.
|
|
explicit SourceReport(intptr_t report_set,
|
|
CompileMode compile = kNoCompile,
|
|
bool report_lines = false);
|
|
explicit SourceReport(intptr_t report_set,
|
|
const GrowableObjectArray& library_filters,
|
|
CompileMode compile = kNoCompile,
|
|
bool report_lines = false);
|
|
~SourceReport();
|
|
|
|
// Generate a source report for (some subrange of) a script.
|
|
//
|
|
// If script is null, then the report is generated for all scripts
|
|
// in the isolate.
|
|
void PrintJSON(JSONStream* js,
|
|
const Script& script,
|
|
TokenPosition start_pos = TokenPosition::kMinSource,
|
|
TokenPosition end_pos = TokenPosition::kMaxSource);
|
|
|
|
private:
|
|
void ClearScriptTable();
|
|
void Init(Thread* thread,
|
|
const Script* script,
|
|
TokenPosition start_pos,
|
|
TokenPosition end_pos);
|
|
|
|
Thread* thread() const { return thread_; }
|
|
Zone* zone() const { return thread_->zone(); }
|
|
Isolate* isolate() const { return thread_->isolate(); }
|
|
|
|
bool IsReportRequested(ReportKind report_kind);
|
|
bool ShouldSkipFunction(const Function& func);
|
|
bool ShouldSkipField(const Field& field);
|
|
bool ShouldCoverageSkipCallSite(const ICData* ic_data);
|
|
intptr_t GetScriptIndex(const Script& script);
|
|
bool ScriptIsLoadedByLibrary(const Script& script, const Library& lib);
|
|
intptr_t GetTokenPosOrLine(const Script& script,
|
|
const TokenPosition& token_pos);
|
|
bool ShouldFiltersIncludeScript(const Script& script);
|
|
bool ShouldFiltersIncludeUrl(const String& url);
|
|
|
|
void PrintCallSitesData(JSONObject* jsobj,
|
|
const Function& func,
|
|
const Code& code);
|
|
void PrintCoverageData(JSONObject* jsobj,
|
|
const Function& func,
|
|
const Code& code,
|
|
bool report_branch_coverage);
|
|
void PrintPossibleBreakpointsData(JSONObject* jsobj,
|
|
const Function& func,
|
|
const Code& code);
|
|
void PrintProfileData(JSONObject* jsobj, ProfileFunction* profile_function);
|
|
#if defined(DEBUG)
|
|
void VerifyScriptTable();
|
|
#endif
|
|
void PrintScriptTable(JSONArray* jsarr);
|
|
|
|
void VisitFunction(JSONArray* jsarr, const Function& func);
|
|
void VisitField(JSONArray* jsarr, const Field& field);
|
|
void VisitLibrary(JSONArray* jsarr, const Library& lib);
|
|
void VisitClosures(JSONArray* jsarr);
|
|
// An entry in the script table.
|
|
struct ScriptTableEntry {
|
|
ScriptTableEntry() : key(NULL), index(-1), script(NULL) {}
|
|
|
|
const String* key;
|
|
intptr_t index;
|
|
const Script* script;
|
|
};
|
|
|
|
// Needed for DirectChainedHashMap.
|
|
struct ScriptTableTrait {
|
|
typedef ScriptTableEntry* Value;
|
|
typedef const ScriptTableEntry* Key;
|
|
typedef ScriptTableEntry* Pair;
|
|
|
|
static Key KeyOf(Pair kv) { return kv; }
|
|
|
|
static Value ValueOf(Pair kv) { return kv; }
|
|
|
|
static inline uword Hash(Key key) { return key->key->Hash(); }
|
|
|
|
static inline bool IsKeyEqual(Pair kv, Key key) {
|
|
return kv->script->ptr() == key->script->ptr();
|
|
}
|
|
};
|
|
|
|
void CollectAllScripts(
|
|
DirectChainedHashMap<ScriptTableTrait>* local_script_table,
|
|
GrowableArray<ScriptTableEntry*>* local_script_table_entries);
|
|
|
|
void CleanupCollectedScripts(
|
|
DirectChainedHashMap<ScriptTableTrait>* local_script_table,
|
|
GrowableArray<ScriptTableEntry*>* local_script_table_entries);
|
|
|
|
void CollectConstConstructorCoverageFromScripts(
|
|
GrowableArray<ScriptTableEntry*>* local_script_table_entries,
|
|
JSONArray* ranges);
|
|
|
|
intptr_t report_set_;
|
|
CompileMode compile_mode_;
|
|
bool report_lines_;
|
|
const GrowableObjectArray& library_filters_;
|
|
Thread* thread_;
|
|
const Script* script_;
|
|
TokenPosition start_pos_;
|
|
TokenPosition end_pos_;
|
|
Profile profile_;
|
|
GrowableArray<ScriptTableEntry*> script_table_entries_;
|
|
DirectChainedHashMap<ScriptTableTrait> script_table_;
|
|
intptr_t next_script_index_;
|
|
intptr_t late_error_class_id_ = ClassId::kIllegalCid;
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
|
|
#endif // RUNTIME_VM_SOURCE_REPORT_H_
|