Home | History | Annotate | Download | only in v8
      1 /*
      2  * Copyright (c) 2011, Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "bindings/core/v8/ScriptProfiler.h"
     33 
     34 #include "bindings/core/v8/RetainedDOMInfo.h"
     35 #include "bindings/core/v8/ScriptValue.h"
     36 #include "bindings/core/v8/V8Binding.h"
     37 #include "bindings/core/v8/V8Node.h"
     38 #include "bindings/core/v8/V8Window.h"
     39 #include "bindings/core/v8/WrapperTypeInfo.h"
     40 #include "core/dom/Document.h"
     41 #include "core/inspector/BindingVisitors.h"
     42 #include "wtf/ThreadSpecific.h"
     43 
     44 #include <v8-profiler.h>
     45 #include <v8.h>
     46 
     47 namespace blink {
     48 
     49 typedef HashMap<String, double> ProfileNameIdleTimeMap;
     50 
     51 void ScriptProfiler::setSamplingInterval(int intervalUs)
     52 {
     53     v8::Isolate* isolate = v8::Isolate::GetCurrent();
     54     v8::CpuProfiler* profiler = isolate->GetCpuProfiler();
     55     if (profiler)
     56         profiler->SetSamplingInterval(intervalUs);
     57 }
     58 
     59 void ScriptProfiler::start(const String& title)
     60 {
     61     ProfileNameIdleTimeMap* profileNameIdleTimeMap = ScriptProfiler::currentProfileNameIdleTimeMap();
     62     if (profileNameIdleTimeMap->contains(title))
     63         return;
     64     profileNameIdleTimeMap->add(title, 0);
     65 
     66     v8::Isolate* isolate = v8::Isolate::GetCurrent();
     67     v8::CpuProfiler* profiler = isolate->GetCpuProfiler();
     68     if (!profiler)
     69         return;
     70     v8::HandleScope handleScope(isolate);
     71     profiler->StartProfiling(v8String(isolate, title), true);
     72 }
     73 
     74 PassRefPtrWillBeRawPtr<ScriptProfile> ScriptProfiler::stop(const String& title)
     75 {
     76     v8::Isolate* isolate = v8::Isolate::GetCurrent();
     77     v8::CpuProfiler* profiler = isolate->GetCpuProfiler();
     78     if (!profiler)
     79         return nullptr;
     80     v8::HandleScope handleScope(isolate);
     81     v8::CpuProfile* profile = profiler->StopProfiling(v8String(isolate, title));
     82     if (!profile)
     83         return nullptr;
     84 
     85     String profileTitle = toCoreString(profile->GetTitle());
     86     double idleTime = 0.0;
     87     ProfileNameIdleTimeMap* profileNameIdleTimeMap = ScriptProfiler::currentProfileNameIdleTimeMap();
     88     ProfileNameIdleTimeMap::iterator profileIdleTime = profileNameIdleTimeMap->find(profileTitle);
     89     if (profileIdleTime != profileNameIdleTimeMap->end()) {
     90         idleTime = profileIdleTime->value * 1000.0;
     91         profileNameIdleTimeMap->remove(profileIdleTime);
     92     }
     93 
     94     return ScriptProfile::create(profile, idleTime);
     95 }
     96 
     97 void ScriptProfiler::collectGarbage()
     98 {
     99     v8::Isolate::GetCurrent()->LowMemoryNotification();
    100 }
    101 
    102 ScriptValue ScriptProfiler::objectByHeapObjectId(unsigned id)
    103 {
    104     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    105     v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
    106     v8::HandleScope handleScope(isolate);
    107     v8::Handle<v8::Value> value = profiler->FindObjectById(id);
    108     if (value.IsEmpty() || !value->IsObject())
    109         return ScriptValue();
    110 
    111     v8::Handle<v8::Object> object = value.As<v8::Object>();
    112 
    113     if (object->InternalFieldCount() >= v8DefaultWrapperInternalFieldCount) {
    114         v8::Handle<v8::Value> wrapper = object->GetInternalField(v8DOMWrapperObjectIndex);
    115         // Skip wrapper boilerplates which are like regular wrappers but don't have
    116         // native object.
    117         if (!wrapper.IsEmpty() && wrapper->IsUndefined())
    118             return ScriptValue();
    119     }
    120 
    121     ScriptState* scriptState = ScriptState::from(object->CreationContext());
    122     return ScriptValue(scriptState, object);
    123 }
    124 
    125 unsigned ScriptProfiler::getHeapObjectId(const ScriptValue& value)
    126 {
    127     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    128     v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
    129     v8::SnapshotObjectId id = profiler->GetObjectId(value.v8Value());
    130     return id;
    131 }
    132 
    133 void ScriptProfiler::clearHeapObjectIds()
    134 {
    135     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    136     v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
    137     profiler->ClearObjectIds();
    138 }
    139 
    140 namespace {
    141 
    142 class ActivityControlAdapter FINAL : public v8::ActivityControl {
    143 public:
    144     ActivityControlAdapter(ScriptProfiler::HeapSnapshotProgress* progress)
    145         : m_progress(progress), m_firstReport(true) { }
    146     virtual ControlOption ReportProgressValue(int done, int total) OVERRIDE
    147     {
    148         ControlOption result = m_progress->isCanceled() ? kAbort : kContinue;
    149         if (m_firstReport) {
    150             m_firstReport = false;
    151             m_progress->Start(total);
    152         } else {
    153             m_progress->Worked(done);
    154         }
    155         if (done >= total)
    156             m_progress->Done();
    157         return result;
    158     }
    159 private:
    160     ScriptProfiler::HeapSnapshotProgress* m_progress;
    161     bool m_firstReport;
    162 };
    163 
    164 class GlobalObjectNameResolver FINAL : public v8::HeapProfiler::ObjectNameResolver {
    165 public:
    166     virtual const char* GetName(v8::Handle<v8::Object> object) OVERRIDE
    167     {
    168         LocalDOMWindow* window = toDOMWindow(object, v8::Isolate::GetCurrent());
    169         if (!window)
    170             return 0;
    171         CString url = window->document()->url().string().utf8();
    172         m_strings.append(url);
    173         return url.data();
    174     }
    175 
    176 private:
    177     Vector<CString> m_strings;
    178 };
    179 
    180 } // namespace
    181 
    182 void ScriptProfiler::startTrackingHeapObjects(bool trackAllocations)
    183 {
    184     v8::Isolate::GetCurrent()->GetHeapProfiler()->StartTrackingHeapObjects(trackAllocations);
    185 }
    186 
    187 namespace {
    188 
    189 class HeapStatsStream : public v8::OutputStream {
    190 public:
    191     HeapStatsStream(ScriptProfiler::OutputStream* stream) : m_stream(stream) { }
    192     virtual void EndOfStream() OVERRIDE { }
    193 
    194     virtual WriteResult WriteAsciiChunk(char* data, int size) OVERRIDE
    195     {
    196         ASSERT(false);
    197         return kAbort;
    198     }
    199 
    200     virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* updateData, int count) OVERRIDE
    201     {
    202         Vector<uint32_t> rawData(count * 3);
    203         for (int i = 0; i < count; ++i) {
    204             int offset = i * 3;
    205             rawData[offset] = updateData[i].index;
    206             rawData[offset + 1] = updateData[i].count;
    207             rawData[offset + 2] = updateData[i].size;
    208         }
    209         m_stream->write(rawData.data(), rawData.size());
    210         return kContinue;
    211     }
    212 
    213 private:
    214     ScriptProfiler::OutputStream* m_stream;
    215 };
    216 
    217 }
    218 
    219 unsigned ScriptProfiler::requestHeapStatsUpdate(ScriptProfiler::OutputStream* stream)
    220 {
    221     HeapStatsStream heapStatsStream(stream);
    222     return v8::Isolate::GetCurrent()->GetHeapProfiler()->GetHeapStats(&heapStatsStream);
    223 }
    224 
    225 void ScriptProfiler::stopTrackingHeapObjects()
    226 {
    227     v8::Isolate::GetCurrent()->GetHeapProfiler()->StopTrackingHeapObjects();
    228 }
    229 
    230 // FIXME: This method should receive a ScriptState, from which we should retrieve an Isolate.
    231 PassRefPtr<ScriptHeapSnapshot> ScriptProfiler::takeHeapSnapshot(const String& title, HeapSnapshotProgress* control)
    232 {
    233     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    234     v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
    235     if (!profiler)
    236         return nullptr;
    237     v8::HandleScope handleScope(isolate);
    238     ASSERT(control);
    239     ActivityControlAdapter adapter(control);
    240     GlobalObjectNameResolver resolver;
    241     const v8::HeapSnapshot* snapshot = profiler->TakeHeapSnapshot(v8String(isolate, title), &adapter, &resolver);
    242     return snapshot ? ScriptHeapSnapshot::create(snapshot) : nullptr;
    243 }
    244 
    245 static v8::RetainedObjectInfo* retainedDOMInfo(uint16_t classId, v8::Handle<v8::Value> wrapper)
    246 {
    247     ASSERT(classId == WrapperTypeInfo::NodeClassId);
    248     if (!wrapper->IsObject())
    249         return 0;
    250     Node* node = V8Node::toImpl(wrapper.As<v8::Object>());
    251     return node ? new RetainedDOMInfo(node) : 0;
    252 }
    253 
    254 void ScriptProfiler::initialize()
    255 {
    256     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    257     v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
    258     if (profiler)
    259         profiler->SetWrapperClassInfoProvider(WrapperTypeInfo::NodeClassId, &retainedDOMInfo);
    260 }
    261 
    262 void ScriptProfiler::visitNodeWrappers(WrappedNodeVisitor* visitor)
    263 {
    264     // visitNodeWrappers() should receive a ScriptState and retrieve an Isolate
    265     // from the ScriptState.
    266     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    267     v8::HandleScope handleScope(isolate);
    268 
    269     class DOMNodeWrapperVisitor : public v8::PersistentHandleVisitor {
    270     public:
    271         DOMNodeWrapperVisitor(WrappedNodeVisitor* visitor, v8::Isolate* isolate)
    272             : m_visitor(visitor)
    273             , m_isolate(isolate)
    274         {
    275         }
    276 
    277         virtual void VisitPersistentHandle(v8::Persistent<v8::Value>* value, uint16_t classId) OVERRIDE
    278         {
    279             if (classId != WrapperTypeInfo::NodeClassId)
    280                 return;
    281             // Casting to Handle is safe here, since the Persistent cannot get
    282             // GCd during visiting.
    283             v8::Handle<v8::Object>* wrapper = reinterpret_cast<v8::Handle<v8::Object>*>(value);
    284             ASSERT_UNUSED(m_isolate, V8Node::hasInstance(*wrapper, m_isolate));
    285             ASSERT((*wrapper)->IsObject());
    286             m_visitor->visitNode(V8Node::toImpl(*wrapper));
    287         }
    288 
    289     private:
    290         WrappedNodeVisitor* m_visitor;
    291         v8::Isolate* m_isolate;
    292     } wrapperVisitor(visitor, isolate);
    293 
    294     v8::V8::VisitHandlesWithClassIds(&wrapperVisitor);
    295 }
    296 
    297 ProfileNameIdleTimeMap* ScriptProfiler::currentProfileNameIdleTimeMap()
    298 {
    299     AtomicallyInitializedStatic(WTF::ThreadSpecific<ProfileNameIdleTimeMap>*, map = new WTF::ThreadSpecific<ProfileNameIdleTimeMap>);
    300     return *map;
    301 }
    302 
    303 void ScriptProfiler::setIdle(bool isIdle)
    304 {
    305     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    306     if (v8::CpuProfiler* profiler = isolate->GetCpuProfiler())
    307         profiler->SetIdle(isIdle);
    308 }
    309 
    310 } // namespace blink
    311