summaryrefslogtreecommitdiff
path: root/chromium/v8/src/profiler
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-01-20 13:40:20 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-01-22 12:41:23 +0000
commit7961cea6d1041e3e454dae6a1da660b453efd238 (patch)
treec0eeb4a9ff9ba32986289c1653d9608e53ccb444 /chromium/v8/src/profiler
parentb7034d0803538058e5c9d904ef03cf5eab34f6ef (diff)
downloadqtwebengine-chromium-7961cea6d1041e3e454dae6a1da660b453efd238.tar.gz
BASELINE: Update Chromium to 78.0.3904.130
Change-Id: If185e0c0061b3437531c97c9c8c78f239352a68b Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/v8/src/profiler')
-rw-r--r--chromium/v8/src/profiler/cpu-profiler.cc229
-rw-r--r--chromium/v8/src/profiler/cpu-profiler.h103
-rw-r--r--chromium/v8/src/profiler/heap-profiler.cc11
-rw-r--r--chromium/v8/src/profiler/heap-profiler.h1
-rw-r--r--chromium/v8/src/profiler/heap-snapshot-generator.cc72
-rw-r--r--chromium/v8/src/profiler/heap-snapshot-generator.h9
-rw-r--r--chromium/v8/src/profiler/profile-generator-inl.h2
-rw-r--r--chromium/v8/src/profiler/profile-generator.cc106
-rw-r--r--chromium/v8/src/profiler/profile-generator.h47
-rw-r--r--chromium/v8/src/profiler/profiler-listener.cc10
-rw-r--r--chromium/v8/src/profiler/profiler-listener.h1
-rw-r--r--chromium/v8/src/profiler/tick-sample.cc115
-rw-r--r--chromium/v8/src/profiler/tick-sample.h76
13 files changed, 604 insertions, 178 deletions
diff --git a/chromium/v8/src/profiler/cpu-profiler.cc b/chromium/v8/src/profiler/cpu-profiler.cc
index 495840fabf5..4c35159b2e9 100644
--- a/chromium/v8/src/profiler/cpu-profiler.cc
+++ b/chromium/v8/src/profiler/cpu-profiler.cc
@@ -47,21 +47,56 @@ class CpuSampler : public sampler::Sampler {
SamplingEventsProcessor* processor_;
};
-ProfilerEventsProcessor::ProfilerEventsProcessor(Isolate* isolate,
- ProfileGenerator* generator)
+ProfilingScope::ProfilingScope(Isolate* isolate, ProfilerListener* listener)
+ : isolate_(isolate), listener_(listener) {
+ size_t profiler_count = isolate_->num_cpu_profilers();
+ profiler_count++;
+ isolate_->set_num_cpu_profilers(profiler_count);
+ isolate_->set_is_profiling(true);
+ isolate_->wasm_engine()->EnableCodeLogging(isolate_);
+
+ Logger* logger = isolate_->logger();
+ logger->AddCodeEventListener(listener_);
+ // Populate the ProfilerCodeObserver with the initial functions and
+ // callbacks on the heap.
+ DCHECK(isolate_->heap()->HasBeenSetUp());
+
+ if (!FLAG_prof_browser_mode) {
+ logger->LogCodeObjects();
+ }
+ logger->LogCompiledFunctions();
+ logger->LogAccessorCallbacks();
+}
+
+ProfilingScope::~ProfilingScope() {
+ isolate_->logger()->RemoveCodeEventListener(listener_);
+
+ size_t profiler_count = isolate_->num_cpu_profilers();
+ DCHECK_GT(profiler_count, 0);
+ profiler_count--;
+ isolate_->set_num_cpu_profilers(profiler_count);
+ if (profiler_count == 0) isolate_->set_is_profiling(false);
+}
+
+ProfilerEventsProcessor::ProfilerEventsProcessor(
+ Isolate* isolate, ProfileGenerator* generator,
+ ProfilerCodeObserver* code_observer)
: Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)),
generator_(generator),
+ code_observer_(code_observer),
running_(1),
last_code_event_id_(0),
last_processed_code_event_id_(0),
- isolate_(isolate),
- profiling_scope_(isolate) {}
-
-SamplingEventsProcessor::SamplingEventsProcessor(Isolate* isolate,
- ProfileGenerator* generator,
- base::TimeDelta period,
- bool use_precise_sampling)
- : ProfilerEventsProcessor(isolate, generator),
+ isolate_(isolate) {
+ DCHECK(!code_observer_->processor());
+ code_observer_->set_processor(this);
+}
+
+SamplingEventsProcessor::SamplingEventsProcessor(
+ Isolate* isolate, ProfileGenerator* generator,
+ ProfilerCodeObserver* code_observer, base::TimeDelta period,
+ bool use_precise_sampling)
+ : ProfilerEventsProcessor(isolate, generator, code_observer),
sampler_(new CpuSampler(isolate, this)),
period_(period),
use_precise_sampling_(use_precise_sampling) {
@@ -70,7 +105,10 @@ SamplingEventsProcessor::SamplingEventsProcessor(Isolate* isolate,
SamplingEventsProcessor::~SamplingEventsProcessor() { sampler_->Stop(); }
-ProfilerEventsProcessor::~ProfilerEventsProcessor() = default;
+ProfilerEventsProcessor::~ProfilerEventsProcessor() {
+ DCHECK_EQ(code_observer_->processor(), this);
+ code_observer_->clear_processor();
+}
void ProfilerEventsProcessor::Enqueue(const CodeEventsContainer& event) {
event.generic.order = ++last_code_event_id_;
@@ -123,16 +161,13 @@ void ProfilerEventsProcessor::StopSynchronously() {
bool ProfilerEventsProcessor::ProcessCodeEvent() {
CodeEventsContainer record;
if (events_buffer_.Dequeue(&record)) {
- switch (record.generic.type) {
-#define PROFILER_TYPE_CASE(type, clss) \
- case CodeEventRecord::type: \
- record.clss##_.UpdateCodeMap(generator_->code_map()); \
- break;
-
- CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE)
-
-#undef PROFILER_TYPE_CASE
- default: return true; // Skip record.
+ if (record.generic.type == CodeEventRecord::NATIVE_CONTEXT_MOVE) {
+ NativeContextMoveEventRecord& nc_record =
+ record.NativeContextMoveEventRecord_;
+ generator_->UpdateNativeContextAddress(nc_record.from_address,
+ nc_record.to_address);
+ } else {
+ code_observer_->CodeEventHandlerInternal(record);
}
last_processed_code_event_id_ = record.generic.order;
return true;
@@ -146,6 +181,7 @@ void ProfilerEventsProcessor::CodeEventHandler(
case CodeEventRecord::CODE_CREATION:
case CodeEventRecord::CODE_MOVE:
case CodeEventRecord::CODE_DISABLE_OPT:
+ case CodeEventRecord::NATIVE_CONTEXT_MOVE:
Enqueue(evt_rec);
break;
case CodeEventRecord::CODE_DEOPT: {
@@ -262,6 +298,62 @@ void* SamplingEventsProcessor::operator new(size_t size) {
void SamplingEventsProcessor::operator delete(void* ptr) { AlignedFree(ptr); }
+ProfilerCodeObserver::ProfilerCodeObserver(Isolate* isolate)
+ : isolate_(isolate), processor_(nullptr) {
+ CreateEntriesForRuntimeCallStats();
+ LogBuiltins();
+}
+
+void ProfilerCodeObserver::CodeEventHandler(
+ const CodeEventsContainer& evt_rec) {
+ if (processor_) {
+ processor_->CodeEventHandler(evt_rec);
+ return;
+ }
+ CodeEventHandlerInternal(evt_rec);
+}
+
+void ProfilerCodeObserver::CodeEventHandlerInternal(
+ const CodeEventsContainer& evt_rec) {
+ CodeEventsContainer record = evt_rec;
+ switch (evt_rec.generic.type) {
+#define PROFILER_TYPE_CASE(type, clss) \
+ case CodeEventRecord::type: \
+ record.clss##_.UpdateCodeMap(&code_map_); \
+ break;
+
+ CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE)
+
+#undef PROFILER_TYPE_CASE
+ default:
+ break;
+ }
+}
+
+void ProfilerCodeObserver::CreateEntriesForRuntimeCallStats() {
+ RuntimeCallStats* rcs = isolate_->counters()->runtime_call_stats();
+ for (int i = 0; i < RuntimeCallStats::kNumberOfCounters; ++i) {
+ RuntimeCallCounter* counter = rcs->GetCounter(i);
+ DCHECK(counter->name());
+ auto entry = new CodeEntry(CodeEventListener::FUNCTION_TAG, counter->name(),
+ "native V8Runtime");
+ code_map_.AddCode(reinterpret_cast<Address>(counter), entry, 1);
+ }
+}
+
+void ProfilerCodeObserver::LogBuiltins() {
+ Builtins* builtins = isolate_->builtins();
+ DCHECK(builtins->is_initialized());
+ for (int i = 0; i < Builtins::builtin_count; i++) {
+ CodeEventsContainer evt_rec(CodeEventRecord::REPORT_BUILTIN);
+ ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_;
+ Builtins::Name id = static_cast<Builtins::Name>(i);
+ rec->instruction_start = builtins->builtin(id).InstructionStart();
+ rec->builtin_id = id;
+ CodeEventHandlerInternal(evt_rec);
+ }
+}
+
int CpuProfiler::GetProfilesCount() {
// The count of profiles doesn't depend on a security token.
return static_cast<int>(profiles_->profiles()->size());
@@ -324,29 +416,37 @@ DEFINE_LAZY_LEAKY_OBJECT_GETTER(CpuProfilersManager, GetProfilersManager)
} // namespace
-CpuProfiler::CpuProfiler(Isolate* isolate, CpuProfilingNamingMode naming_mode)
- : CpuProfiler(isolate, naming_mode, new CpuProfilesCollection(isolate),
- nullptr, nullptr) {}
+CpuProfiler::CpuProfiler(Isolate* isolate, CpuProfilingNamingMode naming_mode,
+ CpuProfilingLoggingMode logging_mode)
+ : CpuProfiler(isolate, naming_mode, logging_mode,
+ new CpuProfilesCollection(isolate), nullptr, nullptr) {}
CpuProfiler::CpuProfiler(Isolate* isolate, CpuProfilingNamingMode naming_mode,
+ CpuProfilingLoggingMode logging_mode,
CpuProfilesCollection* test_profiles,
ProfileGenerator* test_generator,
ProfilerEventsProcessor* test_processor)
: isolate_(isolate),
naming_mode_(naming_mode),
+ logging_mode_(logging_mode),
base_sampling_interval_(base::TimeDelta::FromMicroseconds(
FLAG_cpu_profiler_sampling_interval)),
profiles_(test_profiles),
generator_(test_generator),
processor_(test_processor),
+ code_observer_(isolate),
is_profiling_(false) {
profiles_->set_cpu_profiler(this);
GetProfilersManager()->AddProfiler(isolate, this);
+
+ if (logging_mode == kEagerLogging) EnableLogging();
}
CpuProfiler::~CpuProfiler() {
DCHECK(!is_profiling_);
GetProfilersManager()->RemoveProfiler(isolate_, this);
+
+ DisableLogging();
}
void CpuProfiler::set_sampling_interval(base::TimeDelta value) {
@@ -362,20 +462,26 @@ void CpuProfiler::set_use_precise_sampling(bool value) {
void CpuProfiler::ResetProfiles() {
profiles_.reset(new CpuProfilesCollection(isolate_));
profiles_->set_cpu_profiler(this);
- profiler_listener_.reset();
generator_.reset();
+ if (!profiling_scope_) profiler_listener_.reset();
}
-void CpuProfiler::CreateEntriesForRuntimeCallStats() {
- RuntimeCallStats* rcs = isolate_->counters()->runtime_call_stats();
- CodeMap* code_map = generator_->code_map();
- for (int i = 0; i < RuntimeCallStats::kNumberOfCounters; ++i) {
- RuntimeCallCounter* counter = rcs->GetCounter(i);
- DCHECK(counter->name());
- auto entry = new CodeEntry(CodeEventListener::FUNCTION_TAG, counter->name(),
- "native V8Runtime");
- code_map->AddCode(reinterpret_cast<Address>(counter), entry, 1);
+void CpuProfiler::EnableLogging() {
+ if (profiling_scope_) return;
+
+ if (!profiler_listener_) {
+ profiler_listener_.reset(
+ new ProfilerListener(isolate_, &code_observer_, naming_mode_));
}
+ profiling_scope_.reset(
+ new ProfilingScope(isolate_, profiler_listener_.get()));
+}
+
+void CpuProfiler::DisableLogging() {
+ if (!profiling_scope_) return;
+
+ DCHECK(profiler_listener_);
+ profiling_scope_.reset();
}
base::TimeDelta CpuProfiler::ComputeSamplingInterval() const {
@@ -418,36 +524,23 @@ void CpuProfiler::StartProcessorIfNotStarted() {
processor_->AddCurrentStack();
return;
}
- isolate_->wasm_engine()->EnableCodeLogging(isolate_);
- Logger* logger = isolate_->logger();
- bool codemap_needs_initialization = false;
+ if (!profiling_scope_) {
+ DCHECK_EQ(logging_mode_, kLazyLogging);
+ EnableLogging();
+ }
+
if (!generator_) {
- generator_.reset(new ProfileGenerator(profiles_.get()));
- codemap_needs_initialization = true;
- CreateEntriesForRuntimeCallStats();
+ generator_.reset(
+ new ProfileGenerator(profiles_.get(), code_observer_.code_map()));
}
+
base::TimeDelta sampling_interval = ComputeSamplingInterval();
- processor_.reset(new SamplingEventsProcessor(
- isolate_, generator_.get(), sampling_interval, use_precise_sampling_));
- if (profiler_listener_) {
- profiler_listener_->set_observer(processor_.get());
- } else {
- profiler_listener_.reset(
- new ProfilerListener(isolate_, processor_.get(), naming_mode_));
- }
- logger->AddCodeEventListener(profiler_listener_.get());
+ processor_.reset(
+ new SamplingEventsProcessor(isolate_, generator_.get(), &code_observer_,
+ sampling_interval, use_precise_sampling_));
is_profiling_ = true;
- // Enumerate stuff we already have in the heap.
- DCHECK(isolate_->heap()->HasBeenSetUp());
- if (codemap_needs_initialization) {
- if (!FLAG_prof_browser_mode) {
- logger->LogCodeObjects();
- }
- logger->LogCompiledFunctions();
- logger->LogAccessorCallbacks();
- LogBuiltins();
- }
+
// Enable stack sampling.
processor_->AddCurrentStack();
processor_->StartSynchronously();
@@ -471,26 +564,14 @@ void CpuProfiler::StopProcessorIfLastProfile(const char* title) {
}
void CpuProfiler::StopProcessor() {
- Logger* logger = isolate_->logger();
is_profiling_ = false;
- logger->RemoveCodeEventListener(profiler_listener_.get());
processor_->StopSynchronously();
processor_.reset();
-}
-
-void CpuProfiler::LogBuiltins() {
- Builtins* builtins = isolate_->builtins();
- DCHECK(builtins->is_initialized());
- for (int i = 0; i < Builtins::builtin_count; i++) {
- CodeEventsContainer evt_rec(CodeEventRecord::REPORT_BUILTIN);
- ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_;
- Builtins::Name id = static_cast<Builtins::Name>(i);
- rec->instruction_start = builtins->builtin(id).InstructionStart();
- rec->builtin_id = id;
- processor_->Enqueue(evt_rec);
+ DCHECK(profiling_scope_);
+ if (logging_mode_ == kLazyLogging) {
+ DisableLogging();
}
}
-
} // namespace internal
} // namespace v8
diff --git a/chromium/v8/src/profiler/cpu-profiler.h b/chromium/v8/src/profiler/cpu-profiler.h
index 96d53356e67..093f28aba34 100644
--- a/chromium/v8/src/profiler/cpu-profiler.h
+++ b/chromium/v8/src/profiler/cpu-profiler.h
@@ -13,6 +13,7 @@
#include "src/base/platform/mutex.h"
#include "src/base/platform/time.h"
#include "src/execution/isolate.h"
+#include "src/handles/maybe-handles.h"
#include "src/libsampler/sampler.h"
#include "src/profiler/circular-queue.h"
#include "src/profiler/profiler-listener.h"
@@ -30,21 +31,21 @@ class CpuProfile;
class CpuProfilesCollection;
class ProfileGenerator;
-#define CODE_EVENTS_TYPE_LIST(V) \
- V(CODE_CREATION, CodeCreateEventRecord) \
- V(CODE_MOVE, CodeMoveEventRecord) \
- V(CODE_DISABLE_OPT, CodeDisableOptEventRecord) \
- V(CODE_DEOPT, CodeDeoptEventRecord) \
+#define CODE_EVENTS_TYPE_LIST(V) \
+ V(CODE_CREATION, CodeCreateEventRecord) \
+ V(CODE_MOVE, CodeMoveEventRecord) \
+ V(CODE_DISABLE_OPT, CodeDisableOptEventRecord) \
+ V(CODE_DEOPT, CodeDeoptEventRecord) \
V(REPORT_BUILTIN, ReportBuiltinEventRecord)
+#define VM_EVENTS_TYPE_LIST(V) \
+ CODE_EVENTS_TYPE_LIST(V) \
+ V(NATIVE_CONTEXT_MOVE, NativeContextMoveEventRecord)
class CodeEventRecord {
public:
#define DECLARE_TYPE(type, ignore) type,
- enum Type {
- NONE = 0,
- CODE_EVENTS_TYPE_LIST(DECLARE_TYPE)
- };
+ enum Type { NONE = 0, VM_EVENTS_TYPE_LIST(DECLARE_TYPE) };
#undef DECLARE_TYPE
Type type;
@@ -102,6 +103,12 @@ class ReportBuiltinEventRecord : public CodeEventRecord {
V8_INLINE void UpdateCodeMap(CodeMap* code_map);
};
+// Signals that a native context's address has changed.
+class NativeContextMoveEventRecord : public CodeEventRecord {
+ public:
+ Address from_address;
+ Address to_address;
+};
class TickSampleEventRecord {
public:
@@ -124,33 +131,25 @@ class CodeEventsContainer {
union {
CodeEventRecord generic;
#define DECLARE_CLASS(ignore, type) type type##_;
- CODE_EVENTS_TYPE_LIST(DECLARE_CLASS)
+ VM_EVENTS_TYPE_LIST(DECLARE_CLASS)
#undef DECLARE_CLASS
};
};
-// Maintains the number of active CPU profilers in an isolate.
+// Maintains the number of active CPU profilers in an isolate, and routes
+// logging to a given ProfilerListener.
class ProfilingScope {
public:
- explicit ProfilingScope(Isolate* isolate) : isolate_(isolate) {
- size_t profiler_count = isolate_->num_cpu_profilers();
- profiler_count++;
- isolate_->set_num_cpu_profilers(profiler_count);
- isolate_->set_is_profiling(true);
- }
-
- ~ProfilingScope() {
- size_t profiler_count = isolate_->num_cpu_profilers();
- DCHECK_GT(profiler_count, 0);
- profiler_count--;
- isolate_->set_num_cpu_profilers(profiler_count);
- if (profiler_count == 0) isolate_->set_is_profiling(false);
- }
+ ProfilingScope(Isolate* isolate, ProfilerListener* listener);
+ ~ProfilingScope();
private:
Isolate* const isolate_;
+ ProfilerListener* const listener_;
};
+class ProfilerCodeObserver;
+
// This class implements both the profile events processor thread and
// methods called by event producers: VM and stack sampler threads.
class V8_EXPORT_PRIVATE ProfilerEventsProcessor : public base::Thread,
@@ -175,7 +174,8 @@ class V8_EXPORT_PRIVATE ProfilerEventsProcessor : public base::Thread,
virtual void SetSamplingInterval(base::TimeDelta) {}
protected:
- ProfilerEventsProcessor(Isolate* isolate, ProfileGenerator* generator);
+ ProfilerEventsProcessor(Isolate* isolate, ProfileGenerator* generator,
+ ProfilerCodeObserver* code_observer);
// Called from events processing thread (Run() method.)
bool ProcessCodeEvent();
@@ -188,6 +188,7 @@ class V8_EXPORT_PRIVATE ProfilerEventsProcessor : public base::Thread,
virtual SampleProcessingResult ProcessOneSample() = 0;
ProfileGenerator* generator_;
+ ProfilerCodeObserver* code_observer_;
base::Atomic32 running_;
base::ConditionVariable running_cond_;
base::Mutex running_mutex_;
@@ -196,13 +197,13 @@ class V8_EXPORT_PRIVATE ProfilerEventsProcessor : public base::Thread,
std::atomic<unsigned> last_code_event_id_;
unsigned last_processed_code_event_id_;
Isolate* isolate_;
- ProfilingScope profiling_scope_;
};
class V8_EXPORT_PRIVATE SamplingEventsProcessor
: public ProfilerEventsProcessor {
public:
SamplingEventsProcessor(Isolate* isolate, ProfileGenerator* generator,
+ ProfilerCodeObserver* code_observer,
base::TimeDelta period, bool use_precise_sampling);
~SamplingEventsProcessor() override;
@@ -241,11 +242,47 @@ class V8_EXPORT_PRIVATE SamplingEventsProcessor
// low sampling intervals on Windows.
};
+// Builds and maintains a CodeMap tracking code objects on the VM heap. While
+// alive, logs generated code, callbacks, and builtins from the isolate.
+// Redirects events to the profiler events processor when present.
+class V8_EXPORT_PRIVATE ProfilerCodeObserver : public CodeEventObserver {
+ public:
+ explicit ProfilerCodeObserver(Isolate*);
+
+ void CodeEventHandler(const CodeEventsContainer& evt_rec) override;
+
+ CodeMap* code_map() { return &code_map_; }
+
+ private:
+ friend class ProfilerEventsProcessor;
+
+ void CodeEventHandlerInternal(const CodeEventsContainer& evt_rec);
+
+ void CreateEntriesForRuntimeCallStats();
+ void LogBuiltins();
+
+ ProfilerEventsProcessor* processor() { return processor_; }
+
+ // Redirects code events to be enqueued on the given events processor.
+ void set_processor(ProfilerEventsProcessor* processor) {
+ processor_ = processor;
+ }
+
+ // Stops redirection of code events onto an events processor.
+ void clear_processor() { processor_ = nullptr; }
+
+ Isolate* const isolate_;
+ CodeMap code_map_;
+ ProfilerEventsProcessor* processor_;
+};
+
class V8_EXPORT_PRIVATE CpuProfiler {
public:
- explicit CpuProfiler(Isolate* isolate, CpuProfilingNamingMode = kDebugNaming);
+ explicit CpuProfiler(Isolate* isolate, CpuProfilingNamingMode = kDebugNaming,
+ CpuProfilingLoggingMode = kLazyLogging);
CpuProfiler(Isolate* isolate, CpuProfilingNamingMode naming_mode,
+ CpuProfilingLoggingMode logging_mode,
CpuProfilesCollection* profiles, ProfileGenerator* test_generator,
ProfilerEventsProcessor* test_processor);
@@ -255,6 +292,7 @@ class V8_EXPORT_PRIVATE CpuProfiler {
using ProfilingMode = v8::CpuProfilingMode;
using NamingMode = v8::CpuProfilingNamingMode;
+ using LoggingMode = v8::CpuProfilingLoggingMode;
base::TimeDelta sampling_interval() const { return base_sampling_interval_; }
void set_sampling_interval(base::TimeDelta value);
@@ -262,6 +300,7 @@ class V8_EXPORT_PRIVATE CpuProfiler {
void CollectSample();
void StartProfiling(const char* title, CpuProfilingOptions options = {});
void StartProfiling(String title, CpuProfilingOptions options = {});
+
CpuProfile* StopProfiling(const char* title);
CpuProfile* StopProfiling(String title);
int GetProfilesCount();
@@ -284,8 +323,9 @@ class V8_EXPORT_PRIVATE CpuProfiler {
void StopProcessorIfLastProfile(const char* title);
void StopProcessor();
void ResetProfiles();
- void LogBuiltins();
- void CreateEntriesForRuntimeCallStats();
+
+ void EnableLogging();
+ void DisableLogging();
// Computes a sampling interval sufficient to accomodate attached profiles.
base::TimeDelta ComputeSamplingInterval() const;
@@ -295,6 +335,7 @@ class V8_EXPORT_PRIVATE CpuProfiler {
Isolate* const isolate_;
const NamingMode naming_mode_;
+ const LoggingMode logging_mode_;
bool use_precise_sampling_ = true;
// Sampling interval to which per-profile sampling intervals will be clamped
// to a multiple of, or used as the default if unspecified.
@@ -303,6 +344,8 @@ class V8_EXPORT_PRIVATE CpuProfiler {
std::unique_ptr<ProfileGenerator> generator_;
std::unique_ptr<ProfilerEventsProcessor> processor_;
std::unique_ptr<ProfilerListener> profiler_listener_;
+ std::unique_ptr<ProfilingScope> profiling_scope_;
+ ProfilerCodeObserver code_observer_;
bool is_profiling_;
DISALLOW_COPY_AND_ASSIGN(CpuProfiler);
diff --git a/chromium/v8/src/profiler/heap-profiler.cc b/chromium/v8/src/profiler/heap-profiler.cc
index 472dbdbb10b..a498e8e2143 100644
--- a/chromium/v8/src/profiler/heap-profiler.cc
+++ b/chromium/v8/src/profiler/heap-profiler.cc
@@ -151,6 +151,17 @@ SnapshotObjectId HeapProfiler::GetSnapshotObjectId(Handle<Object> obj) {
return ids_->FindEntry(HeapObject::cast(*obj).address());
}
+SnapshotObjectId HeapProfiler::GetSnapshotObjectId(NativeObject obj) {
+ // Try to find id of regular native node first.
+ SnapshotObjectId id = ids_->FindEntry(reinterpret_cast<Address>(obj));
+ // In case no id has been found, check whether there exists an entry where the
+ // native objects has been merged into a V8 entry.
+ if (id == v8::HeapProfiler::kUnknownObjectId) {
+ id = ids_->FindMergedNativeEntry(obj);
+ }
+ return id;
+}
+
void HeapProfiler::ObjectMoveEvent(Address from, Address to, int size) {
base::MutexGuard guard(&profiler_mutex_);
bool known_object = ids_->MoveObject(from, to, size);
diff --git a/chromium/v8/src/profiler/heap-profiler.h b/chromium/v8/src/profiler/heap-profiler.h
index 940574282ef..f7336eb6be5 100644
--- a/chromium/v8/src/profiler/heap-profiler.h
+++ b/chromium/v8/src/profiler/heap-profiler.h
@@ -52,6 +52,7 @@ class HeapProfiler : public HeapObjectAllocationTracker {
int GetSnapshotsCount();
HeapSnapshot* GetSnapshot(int index);
SnapshotObjectId GetSnapshotObjectId(Handle<Object> obj);
+ SnapshotObjectId GetSnapshotObjectId(NativeObject obj);
void DeleteAllSnapshots();
void RemoveSnapshot(HeapSnapshot* snapshot);
diff --git a/chromium/v8/src/profiler/heap-snapshot-generator.cc b/chromium/v8/src/profiler/heap-snapshot-generator.cc
index df941eda96a..75b6aa7b77e 100644
--- a/chromium/v8/src/profiler/heap-snapshot-generator.cc
+++ b/chromium/v8/src/profiler/heap-snapshot-generator.cc
@@ -352,7 +352,7 @@ void HeapObjectsMap::UpdateObjectSize(Address addr, int size) {
SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) {
base::HashMap::Entry* entry = entries_map_.Lookup(
reinterpret_cast<void*>(addr), ComputeAddressHash(addr));
- if (entry == nullptr) return 0;
+ if (entry == nullptr) return v8::HeapProfiler::kUnknownObjectId;
int entry_index = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
EntryInfo& entry_info = entries_.at(entry_index);
DCHECK(static_cast<uint32_t>(entries_.size()) > entries_map_.occupancy());
@@ -386,6 +386,25 @@ SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr,
return id;
}
+SnapshotObjectId HeapObjectsMap::FindMergedNativeEntry(NativeObject addr) {
+ auto it = merged_native_entries_map_.find(addr);
+ if (it == merged_native_entries_map_.end())
+ return v8::HeapProfiler::kUnknownObjectId;
+ return entries_[it->second].id;
+}
+
+void HeapObjectsMap::AddMergedNativeEntry(NativeObject addr,
+ Address canonical_addr) {
+ base::HashMap::Entry* entry =
+ entries_map_.Lookup(reinterpret_cast<void*>(canonical_addr),
+ ComputeAddressHash(canonical_addr));
+ auto result = merged_native_entries_map_.insert(
+ {addr, reinterpret_cast<size_t>(entry->value)});
+ if (!result.second) {
+ result.first->second = reinterpret_cast<size_t>(entry->value);
+ }
+}
+
void HeapObjectsMap::StopHeapObjectsTracking() { time_intervals_.clear(); }
void HeapObjectsMap::UpdateHeapObjectsMap() {
@@ -465,9 +484,20 @@ SnapshotObjectId HeapObjectsMap::PushHeapObjectsStats(OutputStream* stream,
void HeapObjectsMap::RemoveDeadEntries() {
DCHECK(entries_.size() > 0 && entries_.at(0).id == 0 &&
entries_.at(0).addr == kNullAddress);
+
+ // Build up temporary reverse map.
+ std::unordered_map<size_t, NativeObject> reverse_merged_native_entries_map;
+ for (const auto& it : merged_native_entries_map_) {
+ auto result =
+ reverse_merged_native_entries_map.emplace(it.second, it.first);
+ DCHECK(result.second);
+ USE(result);
+ }
+
size_t first_free_entry = 1;
for (size_t i = 1; i < entries_.size(); ++i) {
EntryInfo& entry_info = entries_.at(i);
+ auto merged_reverse_it = reverse_merged_native_entries_map.find(i);
if (entry_info.accessed) {
if (first_free_entry != i) {
entries_.at(first_free_entry) = entry_info;
@@ -478,11 +508,19 @@ void HeapObjectsMap::RemoveDeadEntries() {
ComputeAddressHash(entry_info.addr));
DCHECK(entry);
entry->value = reinterpret_cast<void*>(first_free_entry);
+ if (merged_reverse_it != reverse_merged_native_entries_map.end()) {
+ auto it = merged_native_entries_map_.find(merged_reverse_it->second);
+ DCHECK_NE(merged_native_entries_map_.end(), it);
+ it->second = first_free_entry;
+ }
++first_free_entry;
} else {
if (entry_info.addr) {
entries_map_.Remove(reinterpret_cast<void*>(entry_info.addr),
ComputeAddressHash(entry_info.addr));
+ if (merged_reverse_it != reverse_merged_native_entries_map.end()) {
+ merged_native_entries_map_.erase(merged_reverse_it->second);
+ }
}
}
}
@@ -1853,10 +1891,14 @@ HeapEntry* EmbedderGraphEntriesAllocator::AllocateEntry(HeapThing ptr) {
reinterpret_cast<EmbedderGraphImpl::Node*>(ptr);
DCHECK(node->IsEmbedderNode());
size_t size = node->SizeInBytes();
- return snapshot_->AddEntry(
- EmbedderGraphNodeType(node), EmbedderGraphNodeName(names_, node),
- static_cast<SnapshotObjectId>(reinterpret_cast<uintptr_t>(node) << 1),
- static_cast<int>(size), 0);
+ Address lookup_address = reinterpret_cast<Address>(node->GetNativeObject());
+ SnapshotObjectId id =
+ (lookup_address) ? heap_object_map_->FindOrAddEntry(lookup_address, 0)
+ : static_cast<SnapshotObjectId>(
+ reinterpret_cast<uintptr_t>(node) << 1);
+ return snapshot_->AddEntry(EmbedderGraphNodeType(node),
+ EmbedderGraphNodeName(names_, node), id,
+ static_cast<int>(size), 0);
}
NativeObjectsExplorer::NativeObjectsExplorer(
@@ -1865,12 +1907,14 @@ NativeObjectsExplorer::NativeObjectsExplorer(
Isolate::FromHeap(snapshot->profiler()->heap_object_map()->heap())),
snapshot_(snapshot),
names_(snapshot_->profiler()->names()),
+ heap_object_map_(snapshot_->profiler()->heap_object_map()),
embedder_graph_entries_allocator_(
new EmbedderGraphEntriesAllocator(snapshot)) {}
HeapEntry* NativeObjectsExplorer::EntryForEmbedderGraphNode(
EmbedderGraphImpl::Node* node) {
EmbedderGraphImpl::Node* wrapper = node->WrapperNode();
+ NativeObject native_object = node->GetNativeObject();
if (wrapper) {
node = wrapper;
}
@@ -1882,8 +1926,16 @@ HeapEntry* NativeObjectsExplorer::EntryForEmbedderGraphNode(
static_cast<EmbedderGraphImpl::V8NodeImpl*>(node);
Object object = v8_node->GetObject();
if (object.IsSmi()) return nullptr;
- return generator_->FindEntry(
+ HeapEntry* entry = generator_->FindEntry(
reinterpret_cast<void*>(Object::cast(object).ptr()));
+ if (native_object) {
+ HeapObject heap_object = HeapObject::cast(object);
+ heap_object_map_->AddMergedNativeEntry(native_object,
+ heap_object.address());
+ DCHECK_EQ(entry->id(),
+ heap_object_map_->FindMergedNativeEntry(native_object));
+ }
+ return entry;
}
}
@@ -1945,13 +1997,13 @@ HeapSnapshotGenerator::HeapSnapshotGenerator(
}
namespace {
-class NullContextScope {
+class NullContextForSnapshotScope {
public:
- explicit NullContextScope(Isolate* isolate)
+ explicit NullContextForSnapshotScope(Isolate* isolate)
: isolate_(isolate), prev_(isolate->context()) {
isolate_->set_context(Context());
}
- ~NullContextScope() { isolate_->set_context(prev_); }
+ ~NullContextForSnapshotScope() { isolate_->set_context(prev_); }
private:
Isolate* isolate_;
@@ -1971,7 +2023,7 @@ bool HeapSnapshotGenerator::GenerateSnapshot() {
heap_->PreciseCollectAllGarbage(Heap::kNoGCFlags,
GarbageCollectionReason::kHeapProfiler);
- NullContextScope null_context_scope(Isolate::FromHeap(heap_));
+ NullContextForSnapshotScope null_context_scope(Isolate::FromHeap(heap_));
#ifdef VERIFY_HEAP
Heap* debug_heap = heap_;
diff --git a/chromium/v8/src/profiler/heap-snapshot-generator.h b/chromium/v8/src/profiler/heap-snapshot-generator.h
index d3d3330e27b..360ed1f0092 100644
--- a/chromium/v8/src/profiler/heap-snapshot-generator.h
+++ b/chromium/v8/src/profiler/heap-snapshot-generator.h
@@ -82,8 +82,8 @@ class HeapGraphEdge {
V8_INLINE HeapSnapshot* snapshot() const;
int from_index() const { return FromIndexField::decode(bit_field_); }
- class TypeField : public BitField<Type, 0, 3> {};
- class FromIndexField : public BitField<int, 3, 29> {};
+ using TypeField = BitField<Type, 0, 3>;
+ using FromIndexField = BitField<int, 3, 29>;
uint32_t bit_field_;
HeapEntry* to_entry_;
union {
@@ -249,6 +249,8 @@ class HeapObjectsMap {
SnapshotObjectId FindOrAddEntry(Address addr,
unsigned int size,
bool accessed = true);
+ SnapshotObjectId FindMergedNativeEntry(NativeObject addr);
+ void AddMergedNativeEntry(NativeObject addr, Address canonical_addr);
bool MoveObject(Address from, Address to, int size);
void UpdateObjectSize(Address addr, int size);
SnapshotObjectId last_assigned_id() const {
@@ -285,6 +287,8 @@ class HeapObjectsMap {
base::HashMap entries_map_;
std::vector<EntryInfo> entries_;
std::vector<TimeInterval> time_intervals_;
+ // Map from NativeObject to EntryInfo index in entries_.
+ std::unordered_map<NativeObject, size_t> merged_native_entries_map_;
Heap* heap_;
DISALLOW_COPY_AND_ASSIGN(HeapObjectsMap);
@@ -453,6 +457,7 @@ class NativeObjectsExplorer {
Isolate* isolate_;
HeapSnapshot* snapshot_;
StringsStorage* names_;
+ HeapObjectsMap* heap_object_map_;
std::unique_ptr<HeapEntriesAllocator> embedder_graph_entries_allocator_;
// Used during references extraction.
HeapSnapshotGenerator* generator_ = nullptr;
diff --git a/chromium/v8/src/profiler/profile-generator-inl.h b/chromium/v8/src/profiler/profile-generator-inl.h
index 2d73c20a37f..bb5ef0da5b7 100644
--- a/chromium/v8/src/profiler/profile-generator-inl.h
+++ b/chromium/v8/src/profiler/profile-generator-inl.h
@@ -28,7 +28,7 @@ CodeEntry::CodeEntry(CodeEventListener::LogEventsAndTags tag, const char* name,
instruction_start_(instruction_start) {}
inline CodeEntry* ProfileGenerator::FindEntry(Address address) {
- CodeEntry* entry = code_map_.FindEntry(address);
+ CodeEntry* entry = code_map_->FindEntry(address);
if (entry) entry->mark_used();
return entry;
}
diff --git a/chromium/v8/src/profiler/profile-generator.cc b/chromium/v8/src/profiler/profile-generator.cc
index e869f657627..f5f71846136 100644
--- a/chromium/v8/src/profiler/profile-generator.cc
+++ b/chromium/v8/src/profiler/profile-generator.cc
@@ -19,6 +19,14 @@ void SourcePositionTable::SetPosition(int pc_offset, int line,
int inlining_id) {
DCHECK_GE(pc_offset, 0);
DCHECK_GT(line, 0); // The 1-based number of the source line.
+ // It's possible that we map multiple source positions to a pc_offset in
+ // optimized code. Usually these map to the same line, so there is no
+ // difference here as we only store line number and not line/col in the form
+ // of a script offset. Ignore any subsequent sets to the same offset.
+ if (!pc_offsets_to_lines_.empty() &&
+ pc_offsets_to_lines_.back().pc_offset == pc_offset) {
+ return;
+ }
// Check that we are inserting in ascending order, so that the vector remains
// sorted.
DCHECK(pc_offsets_to_lines_.empty() ||
@@ -404,16 +412,18 @@ ProfileNode* ProfileTree::AddPathFromEnd(const std::vector<CodeEntry*>& path,
ProfileNode* ProfileTree::AddPathFromEnd(const ProfileStackTrace& path,
int src_line, bool update_stats,
- ProfilingMode mode) {
+ ProfilingMode mode,
+ ContextFilter* context_filter) {
ProfileNode* node = root_;
CodeEntry* last_entry = nullptr;
int parent_line_number = v8::CpuProfileNode::kNoLineNumberInfo;
for (auto it = path.rbegin(); it != path.rend(); ++it) {
- if ((*it).code_entry == nullptr) continue;
- last_entry = (*it).code_entry;
- node = node->FindOrAddChild((*it).code_entry, parent_line_number);
+ if (it->entry.code_entry == nullptr) continue;
+ if (context_filter && !context_filter->Accept(*it)) continue;
+ last_entry = (*it).entry.code_entry;
+ node = node->FindOrAddChild((*it).entry.code_entry, parent_line_number);
parent_line_number = mode == ProfilingMode::kCallerLineNumbers
- ? (*it).line_number
+ ? (*it).entry.line_number
: v8::CpuProfileNode::kNoLineNumberInfo;
}
if (last_entry && last_entry->has_deopt_info()) {
@@ -428,7 +438,6 @@ ProfileNode* ProfileTree::AddPathFromEnd(const ProfileStackTrace& path,
return node;
}
-
class Position {
public:
explicit Position(ProfileNode* node)
@@ -470,6 +479,21 @@ void ProfileTree::TraverseDepthFirst(Callback* callback) {
}
}
+bool ContextFilter::Accept(const ProfileStackFrame& frame) {
+ // If a frame should always be included in profiles (e.g. metadata frames),
+ // skip the context check.
+ if (!frame.filterable) return true;
+
+ // Strip heap object tag from frame.
+ return (frame.native_context & ~kHeapObjectTag) == native_context_address_;
+}
+
+void ContextFilter::OnMoveEvent(Address from_address, Address to_address) {
+ if (native_context_address() != from_address) return;
+
+ set_native_context_address(to_address);
+}
+
using v8::tracing::TracedValue;
std::atomic<uint32_t> CpuProfile::last_id_;
@@ -488,6 +512,13 @@ CpuProfile::CpuProfile(CpuProfiler* profiler, const char* title,
(start_time_ - base::TimeTicks()).InMicroseconds());
TRACE_EVENT_SAMPLE_WITH_ID1(TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"),
"Profile", id_, "data", std::move(value));
+
+ if (options_.has_filter_context()) {
+ DisallowHeapAllocation no_gc;
+ i::Address raw_filter_context =
+ reinterpret_cast<i::Address>(options_.raw_filter_context());
+ context_filter_ = base::make_unique<ContextFilter>(raw_filter_context);
+ }
}
bool CpuProfile::CheckSubsample(base::TimeDelta source_sampling_interval) {
@@ -512,11 +543,11 @@ void CpuProfile::AddPath(base::TimeTicks timestamp,
bool update_stats, base::TimeDelta sampling_interval) {
if (!CheckSubsample(sampling_interval)) return;
- ProfileNode* top_frame_node =
- top_down_.AddPathFromEnd(path, src_line, update_stats, options_.mode());
+ ProfileNode* top_frame_node = top_down_.AddPathFromEnd(
+ path, src_line, update_stats, options_.mode(), context_filter_.get());
bool should_record_sample =
- !timestamp.IsNull() &&
+ !timestamp.IsNull() && timestamp >= start_time_ &&
(options_.max_samples() == CpuProfilingOptions::kNoSampleLimit ||
samples_.size() < options_.max_samples());
@@ -615,6 +646,8 @@ void CpuProfile::StreamPendingTraceEvents() {
void CpuProfile::FinishProfile() {
end_time_ = base::TimeTicks::HighResolutionNow();
+ // Stop tracking context movements after profiling stops.
+ context_filter_ = nullptr;
StreamPendingTraceEvents();
auto value = TracedValue::Create();
value->SetDouble("endTime", (end_time_ - base::TimeTicks()).InMicroseconds());
@@ -825,8 +858,20 @@ void CpuProfilesCollection::AddPathToCurrentProfiles(
current_profiles_semaphore_.Signal();
}
-ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
- : profiles_(profiles) {}
+void CpuProfilesCollection::UpdateNativeContextAddressForCurrentProfiles(
+ Address from, Address to) {
+ current_profiles_semaphore_.Wait();
+ for (const std::unique_ptr<CpuProfile>& profile : current_profiles_) {
+ if (auto* context_filter = profile->context_filter()) {
+ context_filter->OnMoveEvent(from, to);
+ }
+ }
+ current_profiles_semaphore_.Signal();
+}
+
+ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles,
+ CodeMap* code_map)
+ : profiles_(profiles), code_map_(code_map) {}
void ProfileGenerator::RecordTickSample(const TickSample& sample) {
ProfileStackTrace stack_trace;
@@ -848,9 +893,11 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
// Don't use PC when in external callback code, as it can point
// inside a callback's code, and we will erroneously report
// that a callback calls itself.
- stack_trace.push_back(
- {FindEntry(reinterpret_cast<Address>(sample.external_callback_entry)),
- no_line_info});
+ stack_trace.push_back({{FindEntry(reinterpret_cast<Address>(
+ sample.external_callback_entry)),
+ no_line_info},
+ reinterpret_cast<Address>(sample.top_context),
+ true});
} else {
Address attributed_pc = reinterpret_cast<Address>(sample.pc);
CodeEntry* pc_entry = FindEntry(attributed_pc);
@@ -874,7 +921,9 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
src_line = pc_entry->line_number();
}
src_line_not_found = false;
- stack_trace.push_back({pc_entry, src_line});
+ stack_trace.push_back({{pc_entry, src_line},
+ reinterpret_cast<Address>(sample.top_context),
+ true});
if (pc_entry->builtin_id() == Builtins::kFunctionPrototypeApply ||
pc_entry->builtin_id() == Builtins::kFunctionPrototypeCall) {
@@ -886,7 +935,9 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
// 'unresolved' entry.
if (!sample.has_external_callback) {
stack_trace.push_back(
- {CodeEntry::unresolved_entry(), no_line_info});
+ {{CodeEntry::unresolved_entry(), no_line_info},
+ kNullAddress,
+ true});
}
}
}
@@ -894,6 +945,7 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
for (unsigned i = 0; i < sample.frames_count; ++i) {
Address stack_pos = reinterpret_cast<Address>(sample.stack[i]);
+ Address native_context = reinterpret_cast<Address>(sample.contexts[i]);
CodeEntry* entry = FindEntry(stack_pos);
int line_number = no_line_info;
if (entry) {
@@ -905,8 +957,13 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
entry->GetInlineStack(pc_offset);
if (inline_stack) {
int most_inlined_frame_line_number = entry->GetSourceLine(pc_offset);
- stack_trace.insert(stack_trace.end(), inline_stack->begin(),
- inline_stack->end());
+ for (auto entry : *inline_stack) {
+ // Set the native context of inlined frames to be equal to that of
+ // their parent. This is safe, as functions cannot inline themselves
+ // into a parent from another native context.
+ stack_trace.push_back({entry, native_context, true});
+ }
+
// This is a bit of a messy hack. The line number for the most-inlined
// frame (the function at the end of the chain of function calls) has
// the wrong line number in inline_stack. The actual line number in
@@ -916,7 +973,7 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
// inlining_id.
DCHECK(!inline_stack->empty());
size_t index = stack_trace.size() - inline_stack->size();
- stack_trace[index].line_number = most_inlined_frame_line_number;
+ stack_trace[index].entry.line_number = most_inlined_frame_line_number;
}
// Skip unresolved frames (e.g. internal frame) and get source line of
// the first JS caller.
@@ -935,21 +992,22 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
// so we use it instead of pushing entry to stack_trace.
if (inline_stack) continue;
}
- stack_trace.push_back({entry, line_number});
+ stack_trace.push_back({{entry, line_number}, native_context, true});
}
}
if (FLAG_prof_browser_mode) {
bool no_symbolized_entries = true;
for (auto e : stack_trace) {
- if (e.code_entry != nullptr) {
+ if (e.entry.code_entry != nullptr) {
no_symbolized_entries = false;
break;
}
}
// If no frames were symbolized, put the VM state entry in.
if (no_symbolized_entries) {
- stack_trace.push_back({EntryForVMState(sample.state), no_line_info});
+ stack_trace.push_back(
+ {{EntryForVMState(sample.state), no_line_info}, kNullAddress, false});
}
}
@@ -958,6 +1016,10 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
sample.sampling_interval);
}
+void ProfileGenerator::UpdateNativeContextAddress(Address from, Address to) {
+ profiles_->UpdateNativeContextAddressForCurrentProfiles(from, to);
+}
+
CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
switch (tag) {
case GC:
diff --git a/chromium/v8/src/profiler/profile-generator.h b/chromium/v8/src/profiler/profile-generator.h
index b0543c9d794..2f7273a0862 100644
--- a/chromium/v8/src/profiler/profile-generator.h
+++ b/chromium/v8/src/profiler/profile-generator.h
@@ -234,7 +234,36 @@ struct CodeEntryAndLineNumber {
int line_number;
};
-using ProfileStackTrace = std::vector<CodeEntryAndLineNumber>;
+struct ProfileStackFrame {
+ CodeEntryAndLineNumber entry;
+ Address native_context;
+ bool filterable; // If true, the frame should be filtered by context (if a
+ // filter is present).
+};
+
+typedef std::vector<ProfileStackFrame> ProfileStackTrace;
+
+// Filters stack frames from sources other than a target native context.
+class ContextFilter {
+ public:
+ explicit ContextFilter(Address native_context_address)
+ : native_context_address_(native_context_address) {}
+
+ // Returns true if the stack frame passes a context check.
+ bool Accept(const ProfileStackFrame&);
+
+ // Invoked when a native context has changed address.
+ void OnMoveEvent(Address from_address, Address to_address);
+
+ // Update the context's tracked address based on VM-thread events.
+ void set_native_context_address(Address address) {
+ native_context_address_ = address;
+ }
+ Address native_context_address() const { return native_context_address_; }
+
+ private:
+ Address native_context_address_;
+};
class ProfileTree;
@@ -321,7 +350,8 @@ class V8_EXPORT_PRIVATE ProfileTree {
const ProfileStackTrace& path,
int src_line = v8::CpuProfileNode::kNoLineNumberInfo,
bool update_stats = true,
- ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers);
+ ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers,
+ ContextFilter* context_filter = nullptr);
ProfileNode* root() const { return root_; }
unsigned next_node_id() { return next_node_id_++; }
unsigned GetFunctionId(const ProfileNode* node);
@@ -389,6 +419,7 @@ class CpuProfile {
base::TimeTicks start_time() const { return start_time_; }
base::TimeTicks end_time() const { return end_time_; }
CpuProfiler* cpu_profiler() const { return profiler_; }
+ ContextFilter* context_filter() const { return context_filter_.get(); }
void UpdateTicksScale();
@@ -399,6 +430,7 @@ class CpuProfile {
const char* title_;
const CpuProfilingOptions options_;
+ std::unique_ptr<ContextFilter> context_filter_;
base::TimeTicks start_time_;
base::TimeTicks end_time_;
std::deque<SampleInfo> samples_;
@@ -477,6 +509,9 @@ class V8_EXPORT_PRIVATE CpuProfilesCollection {
bool update_stats,
base::TimeDelta sampling_interval);
+ // Called from profile generator thread.
+ void UpdateNativeContextAddressForCurrentProfiles(Address from, Address to);
+
// Limits the number of profiles that can be simultaneously collected.
static const int kMaxSimultaneousProfiles = 100;
@@ -494,18 +529,20 @@ class V8_EXPORT_PRIVATE CpuProfilesCollection {
class V8_EXPORT_PRIVATE ProfileGenerator {
public:
- explicit ProfileGenerator(CpuProfilesCollection* profiles);
+ explicit ProfileGenerator(CpuProfilesCollection* profiles, CodeMap* code_map);
void RecordTickSample(const TickSample& sample);
- CodeMap* code_map() { return &code_map_; }
+ void UpdateNativeContextAddress(Address from, Address to);
+
+ CodeMap* code_map() { return code_map_; }
private:
CodeEntry* FindEntry(Address address);
CodeEntry* EntryForVMState(StateTag tag);
CpuProfilesCollection* profiles_;
- CodeMap code_map_;
+ CodeMap* const code_map_;
DISALLOW_COPY_AND_ASSIGN(ProfileGenerator);
};
diff --git a/chromium/v8/src/profiler/profiler-listener.cc b/chromium/v8/src/profiler/profiler-listener.cc
index 156c1b8bb01..b00c1f5cfd7 100644
--- a/chromium/v8/src/profiler/profiler-listener.cc
+++ b/chromium/v8/src/profiler/profiler-listener.cc
@@ -177,8 +177,7 @@ void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
CodeEntry* cached_entry = GetOrInsertCachedEntry(
&cached_inline_entries, std::move(inline_entry));
- inline_stack.push_back(
- CodeEntryAndLineNumber{cached_entry, line_number});
+ inline_stack.push_back({cached_entry, line_number});
}
DCHECK(!inline_stack.empty());
inline_stacks.emplace(inlining_id, std::move(inline_stack));
@@ -280,6 +279,13 @@ void ProfilerListener::SetterCallbackEvent(Name name, Address entry_point) {
DispatchCodeEvent(evt_rec);
}
+void ProfilerListener::NativeContextMoveEvent(Address from, Address to) {
+ CodeEventsContainer evt_rec(CodeEventRecord::NATIVE_CONTEXT_MOVE);
+ evt_rec.NativeContextMoveEventRecord_.from_address = from;
+ evt_rec.NativeContextMoveEventRecord_.to_address = to;
+ DispatchCodeEvent(evt_rec);
+}
+
Name ProfilerListener::InferScriptName(Name name, SharedFunctionInfo info) {
if (name.IsString() && String::cast(name).length()) return name;
if (!info.script().IsScript()) return name;
diff --git a/chromium/v8/src/profiler/profiler-listener.h b/chromium/v8/src/profiler/profiler-listener.h
index 6ca4225e540..85070d65aaf 100644
--- a/chromium/v8/src/profiler/profiler-listener.h
+++ b/chromium/v8/src/profiler/profiler-listener.h
@@ -55,6 +55,7 @@ class V8_EXPORT_PRIVATE ProfilerListener : public CodeEventListener {
void RegExpCodeCreateEvent(AbstractCode code, String source) override;
void SetterCallbackEvent(Name name, Address entry_point) override;
void SharedFunctionInfoMoveEvent(Address from, Address to) override {}
+ void NativeContextMoveEvent(Address from, Address to) override;
const char* GetName(Name name) {
return function_and_resource_names_.GetName(name);
diff --git a/chromium/v8/src/profiler/tick-sample.cc b/chromium/v8/src/profiler/tick-sample.cc
index b3ea07db34f..5c2f2d63ce3 100644
--- a/chromium/v8/src/profiler/tick-sample.cc
+++ b/chromium/v8/src/profiler/tick-sample.cc
@@ -16,6 +16,7 @@
#include "src/sanitizer/msan.h"
namespace v8 {
+namespace internal {
namespace {
bool IsSamePage(i::Address ptr1, i::Address ptr2) {
@@ -78,11 +79,6 @@ bool IsNoFrameRegion(i::Address address) {
return false;
}
-} // namespace
-
-namespace internal {
-namespace {
-
#if defined(USE_SIMULATOR)
class SimulatorHelper {
public:
@@ -147,22 +143,56 @@ bool SimulatorHelper::FillRegisters(Isolate* isolate,
}
#endif // USE_SIMULATOR
+// Attempts to safely dereference the address of a native context at a given
+// context's address. Returns kNullAddress on failure, in the event that the
+// context is in an inconsistent state.
+Address ScrapeNativeContextAddress(Heap* heap, Address context_address) {
+ DCHECK_EQ(heap->gc_state(), Heap::NOT_IN_GC);
+
+ if (!HAS_STRONG_HEAP_OBJECT_TAG(context_address)) return kNullAddress;
+
+ if (heap->memory_allocator()->IsOutsideAllocatedSpace(context_address))
+ return kNullAddress;
+
+ // Note that once a native context has been assigned to a context, the slot
+ // is no longer mutated except during pointer updates / evictions. Since
+ // pointer updates exclusively occur on the main thread, and we don't record
+ // TickSamples when the main thread's VM state is GC, the only other
+ // situation where the address here would be invalid is if it's being
+ // reassigned -- which isn't possible.
+ int native_context_offset =
+ i::Context::SlotOffset(i::Context::NATIVE_CONTEXT_INDEX);
+ i::Address native_context_slot_address =
+ context_address + native_context_offset;
+
+ // By the prior hypothesis, the indirect native context address should always
+ // be valid.
+ if (heap->memory_allocator()->IsOutsideAllocatedSpace(
+ native_context_slot_address)) {
+ DCHECK(false);
+ return kNullAddress;
+ }
+
+ i::ObjectSlot native_context_slot(native_context_slot_address);
+ i::Object native_context = native_context_slot.Relaxed_Load();
+
+ return native_context.ptr();
+}
+
} // namespace
-} // namespace internal
-//
-// StackTracer implementation
-//
DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate,
const RegisterState& reg_state,
RecordCEntryFrame record_c_entry_frame,
bool update_stats,
- bool use_simulator_reg_state) {
+ bool use_simulator_reg_state,
+ base::TimeDelta sampling_interval) {
this->update_stats = update_stats;
SampleInfo info;
RegisterState regs = reg_state;
if (!GetStackSample(v8_isolate, &regs, record_c_entry_frame, stack,
- kMaxFramesCount, &info, use_simulator_reg_state)) {
+ kMaxFramesCount, &info, use_simulator_reg_state,
+ contexts)) {
// It is executing JS but failed to collect a stack trace.
// Mark the sample as spoiled.
pc = nullptr;
@@ -173,6 +203,7 @@ DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate,
pc = regs.pc;
frames_count = static_cast<unsigned>(info.frames_count);
has_external_callback = info.external_callback_entry != nullptr;
+ top_context = info.top_context;
if (has_external_callback) {
external_callback_entry = info.external_callback_entry;
} else if (frames_count) {
@@ -191,17 +222,20 @@ DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate,
} else {
tos = nullptr;
}
+ this->sampling_interval = sampling_interval;
+ timestamp = base::TimeTicks::HighResolutionNow();
}
bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs,
RecordCEntryFrame record_c_entry_frame,
void** frames, size_t frames_limit,
v8::SampleInfo* sample_info,
- bool use_simulator_reg_state) {
+ bool use_simulator_reg_state, void** contexts) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
sample_info->frames_count = 0;
sample_info->vm_state = isolate->current_vm_state();
sample_info->external_callback_entry = nullptr;
+ sample_info->top_context = nullptr;
if (sample_info->vm_state == GC) return true;
i::Address js_entry_sp = isolate->js_entry_sp();
@@ -229,7 +263,7 @@ bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs,
i::ExternalCallbackScope* scope = isolate->external_callback_scope();
i::Address handler = i::Isolate::handler(isolate->thread_local_top());
// If there is a handler on top of the external callback scope then
- // we have already entrered JavaScript again and the external callback
+ // we have already entered JavaScript again and the external callback
// is not the top function.
if (scope && scope->scope_address() < handler) {
i::Address* external_callback_entry_ptr =
@@ -245,23 +279,62 @@ bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs,
reinterpret_cast<i::Address>(regs->sp),
reinterpret_cast<i::Address>(regs->lr),
js_entry_sp);
+
+ i::Address top_context_address = it.top_context_address();
+ if (top_context_address != i::kNullAddress) {
+ sample_info->top_context = reinterpret_cast<void*>(
+ i::ScrapeNativeContextAddress(isolate->heap(), top_context_address));
+ } else {
+ sample_info->top_context = nullptr;
+ }
+
if (it.done()) return true;
size_t i = 0;
if (record_c_entry_frame == kIncludeCEntryFrame &&
(it.top_frame_type() == internal::StackFrame::EXIT ||
it.top_frame_type() == internal::StackFrame::BUILTIN_EXIT)) {
- frames[i++] = reinterpret_cast<void*>(isolate->c_function());
+ frames[i] = reinterpret_cast<void*>(isolate->c_function());
+ if (contexts) contexts[i] = sample_info->top_context;
+ i++;
}
+
+ // If we couldn't get a context address from the top frame due to execution
+ // being in a callback, borrow it from the next context on the stack.
+ bool borrows_top_context = it.top_frame_type() == i::StackFrame::EXIT ||
+ it.top_frame_type() == i::StackFrame::BUILTIN_EXIT;
+
i::RuntimeCallTimer* timer =
isolate->counters()->runtime_call_stats()->current_timer();
for (; !it.done() && i < frames_limit; it.Advance()) {
while (timer && reinterpret_cast<i::Address>(timer) < it.frame()->fp() &&
i < frames_limit) {
+ if (contexts) contexts[i] = nullptr;
frames[i++] = reinterpret_cast<void*>(timer->counter());
timer = timer->parent();
}
if (i == frames_limit) break;
+
+ // Attempt to read the native context associated with the frame from the
+ // heap for standard frames.
+ if (it.frame()->is_standard() && (contexts || borrows_top_context)) {
+ i::Address context_address = base::Memory<i::Address>(
+ it.frame()->fp() + i::StandardFrameConstants::kContextOffset);
+ i::Address native_context_address =
+ i::ScrapeNativeContextAddress(isolate->heap(), context_address);
+ if (contexts)
+ contexts[i] = reinterpret_cast<void*>(native_context_address);
+
+ if (borrows_top_context) {
+ DCHECK(!sample_info->top_context);
+ sample_info->top_context =
+ reinterpret_cast<void*>(native_context_address);
+ }
+ } else if (contexts) {
+ contexts[i] = nullptr;
+ }
+ borrows_top_context = false;
+
if (it.frame()->is_interpreted()) {
// For interpreted frames use the bytecode array pointer as the pc.
i::InterpretedFrame* frame =
@@ -290,20 +363,6 @@ bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs,
return true;
}
-namespace internal {
-
-void TickSample::Init(Isolate* isolate, const v8::RegisterState& state,
- RecordCEntryFrame record_c_entry_frame, bool update_stats,
- bool use_simulator_reg_state,
- base::TimeDelta sampling_interval) {
- v8::TickSample::Init(reinterpret_cast<v8::Isolate*>(isolate), state,
- record_c_entry_frame, update_stats,
- use_simulator_reg_state);
- this->sampling_interval = sampling_interval;
- if (pc == nullptr) return;
- timestamp = base::TimeTicks::HighResolutionNow();
-}
-
void TickSample::print() const {
PrintF("TickSample: at %p\n", this);
PrintF(" - state: %s\n", StateToString(state));
diff --git a/chromium/v8/src/profiler/tick-sample.h b/chromium/v8/src/profiler/tick-sample.h
index ba78c923c4c..37ae1e9d06e 100644
--- a/chromium/v8/src/profiler/tick-sample.h
+++ b/chromium/v8/src/profiler/tick-sample.h
@@ -5,7 +5,7 @@
#ifndef V8_PROFILER_TICK_SAMPLE_H_
#define V8_PROFILER_TICK_SAMPLE_H_
-#include "include/v8-profiler.h"
+#include "include/v8.h"
#include "src/base/platform/time.h"
#include "src/common/globals.h"
@@ -14,15 +14,83 @@ namespace internal {
class Isolate;
-struct TickSample : public v8::TickSample {
+// TickSample captures the information collected for each sample.
+struct V8_EXPORT TickSample {
+ // Internal profiling (with --prof + tools/$OS-tick-processor) wants to
+ // include the runtime function we're calling. Externally exposed tick
+ // samples don't care.
+ enum RecordCEntryFrame { kIncludeCEntryFrame, kSkipCEntryFrame };
+
+ TickSample()
+ : state(OTHER),
+ pc(nullptr),
+ external_callback_entry(nullptr),
+ frames_count(0),
+ has_external_callback(false),
+ update_stats(true) {}
+
+ /**
+ * Initialize a tick sample from the isolate.
+ * \param isolate The isolate.
+ * \param state Execution state.
+ * \param record_c_entry_frame Include or skip the runtime function.
+ * \param update_stats Whether update the sample to the aggregated stats.
+ * \param use_simulator_reg_state When set to true and V8 is running under a
+ * simulator, the method will use the simulator
+ * register state rather than the one provided
+ * with |state| argument. Otherwise the method
+ * will use provided register |state| as is.
+ */
void Init(Isolate* isolate, const v8::RegisterState& state,
RecordCEntryFrame record_c_entry_frame, bool update_stats,
bool use_simulator_reg_state = true,
base::TimeDelta sampling_interval = base::TimeDelta());
- base::TimeTicks timestamp;
- base::TimeDelta sampling_interval; // Sampling interval used to capture.
+ /**
+ * Get a call stack sample from the isolate.
+ * \param isolate The isolate.
+ * \param state Register state.
+ * \param record_c_entry_frame Include or skip the runtime function.
+ * \param frames Caller allocated buffer to store stack frames.
+ * \param frames_limit Maximum number of frames to capture. The buffer must
+ * be large enough to hold the number of frames.
+ * \param sample_info The sample info is filled up by the function
+ * provides number of actual captured stack frames and
+ * the current VM state.
+ * \param use_simulator_reg_state When set to true and V8 is running under a
+ * simulator, the method will use the simulator
+ * register state rather than the one provided
+ * with |state| argument. Otherwise the method
+ * will use provided register |state| as is.
+ * \note GetStackSample is thread and signal safe and should only be called
+ * when the JS thread is paused or interrupted.
+ * Otherwise the behavior is undefined.
+ */
+ static bool GetStackSample(Isolate* isolate, v8::RegisterState* state,
+ RecordCEntryFrame record_c_entry_frame,
+ void** frames, size_t frames_limit,
+ v8::SampleInfo* sample_info,
+ bool use_simulator_reg_state = true,
+ void** contexts = nullptr);
void print() const;
+
+ StateTag state; // The state of the VM.
+ void* pc; // Instruction pointer.
+ union {
+ void* tos; // Top stack value (*sp).
+ void* external_callback_entry;
+ };
+ static const unsigned kMaxFramesCountLog2 = 8;
+ static const unsigned kMaxFramesCount = (1 << kMaxFramesCountLog2) - 1;
+ void* stack[kMaxFramesCount]; // Call stack.
+ void* contexts[kMaxFramesCount]; // Stack of associated native contexts.
+ void* top_context = nullptr; // Address of the incumbent native context.
+ unsigned frames_count : kMaxFramesCountLog2; // Number of captured frames.
+ bool has_external_callback : 1;
+ bool update_stats : 1; // Whether the sample should update aggregated stats.
+
+ base::TimeTicks timestamp;
+ base::TimeDelta sampling_interval; // Sampling interval used to capture.
};
} // namespace internal