| // Copyright 2018 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/script/v8c/v8c_heap_tracer.h" |
| |
| #include <algorithm> |
| |
| #include "base/trace_event/trace_event.h" |
| #include "cobalt/script/v8c/v8c_engine.h" |
| #include "cobalt/script/v8c/v8c_global_environment.h" |
| #include "cobalt/script/v8c/wrapper_private.h" |
| |
| namespace cobalt { |
| namespace script { |
| namespace v8c { |
| |
| void V8cHeapTracer::RegisterV8References( |
| const std::vector<std::pair<void*, void*>>& embedder_fields) { |
| for (const auto& embedder_field : embedder_fields) { |
| WrapperPrivate* wrapper_private = |
| static_cast<WrapperPrivate*>(embedder_field.first); |
| Wrappable* wrappable = wrapper_private->raw_wrappable(); |
| MaybeAddToFrontier(wrappable); |
| DCHECK(embedder_field.second == WrapperPrivate::kInternalFieldIdValue); |
| } |
| } |
| |
| void V8cHeapTracer::TracePrologue(TraceFlags flags) { |
| TRACE_EVENT0("cobalt::script", "V8cHeapTracer::TracePrologue"); |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| if (disabled_) { |
| return; |
| } |
| DCHECK_EQ(frontier_.size(), 0); |
| DCHECK_EQ(visited_.size(), 0); |
| } |
| |
| bool V8cHeapTracer::AdvanceTracing(double deadline_in_ms) { |
| TRACE_EVENT0("cobalt::script", "V8cHeapTracer::AdvanceTracing"); |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| double start_time = platform_->MonotonicallyIncreasingTime(); |
| if (disabled_) { |
| return true; |
| } |
| |
| // This feels a bit weird, but as far as I can tell, we're expected to |
| // manually decide to trace the from the global object. |
| MaybeAddToFrontier( |
| V8cGlobalEnvironment::GetFromIsolate(isolate_)->global_wrappable()); |
| |
| // Objects that we want to keep alive. |
| for (Traceable* traceable : roots_) { |
| MaybeAddToFrontier(traceable); |
| } |
| for (v8::TracedGlobal<v8::Value>* traced_global : globals_) { |
| RegisterEmbedderReference(traced_global->As<v8::Data>()); |
| } |
| |
| while (platform_->MonotonicallyIncreasingTime() - start_time < |
| deadline_in_ms) { |
| if (frontier_.empty()) { |
| return false; |
| } |
| |
| Traceable* traceable = frontier_.back(); |
| frontier_.pop_back(); |
| DCHECK(traceable); |
| |
| if (traceable->IsWrappable()) { |
| Wrappable* wrappable = base::polymorphic_downcast<Wrappable*>(traceable); |
| auto pair_range = reference_map_.equal_range(wrappable); |
| for (auto it = pair_range.first; it != pair_range.second; ++it) { |
| // Tell v8 this object is referenced on Cobalt heap. |
| RegisterEmbedderReference(it->second->traced_global().As<v8::Data>()); |
| } |
| WrapperFactory* wrapper_factory = |
| V8cGlobalEnvironment::GetFromIsolate(isolate_)->wrapper_factory(); |
| WrapperPrivate* maybe_wrapper_private = |
| wrapper_factory->MaybeGetWrapperPrivate( |
| static_cast<Wrappable*>(traceable)); |
| if (maybe_wrapper_private) { |
| RegisterEmbedderReference(maybe_wrapper_private->traced_global().As<v8::Data>()); |
| } |
| } |
| |
| traceable->TraceMembers(this); |
| } |
| |
| return true; |
| } |
| |
| bool V8cHeapTracer::IsTracingDone() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| if (disabled_) { |
| return true; |
| } |
| return frontier_.empty(); |
| } |
| |
| void V8cHeapTracer::TraceEpilogue(TraceSummary* trace_summary) { |
| TRACE_EVENT0("cobalt::script", "V8cHeapTracer::TraceEpilogue"); |
| |
| if (disabled_) { |
| return; |
| } |
| DCHECK(frontier_.empty()); |
| visited_.clear(); |
| } |
| |
| void V8cHeapTracer::EnterFinalPause(EmbedderStackState stack_state) { |
| TRACE_EVENT0("cobalt::script", "V8cHeapTracer::EnterFinalPause"); |
| } |
| |
| void V8cHeapTracer::Trace(Traceable* traceable) { |
| DCHECK(!disabled_); |
| MaybeAddToFrontier(traceable); |
| } |
| |
| void V8cHeapTracer::AddReferencedObject(Wrappable* owner, |
| ScopedPersistent<v8::Value>* value) { |
| DCHECK(owner); |
| DCHECK(value); |
| auto it = reference_map_.insert({owner, value}); |
| } |
| |
| void V8cHeapTracer::RemoveReferencedObject(Wrappable* owner, |
| ScopedPersistent<v8::Value>* value) { |
| DCHECK(owner); |
| DCHECK(value); |
| auto pair_range = reference_map_.equal_range(owner); |
| auto it = std::find_if( |
| pair_range.first, pair_range.second, |
| [&](decltype(*pair_range.first) it) { return it.second == value; }); |
| DCHECK(it != pair_range.second); |
| reference_map_.erase(it); |
| } |
| |
| void V8cHeapTracer::AddRoot(Traceable* traceable) { |
| DCHECK(traceable); |
| roots_.insert(traceable); |
| } |
| |
| void V8cHeapTracer::RemoveRoot(Traceable* traceable) { |
| DCHECK(traceable); |
| auto it = roots_.find(traceable); |
| DCHECK(it != roots_.end()); |
| roots_.erase(it); |
| } |
| |
| void V8cHeapTracer::AddRoot(v8::TracedGlobal<v8::Value>* traced_global) { |
| DCHECK(traced_global); |
| globals_.insert(traced_global); |
| } |
| |
| void V8cHeapTracer::RemoveRoot(v8::TracedGlobal<v8::Value>* traced_global) { |
| DCHECK(traced_global); |
| auto it = globals_.find(traced_global); |
| DCHECK(it != globals_.end()); |
| globals_.erase(it); |
| } |
| |
| void V8cHeapTracer::MaybeAddToFrontier(Traceable* traceable) { |
| if (!traceable) { |
| return; |
| } |
| if (!visited_.insert(traceable).second) { |
| return; |
| } |
| frontier_.push_back(traceable); |
| } |
| |
| } // namespace v8c |
| } // namespace script |
| } // namespace cobalt |